diff options
828 files changed, 33230 insertions, 29170 deletions
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 73060819ed9..43827780010 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -159,8 +159,6 @@ hayes-esp.txt - info on using the Hayes ESP serial driver. highuid.txt - notes on the change from 16 bit to 32 bit user/group IDs. -hpet.txt - - High Precision Event Timer Driver for Linux. timers/ - info on the timer related topics hw_random.txt diff --git a/Documentation/blackfin/kgdb.txt b/Documentation/blackfin/kgdb.txt deleted file mode 100644 index 84f6a484ae9..00000000000 --- a/Documentation/blackfin/kgdb.txt +++ /dev/null @@ -1,155 +0,0 @@ - A Simple Guide to Configure KGDB - - Sonic Zhang <sonic.zhang@analog.com> - Aug. 24th 2006 - - -This KGDB patch enables the kernel developer to do source level debugging on -the kernel for the Blackfin architecture. The debugging works over either the -ethernet interface or one of the uarts. Both software breakpoints and -hardware breakpoints are supported in this version. -http://docs.blackfin.uclinux.org/doku.php?id=kgdb - - -2 known issues: -1. This bug: - http://blackfin.uclinux.org/tracker/index.php?func=detail&aid=544&group_id=18&atid=145 - The GDB client for Blackfin uClinux causes incorrect values of local - variables to be displayed when the user breaks the running of kernel in GDB. -2. Because of a hardware bug in Blackfin 533 v1.0.3: - 05000067 - Watchpoints (Hardware Breakpoints) are not supported - Hardware breakpoints cannot be set properly. - - -Debug over Ethernet: - -1. Compile and install the cross platform version of gdb for blackfin, which - can be found at $(BINROOT)/bfin-elf-gdb. - -2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under - "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb". - With this selected, option "Full Symbolic/Source Debugging support" and - "Compile the kernel with frame pointers" are also selected. - -3. Select option "KGDB: connect over (Ethernet)". Add "kgdboe=@target-IP/,@host-IP/" to - the option "Compiled-in Kernel Boot Parameter" under "Kernel hacking". - -4. Connect minicom to the serial port and boot the kernel image. - -5. Configure the IP "/> ifconfig eth0 target-IP" - -6. Start GDB client "bfin-elf-gdb vmlinux". - -7. Connect to the target "(gdb) target remote udp:target-IP:6443". - -8. Set software breakpoint "(gdb) break sys_open". - -9. Continue "(gdb) c". - -10. Run ls in the target console "/> ls". - -11. Breakpoint hits. "Breakpoint 1: sys_open(..." - -12. Display local variables and function paramters. - (*) This operation gives wrong results, see known issue 1. - -13. Single stepping "(gdb) si". - -14. Remove breakpoint 1. "(gdb) del 1" - -15. Set hardware breakpoint "(gdb) hbreak sys_open". - -16. Continue "(gdb) c". - -17. Run ls in the target console "/> ls". - -18. Hardware breakpoint hits. "Breakpoint 1: sys_open(...". - (*) This hardware breakpoint will not be hit, see known issue 2. - -19. Continue "(gdb) c". - -20. Interrupt the target in GDB "Ctrl+C". - -21. Detach from the target "(gdb) detach". - -22. Exit GDB "(gdb) quit". - - -Debug over the UART: - -1. Compile and install the cross platform version of gdb for blackfin, which - can be found at $(BINROOT)/bfin-elf-gdb. - -2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under - "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb". - With this selected, option "Full Symbolic/Source Debugging support" and - "Compile the kernel with frame pointers" are also selected. - -3. Select option "KGDB: connect over (UART)". Set "KGDB: UART port number" to be - a different one from the console. Don't forget to change the mode of - blackfin serial driver to PIO. Otherwise kgdb works incorrectly on UART. - -4. If you want connect to kgdb when the kernel boots, enable - "KGDB: Wait for gdb connection early" - -5. Compile kernel. - -6. Connect minicom to the serial port of the console and boot the kernel image. - -7. Start GDB client "bfin-elf-gdb vmlinux". - -8. Set the baud rate in GDB "(gdb) set remotebaud 57600". - -9. Connect to the target on the second serial port "(gdb) target remote /dev/ttyS1". - -10. Set software breakpoint "(gdb) break sys_open". - -11. Continue "(gdb) c". - -12. Run ls in the target console "/> ls". - -13. A breakpoint is hit. "Breakpoint 1: sys_open(..." - -14. All other operations are the same as that in KGDB over Ethernet. - - -Debug over the same UART as console: - -1. Compile and install the cross platform version of gdb for blackfin, which - can be found at $(BINROOT)/bfin-elf-gdb. - -2. Apply this patch to the 2.6.x kernel. Select the menuconfig option under - "Kernel hacking" -> "Kernel debugging" -> "KGDB: kernel debug with remote gdb". - With this selected, option "Full Symbolic/Source Debugging support" and - "Compile the kernel with frame pointers" are also selected. - -3. Select option "KGDB: connect over UART". Set "KGDB: UART port number" to console. - Don't forget to change the mode of blackfin serial driver to PIO. - Otherwise kgdb works incorrectly on UART. - -4. If you want connect to kgdb when the kernel boots, enable - "KGDB: Wait for gdb connection early" - -5. Connect minicom to the serial port and boot the kernel image. - -6. (Optional) Ask target to wait for gdb connection by entering Ctrl+A. In minicom, you should enter Ctrl+A+A. - -7. Start GDB client "bfin-elf-gdb vmlinux". - -8. Set the baud rate in GDB "(gdb) set remotebaud 57600". - -9. Connect to the target "(gdb) target remote /dev/ttyS0". - -10. Set software breakpoint "(gdb) break sys_open". - -11. Continue "(gdb) c". Then enter Ctrl+C twice to stop GDB connection. - -12. Run ls in the target console "/> ls". Dummy string can be seen on the console. - -13. Then connect the gdb to target again. "(gdb) target remote /dev/ttyS0". - Now you will find a breakpoint is hit. "Breakpoint 1: sys_open(..." - -14. All other operations are the same as that in KGDB over Ethernet. The only - difference is that after continue command in GDB, please stop GDB - connection by 2 "Ctrl+C"s and connect again after breakpoints are hit or - Ctrl+A is entered. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 3d2d0c29f02..cc8093c15cf 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -287,14 +287,6 @@ Who: Glauber Costa <gcosta@redhat.com> --------------------------- -What: old style serial driver for ColdFire (CONFIG_SERIAL_COLDFIRE) -When: 2.6.28 -Why: This driver still uses the old interface and has been replaced - by CONFIG_SERIAL_MCF. -Who: Sebastian Siewior <sebastian@breakpoint.cc> - ---------------------------- - What: /sys/o2cb symlink When: January 2010 Why: /sys/fs/o2cb is the proper location for this information - /sys/o2cb diff --git a/Documentation/filesystems/ext4.txt b/Documentation/filesystems/ext4.txt index 74484e69640..eb154ef36c2 100644 --- a/Documentation/filesystems/ext4.txt +++ b/Documentation/filesystems/ext4.txt @@ -223,6 +223,11 @@ errors=remount-ro(*) Remount the filesystem read-only on an error. errors=continue Keep going on a filesystem error. errors=panic Panic and halt the machine if an error occurs. +data_err=ignore(*) Just print an error message if an error occurs + in a file data buffer in ordered mode. +data_err=abort Abort the journal if an error occurs in a file + data buffer in ordered mode. + grpid Give objects the same group ID as their creator. bsdgroups diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index d831d24d2a6..b488edad743 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1331,13 +1331,6 @@ determine whether or not they are still functioning properly. Because the NMI watchdog shares registers with oprofile, by disabling the NMI watchdog, oprofile may have more registers to utilize. -maps_protect ------------- - -Enables/Disables the protection of the per-process proc entries "maps" and -"smaps". When enabled, the contents of these files are visible only to -readers that are allowed to ptrace() the given process. - msgmni ------ diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 25efbaf1f59..2443f5bb436 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -658,11 +658,12 @@ and is between 256 and 4096 characters. It is defined in the file earlyprintk= [X86-32,X86-64,SH,BLACKFIN] earlyprintk=vga earlyprintk=serial[,ttySn[,baudrate]] + earlyprintk=dbgp Append ",keep" to not disable it when the real console takes over. - Only vga or serial at a time, not both. + Only vga or serial or usb debug port at a time. Currently only ttyS0 and ttyS1 are supported. @@ -1231,6 +1232,29 @@ and is between 256 and 4096 characters. It is defined in the file or memmap=0x10000$0x18690000 + memory_corruption_check=0/1 [X86] + Some BIOSes seem to corrupt the first 64k of + memory when doing things like suspend/resume. + Setting this option will scan the memory + looking for corruption. Enabling this will + both detect corruption and prevent the kernel + from using the memory being corrupted. + However, its intended as a diagnostic tool; if + repeatable BIOS-originated corruption always + affects the same memory, you can use memmap= + to prevent the kernel from using that memory. + + memory_corruption_check_size=size [X86] + By default it checks for corruption in the low + 64k, making this memory unavailable for normal + use. Use this parameter to scan for + corruption in more or less memory. + + memory_corruption_check_period=seconds [X86] + By default it checks for corruption every 60 + seconds. Use this parameter to check at some + other rate. 0 disables periodic checking. + memtest= [KNL,X86] Enable memtest Format: <integer> range: 0,4 : pattern number diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index b54cb5048df..87a7c07ab65 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -5073,8 +5073,7 @@ struct _snd_pcm_runtime { with <constant>SNDRV_DMA_TYPE_CONTINUOUS</constant> type and the <function>snd_dma_continuous_data(GFP_KERNEL)</function> device pointer, where <constant>GFP_KERNEL</constant> is the kernel allocation flag to - use. For the SBUS, <constant>SNDRV_DMA_TYPE_SBUS</constant> and - <function>snd_dma_sbus_data(sbus_dev)</function> are used instead. + use. For the PCI scatter-gather buffers, use <constant>SNDRV_DMA_TYPE_DEV_SG</constant> with <function>snd_dma_pci_data(pci)</function> diff --git a/Documentation/sparc/sbus_drivers.txt b/Documentation/sparc/sbus_drivers.txt deleted file mode 100644 index eb1e28ad882..00000000000 --- a/Documentation/sparc/sbus_drivers.txt +++ /dev/null @@ -1,309 +0,0 @@ - - Writing SBUS Drivers - - David S. Miller (davem@redhat.com) - - The SBUS driver interfaces of the Linux kernel have been -revamped completely for 2.4.x for several reasons. Foremost were -performance and complexity concerns. This document details these -new interfaces and how they are used to write an SBUS device driver. - - SBUS drivers need to include <asm/sbus.h> to get access -to functions and structures described here. - - Probing and Detection - - Each SBUS device inside the machine is described by a -structure called "struct sbus_dev". Likewise, each SBUS bus -found in the system is described by a "struct sbus_bus". For -each SBUS bus, the devices underneath are hung in a tree-like -fashion off of the bus structure. - - The SBUS device structure contains enough information -for you to implement your device probing algorithm and obtain -the bits necessary to run your device. The most commonly -used members of this structure, and their typical usage, -will be detailed below. - - Here is a piece of skeleton code for performing a device -probe in an SBUS driver under Linux: - - static int __devinit mydevice_probe_one(struct sbus_dev *sdev) - { - struct mysdevice *mp = kzalloc(sizeof(*mp), GFP_KERNEL); - - if (!mp) - return -ENODEV; - - ... - dev_set_drvdata(&sdev->ofdev.dev, mp); - return 0; - ... - } - - static int __devinit mydevice_probe(struct of_device *dev, - const struct of_device_id *match) - { - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - - return mydevice_probe_one(sdev); - } - - static int __devexit mydevice_remove(struct of_device *dev) - { - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct mydevice *mp = dev_get_drvdata(&dev->dev); - - return mydevice_remove_one(sdev, mp); - } - - static struct of_device_id mydevice_match[] = { - { - .name = "mydevice", - }, - {}, - }; - - MODULE_DEVICE_TABLE(of, mydevice_match); - - static struct of_platform_driver mydevice_driver = { - .match_table = mydevice_match, - .probe = mydevice_probe, - .remove = __devexit_p(mydevice_remove), - .driver = { - .name = "mydevice", - }, - }; - - static int __init mydevice_init(void) - { - return of_register_driver(&mydevice_driver, &sbus_bus_type); - } - - static void __exit mydevice_exit(void) - { - of_unregister_driver(&mydevice_driver); - } - - module_init(mydevice_init); - module_exit(mydevice_exit); - - The mydevice_match table is a series of entries which -describes what SBUS devices your driver is meant for. In the -simplest case you specify a string for the 'name' field. Every -SBUS device with a 'name' property matching your string will -be passed one-by-one to your .probe method. - - You should store away your device private state structure -pointer in the drvdata area so that you can retrieve it later on -in your .remove method. - - Any memory allocated, registers mapped, IRQs registered, -etc. must be undone by your .remove method so that all resources -of your device are released by the time it returns. - - You should _NOT_ use the for_each_sbus(), for_each_sbusdev(), -and for_all_sbusdev() interfaces. They are deprecated, will be -removed, and no new driver should reference them ever. - - Mapping and Accessing I/O Registers - - Each SBUS device structure contains an array of descriptors -which describe each register set. We abuse struct resource for that. -They each correspond to the "reg" properties provided by the OBP firmware. - - Before you can access your device's registers you must map -them. And later if you wish to shutdown your driver (for module -unload or similar) you must unmap them. You must treat them as -a resource, which you allocate (map) before using and free up -(unmap) when you are done with it. - - The mapping information is stored in an opaque value -typed as an "unsigned long". This is the type of the return value -of the mapping interface, and the arguments to the unmapping -interface. Let's say you want to map the first set of registers. -Perhaps part of your driver software state structure looks like: - - struct mydevice { - unsigned long control_regs; - ... - struct sbus_dev *sdev; - ... - }; - - At initialization time you then use the sbus_ioremap -interface to map in your registers, like so: - - static void init_one_mydevice(struct sbus_dev *sdev) - { - struct mydevice *mp; - ... - - mp->control_regs = sbus_ioremap(&sdev->resource[0], 0, - CONTROL_REGS_SIZE, "mydevice regs"); - if (!mp->control_regs) { - /* Failure, cleanup and return. */ - } - } - - Second argument to sbus_ioremap is an offset for -cranky devices with broken OBP PROM. The sbus_ioremap uses only -a start address and flags from the resource structure. -Therefore it is possible to use the same resource to map -several sets of registers or even to fabricate a resource -structure if driver gets physical address from some private place. -This practice is discouraged though. Use whatever OBP PROM -provided to you. - - And here is how you might unmap these registers later at -driver shutdown or module unload time, using the sbus_iounmap -interface: - - static void mydevice_unmap_regs(struct mydevice *mp) - { - sbus_iounmap(mp->control_regs, CONTROL_REGS_SIZE); - } - - Finally, to actually access your registers there are 6 -interface routines at your disposal. Accesses are byte (8 bit), -word (16 bit), or longword (32 bit) sized. Here they are: - - u8 sbus_readb(unsigned long reg) /* read byte */ - u16 sbus_readw(unsigned long reg) /* read word */ - u32 sbus_readl(unsigned long reg) /* read longword */ - void sbus_writeb(u8 value, unsigned long reg) /* write byte */ - void sbus_writew(u16 value, unsigned long reg) /* write word */ - void sbus_writel(u32 value, unsigned long reg) /* write longword */ - - So, let's say your device has a control register of some sort -at offset zero. The following might implement resetting your device: - - #define CONTROL 0x00UL - - #define CONTROL_RESET 0x00000001 /* Reset hardware */ - - static void mydevice_reset(struct mydevice *mp) - { - sbus_writel(CONTROL_RESET, mp->regs + CONTROL); - } - - Or perhaps there is a data port register at an offset of -16 bytes which allows you to read bytes from a fifo in the device: - - #define DATA 0x10UL - - static u8 mydevice_get_byte(struct mydevice *mp) - { - return sbus_readb(mp->regs + DATA); - } - - It's pretty straightforward, and clueful readers may have -noticed that these interfaces mimick the PCI interfaces of the -Linux kernel. This was not by accident. - - WARNING: - - DO NOT try to treat these opaque register mapping - values as a memory mapped pointer to some structure - which you can dereference. - - It may be memory mapped, it may not be. In fact it - could be a physical address, or it could be the time - of day xor'd with 0xdeadbeef. :-) - - Whatever it is, it's an implementation detail. The - interface was done this way to shield the driver - author from such complexities. - - Doing DVMA - - SBUS devices can perform DMA transactions in a way similar -to PCI but dissimilar to ISA, e.g. DMA masters supply address. -In contrast to PCI, however, that address (a bus address) is -translated by IOMMU before a memory access is performed and therefore -it is virtual. Sun calls this procedure DVMA. - - Linux supports two styles of using SBUS DVMA: "consistent memory" -and "streaming DVMA". CPU view of consistent memory chunk is, well, -consistent with a view of a device. Think of it as an uncached memory. -Typically this way of doing DVMA is not very fast and drivers use it -mostly for control blocks or queues. On some CPUs we cannot flush or -invalidate individual pages or cache lines and doing explicit flushing -over ever little byte in every control block would be wasteful. - -Streaming DVMA is a preferred way to transfer large amounts of data. -This process works in the following way: -1. a CPU stops accessing a certain part of memory, - flushes its caches covering that memory; -2. a device does DVMA accesses, then posts an interrupt; -3. CPU invalidates its caches and starts to access the memory. - -A single streaming DVMA operation can touch several discontiguous -regions of a virtual bus address space. This is called a scatter-gather -DVMA. - -[TBD: Why do not we neither Solaris attempt to map disjoint pages -into a single virtual chunk with the help of IOMMU, so that non SG -DVMA masters would do SG? It'd be very helpful for RAID.] - - In order to perform a consistent DVMA a driver does something -like the following: - - char *mem; /* Address in the CPU space */ - u32 busa; /* Address in the SBus space */ - - mem = (char *) sbus_alloc_consistent(sdev, MYMEMSIZE, &busa); - - Then mem is used when CPU accesses this memory and u32 -is fed to the device so that it can do DVMA. This is typically -done with an sbus_writel() into some device register. - - Do not forget to free the DVMA resources once you are done: - - sbus_free_consistent(sdev, MYMEMSIZE, mem, busa); - - Streaming DVMA is more interesting. First you allocate some -memory suitable for it or pin down some user pages. Then it all works -like this: - - char *mem = argumen1; - unsigned int size = argument2; - u32 busa; /* Address in the SBus space */ - - *mem = 1; /* CPU can access */ - busa = sbus_map_single(sdev, mem, size); - if (busa == 0) ....... - - /* Tell the device to use busa here */ - /* CPU cannot access the memory without sbus_dma_sync_single() */ - - sbus_unmap_single(sdev, busa, size); - if (*mem == 0) .... /* CPU can access again */ - - It is possible to retain mappings and ask the device to -access data again and again without calling sbus_unmap_single. -However, CPU caches must be invalidated with sbus_dma_sync_single -before such access. - -[TBD but what about writeback caches here... do we have any?] - - There is an equivalent set of functions doing the same thing -only with several memory segments at once for devices capable of -scatter-gather transfers. Use the Source, Luke. - - Examples - - drivers/net/sunhme.c - This is a complicated driver which illustrates many concepts -discussed above and plus it handles both PCI and SBUS boards. - - drivers/scsi/esp.c - Check it out for scatter-gather DVMA. - - drivers/sbus/char/bpp.c - A non-DVMA device. - - drivers/net/sunlance.c - Lance driver abuses consistent mappings for data transfer. -It is a nifty trick which we do not particularly recommend... -Just check it out and know that it's legal. diff --git a/Documentation/timers/00-INDEX b/Documentation/timers/00-INDEX new file mode 100644 index 00000000000..397dc35e132 --- /dev/null +++ b/Documentation/timers/00-INDEX @@ -0,0 +1,10 @@ +00-INDEX + - this file +highres.txt + - High resolution timers and dynamic ticks design notes +hpet.txt + - High Precision Event Timer Driver for Linux +hrtimers.txt + - subsystem for high-resolution kernel timers +timer_stats.txt + - timer usage statistics diff --git a/Documentation/hpet.txt b/Documentation/timers/hpet.txt index 6ad52d9dad6..e7c09abcfab 100644 --- a/Documentation/hpet.txt +++ b/Documentation/timers/hpet.txt @@ -1,21 +1,32 @@ High Precision Event Timer Driver for Linux -The High Precision Event Timer (HPET) hardware is the future replacement -for the 8254 and Real Time Clock (RTC) periodic timer functionality. -Each HPET can have up to 32 timers. It is possible to configure the -first two timers as legacy replacements for 8254 and RTC periodic timers. -A specification done by Intel and Microsoft can be found at -<http://www.intel.com/technology/architecture/hpetspec.htm>. +The High Precision Event Timer (HPET) hardware follows a specification +by Intel and Microsoft which can be found at + + http://www.intel.com/technology/architecture/hpetspec.htm + +Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision") +and up to 32 comparators. Normally three or more comparators are provided, +each of which can generate oneshot interupts and at least one of which has +additional hardware to support periodic interrupts. The comparators are +also called "timers", which can be misleading since usually timers are +independent of each other ... these share a counter, complicating resets. + +HPET devices can support two interrupt routing modes. In one mode, the +comparators are additional interrupt sources with no particular system +role. Many x86 BIOS writers don't route HPET interrupts at all, which +prevents use of that mode. They support the other "legacy replacement" +mode where the first two comparators block interrupts from 8254 timers +and from the RTC. The driver supports detection of HPET driver allocation and initialization of the HPET before the driver module_init routine is called. This enables platform code which uses timer 0 or 1 as the main timer to intercept HPET initialization. An example of this initialization can be found in -arch/i386/kernel/time_hpet.c. +arch/x86/kernel/hpet.c. -The driver provides two APIs which are very similar to the API found in -the rtc.c driver. There is a user space API and a kernel space API. -An example user space program is provided below. +The driver provides a userspace API which resembles the API found in the +RTC driver framework. An example user space program is provided below. #include <stdio.h> #include <stdlib.h> @@ -286,15 +297,3 @@ out: return; } - -The kernel API has three interfaces exported from the driver: - - hpet_register(struct hpet_task *tp, int periodic) - hpet_unregister(struct hpet_task *tp) - hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg) - -The kernel module using this interface fills in the ht_func and ht_data -members of the hpet_task structure before calling hpet_register. -hpet_control simply vectors to the hpet_ioctl routine and has the same -commands and respective arguments as the user API. hpet_unregister -is used to terminate usage of the HPET timer reserved by hpet_register. diff --git a/MAINTAINERS b/MAINTAINERS index 587f418ed00..988b0a85289 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -72,6 +72,7 @@ M: Mail patches to L: Mailing list that is relevant to this area W: Web-page with status/info T: SCM tree type and location. Type is one of: git, hg, quilt. +F: Applicable files and/or directories S: Status, one of the following: Supported: Someone is actually paid to look after this. @@ -102,14 +103,14 @@ M: dave@thedillows.org L: netdev@vger.kernel.org S: Maintained -3W-XXXX ATA-RAID CONTROLLER DRIVER +3W-9XXX SATA-RAID CONTROLLER DRIVER P: Adam Radford M: linuxraid@amcc.com L: linux-scsi@vger.kernel.org W: http://www.amcc.com S: Supported -3W-9XXX SATA-RAID CONTROLLER DRIVER +3W-XXXX ATA-RAID CONTROLLER DRIVER P: Adam Radford M: linuxraid@amcc.com L: linux-scsi@vger.kernel.org @@ -163,16 +164,11 @@ M: A2232@gmx.net L: linux-m68k@lists.linux-m68k.org S: Maintained -AFS FILESYSTEM & AF_RXRPC SOCKET DOMAIN -P: David Howells -M: dhowells@redhat.com -L: linux-afs@lists.infradead.org -S: Supported - -AIO -P: Benjamin LaHaise -M: bcrl@kvack.org -L: linux-aio@kvack.org +AACRAID SCSI RAID DRIVER +P: Adaptec OEM Raid Solutions +M: aacraid@adaptec.com +L: linux-scsi@vger.kernel.org +W: http://www.adaptec.com/ S: Supported ABIT UGURU 1,2 HARDWARE MONITOR DRIVER @@ -193,27 +189,6 @@ M: jes@trained-monkey.org L: linux-acenic@sunsite.dk S: Maintained -IPS SCSI RAID DRIVER -P: Adaptec OEM Raid Solutions -M: aacraid@adaptec.com -L: linux-scsi@vger.kernel.org -W: http://www.adaptec.com/ -S: Maintained - -DPT_I2O SCSI RAID DRIVER -P: Adaptec OEM Raid Solutions -M: aacraid@adaptec.com -L: linux-scsi@vger.kernel.org -W: http://www.adaptec.com/ -S: Maintained - -AACRAID SCSI RAID DRIVER -P: Adaptec OEM Raid Solutions -M: aacraid@adaptec.com -L: linux-scsi@vger.kernel.org -W: http://www.adaptec.com/ -S: Supported - ACER WMI LAPTOP EXTRAS P: Carlos Corbacho M: carlos@strangeworlds.co.uk @@ -327,6 +302,12 @@ P: Roman Zippel M: zippel@linux-m68k.org S: Maintained +AFS FILESYSTEM & AF_RXRPC SOCKET DOMAIN +P: David Howells +M: dhowells@redhat.com +L: linux-afs@lists.infradead.org +S: Supported + AGPGART DRIVER P: David Airlie M: airlied@linux.ie @@ -345,6 +326,12 @@ M: hare@suse.de L: linux-scsi@vger.kernel.org S: Maintained +AIO +P: Benjamin LaHaise +M: bcrl@kvack.org +L: linux-aio@kvack.org +S: Supported + ALCATEL SPEEDTOUCH USB DRIVER P: Duncan Sands M: duncan.sands@free.fr @@ -390,6 +377,11 @@ L: iommu@lists.linux-foundation.org T: git://git.kernel.org/pub/scm/linux/kernel/git/joro/linux-2.6-iommu.git S: Supported +AMD MICROCODE UPDATE SUPPORT +P: Peter Oruba +M: peter.oruba@amd.com +S: Supported + AMS (Apple Motion Sensor) DRIVER P: Stelian Pop M: stelian@popies.net @@ -732,7 +724,7 @@ L: linux-kernel@vger.kernel.org W: http://sourceforge.net/projects/xscaleiop S: Supported -ATA OVER ETHERNET DRIVER +ATA OVER ETHERNET (AOE) DRIVER P: Ed L. Cashin M: ecashin@coraid.com W: http://www.coraid.com/support/linux @@ -865,11 +857,48 @@ L: linux-hams@vger.kernel.org W: http://www.linux-ax25.org/ S: Maintained +B43 WIRELESS DRIVER +P: Michael Buesch +M: mb@bu3sch.de +P: Stefano Brivio +M: stefano.brivio@polimi.it +L: linux-wireless@vger.kernel.org +W: http://linuxwireless.org/en/users/Drivers/b43 +S: Maintained + +B43LEGACY WIRELESS DRIVER +P: Larry Finger +M: Larry.Finger@lwfinger.net +P: Stefano Brivio +M: stefano.brivio@polimi.it +L: linux-wireless@vger.kernel.org +W: http://linuxwireless.org/en/users/Drivers/b43 +S: Maintained + BACKLIGHT CLASS/SUBSYSTEM P: Richard Purdie M: rpurdie@rpsys.net S: Maintained +BAYCOM/HDLCDRV DRIVERS FOR AX.25 +P: Thomas Sailer +M: t.sailer@alumni.ethz.ch +L: linux-hams@vger.kernel.org +W: http://www.baycom.org/~tom/ham/ham.html +S: Maintained + +BEFS FILE SYSTEM +P: Sergey S. Kostyliov +M: rathamahata@php4.ru +L: linux-kernel@vger.kernel.org +S: Maintained + +BFS FILE SYSTEM +P: Tigran A. Aivazian +M: tigran@aivazian.fsnet.co.uk +L: linux-kernel@vger.kernel.org +S: Maintained + BLACKFIN ARCHITECTURE P: Bryan Wu M: cooloney@kernel.org @@ -905,43 +934,6 @@ L: uclinux-dist-devel@blackfin.uclinux.org (subscribers-only) W: http://blackfin.uclinux.org S: Supported -BAYCOM/HDLCDRV DRIVERS FOR AX.25 -P: Thomas Sailer -M: t.sailer@alumni.ethz.ch -L: linux-hams@vger.kernel.org -W: http://www.baycom.org/~tom/ham/ham.html -S: Maintained - -B43 WIRELESS DRIVER -P: Michael Buesch -M: mb@bu3sch.de -P: Stefano Brivio -M: stefano.brivio@polimi.it -L: linux-wireless@vger.kernel.org -W: http://linuxwireless.org/en/users/Drivers/b43 -S: Maintained - -B43LEGACY WIRELESS DRIVER -P: Larry Finger -M: Larry.Finger@lwfinger.net -P: Stefano Brivio -M: stefano.brivio@polimi.it -L: linux-wireless@vger.kernel.org -W: http://linuxwireless.org/en/users/Drivers/b43 -S: Maintained - -BEFS FILE SYSTEM -P: Sergey S. Kostyliov -M: rathamahata@php4.ru -L: linux-kernel@vger.kernel.org -S: Maintained - -BFS FILE SYSTEM -P: Tigran A. Aivazian -M: tigran@aivazian.fsnet.co.uk -L: linux-kernel@vger.kernel.org -S: Maintained - BLACKFIN I2C TWI DRIVER P: Sonic Zhang M: sonic.zhang@analog.com @@ -1035,15 +1027,6 @@ M: corbet@lwn.net L: video4linux-list@redhat.com S: Maintained -CAN NETWORK LAYER -P: Urs Thuermann -M: urs.thuermann@volkswagen.de -P: Oliver Hartkopp -M: oliver.hartkopp@volkswagen.de -L: socketcan-core@lists.berlios.de (subscribers-only) -W: http://developer.berlios.de/projects/socketcan/ -S: Maintained - CALGARY x86-64 IOMMU P: Muli Ben-Yehuda M: muli@il.ibm.com @@ -1053,6 +1036,15 @@ L: linux-kernel@vger.kernel.org L: discuss@x86-64.org S: Maintained +CAN NETWORK LAYER +P: Urs Thuermann +M: urs.thuermann@volkswagen.de +P: Oliver Hartkopp +M: oliver.hartkopp@volkswagen.de +L: socketcan-core@lists.berlios.de (subscribers-only) +W: http://developer.berlios.de/projects/socketcan/ +S: Maintained + CELL BROADBAND ENGINE ARCHITECTURE P: Arnd Bergmann M: arnd@arndb.de @@ -1061,13 +1053,6 @@ L: cbe-oss-dev@ozlabs.org W: http://www.ibm.com/developerworks/power/cell/ S: Supported -CISCO 10G ETHERNET DRIVER -P: Scott Feldman -M: scofeldm@cisco.com -P: Joe Eykholt -M: jeykholt@cisco.com -S: Supported - CFAG12864B LCD DRIVER P: Miguel Ojeda Sandonis M: miguel.ojeda.sandonis@gmail.com @@ -1099,19 +1084,11 @@ P: Joel Schopp M: jschopp@austin.ibm.com S: Supported -COMMON INTERNET FILE SYSTEM (CIFS) -P: Steve French -M: sfrench@samba.org -L: linux-cifs-client@lists.samba.org -L: samba-technical@lists.samba.org -W: http://linux-cifs.samba.org/ -T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git -S: Supported - -CONFIGFS -P: Joel Becker -M: joel.becker@oracle.com -L: linux-kernel@vger.kernel.org +CISCO 10G ETHERNET DRIVER +P: Scott Feldman +M: scofeldm@cisco.com +P: Joe Eykholt +M: jeykholt@cisco.com S: Supported CIRRUS LOGIC EP93XX ETHERNET DRIVER @@ -1147,6 +1124,15 @@ L: codalist@coda.cs.cmu.edu W: http://www.coda.cs.cmu.edu/ S: Maintained +COMMON INTERNET FILE SYSTEM (CIFS) +P: Steve French +M: sfrench@samba.org +L: linux-cifs-client@lists.samba.org +L: samba-technical@lists.samba.org +W: http://linux-cifs.samba.org/ +T: git kernel.org:/pub/scm/linux/kernel/git/sfrench/cifs-2.6.git +S: Supported + COMPACTPCI HOTPLUG CORE P: Scott Murray M: scottm@somanetworks.com @@ -1186,6 +1172,12 @@ L: accessrunner-general@lists.sourceforge.net W: http://accessrunner.sourceforge.net/ S: Maintained +CONFIGFS +P: Joel Becker +M: joel.becker@oracle.com +L: linux-kernel@vger.kernel.org +S: Supported + CONTROL GROUPS (CGROUPS) P: Paul Menage M: menage@google.com @@ -1309,6 +1301,20 @@ W: http://www.qsl.net/dl1bke/ L: linux-hams@vger.kernel.org S: Maintained +DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER +P: Tobias Ringstrom +M: tori@unhappy.mine.nu +L: netdev@vger.kernel.org +S: Maintained + +DC390/AM53C974 SCSI driver +P: Kurt Garloff +M: garloff@suse.de +W: http://www.garloff.de/kurt/linux/dc390/ +P: Guennadi Liakhovetski +M: g.liakhovetski@gmx.de +S: Maintained + DC395x SCSI driver P: Oliver Neukum M: oliver@neukum.name @@ -1321,14 +1327,6 @@ L: dc395x@twibble.org L: http://lists.twibble.org/mailman/listinfo/dc395x/ S: Maintained -DC390/AM53C974 SCSI driver -P: Kurt Garloff -M: garloff@suse.de -W: http://www.garloff.de/kurt/linux/dc390/ -P: Guennadi Liakhovetski -M: g.liakhovetski@gmx.de -S: Maintained - DCCP PROTOCOL P: Arnaldo Carvalho de Melo M: acme@ghostprotocols.net @@ -1359,12 +1357,6 @@ P: Doug Warzecha M: Douglas_Warzecha@dell.com S: Maintained -DEVICE-MAPPER (LVM) -P: Alasdair Kergon -L: dm-devel@redhat.com -W: http://sources.redhat.com/dm -S: Maintained - DEVICE NUMBER REGISTRY P: Torben Mathiasen M: device@lanana.org @@ -1372,6 +1364,12 @@ W: http://lanana.org/docs/device-list/index.html L: linux-kernel@vger.kernel.org S: Maintained +DEVICE-MAPPER (LVM) +P: Alasdair Kergon +L: dm-devel@redhat.com +W: http://sources.redhat.com/dm +S: Maintained + DIGI INTL. EPCA DRIVER P: Digi International, Inc M: Eng.Linux@digi.com @@ -1379,7 +1377,7 @@ L: Eng.Linux@digi.com W: http://www.digi.com S: Orphan -DIRECTORY NOTIFICATION +DIRECTORY NOTIFICATION (DNOTIFY) P: Stephen Rothwell M: sfr@canb.auug.org.au L: linux-kernel@vger.kernel.org @@ -1393,13 +1391,13 @@ W: http://www.win.tue.nl/~aeb/linux/zip/zip-1.html W: http://www.win.tue.nl/~aeb/partitions/partition_types-1.html S: Maintained -DISKQUOTA: +DISKQUOTA P: Jan Kara M: jack@suse.cz L: linux-kernel@vger.kernel.org S: Maintained -DISTRIBUTED LOCK MANAGER +DISTRIBUTED LOCK MANAGER (DLM) P: Christine Caulfield M: ccaulfie@redhat.com P: David Teigland @@ -1409,12 +1407,6 @@ W: http://sources.redhat.com/cluster/ T: git kernel.org:/pub/scm/linux/kernel/git/teigland/dlm.git S: Supported -DAVICOM FAST ETHERNET (DMFE) NETWORK DRIVER -P: Tobias Ringstrom -M: tori@unhappy.mine.nu -L: netdev@vger.kernel.org -S: Maintained - DMA GENERIC OFFLOAD ENGINE SUBSYSTEM P: Maciej Sosnowski M: maciej.sosnowski@intel.com @@ -1454,6 +1446,13 @@ M: jrv@vanzandt.mv.com L: blinux-list@redhat.com S: Maintained +DPT_I2O SCSI RAID DRIVER +P: Adaptec OEM Raid Solutions +M: aacraid@adaptec.com +L: linux-scsi@vger.kernel.org +W: http://www.adaptec.com/ +S: Maintained + DRIVER CORE, KOBJECTS, AND SYSFS P: Greg Kroah-Hartman M: gregkh@suse.de @@ -1602,7 +1601,7 @@ EFS FILESYSTEM W: http://aeschi.ch.eu.org/efs/ S: Orphan -EHCA (IBM GX bus InfiniBand adapter) DRIVER: +EHCA (IBM GX bus InfiniBand adapter) DRIVER P: Hoang-Nam Nguyen M: hnguyen@de.ibm.com P: Christoph Raisch @@ -1682,6 +1681,42 @@ P: Akinobu Mita M: akinobu.mita@gmail.com S: Supported +FILE LOCKING (flock() and fcntl()/lockf()) +P: Matthew Wilcox +M: matthew@wil.cx +L: linux-fsdevel@vger.kernel.org +S: Maintained + +FILESYSTEMS (VFS and infrastructure) +P: Alexander Viro +M: viro@zeniv.linux.org.uk +L: linux-fsdevel@vger.kernel.org +S: Maintained + +FIREWIRE SUBSYSTEM (drivers/firewire, <linux/firewire*.h>) +P: Kristian Hoegsberg, Stefan Richter +M: krh@redhat.com, stefanr@s5r6.in-berlin.de +L: linux1394-devel@lists.sourceforge.net +W: http://www.linux1394.org/ +T: git kernel.org:/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git +S: Maintained + +FIRMWARE LOADER (request_firmware) +L: linux-kernel@vger.kernel.org +S: Orphan + +FPU EMULATOR +P: Bill Metzenthen +M: billm@suburbia.net +W: http://suburbia.net/~billm/floating-point/emulator/ +S: Maintained + +FRAME RELAY DLCI/FRAD (Sangoma drivers too) +P: Mike McLagan +M: mike.mclagan@linux.org +L: netdev@vger.kernel.org +S: Maintained + FRAMEBUFFER LAYER P: Antonino Daplas M: adaplas@gmail.com @@ -1747,42 +1782,6 @@ L: alsa-devel@alsa-project.org L: linuxppc-dev@ozlabs.org S: Supported -FILE LOCKING (flock() and fcntl()/lockf()) -P: Matthew Wilcox -M: matthew@wil.cx -L: linux-fsdevel@vger.kernel.org -S: Maintained - -FILESYSTEMS (VFS and infrastructure) -P: Alexander Viro -M: viro@zeniv.linux.org.uk -L: linux-fsdevel@vger.kernel.org -S: Maintained - -FIREWIRE SUBSYSTEM (drivers/firewire, <linux/firewire*.h>) -P: Kristian Hoegsberg, Stefan Richter -M: krh@redhat.com, stefanr@s5r6.in-berlin.de -L: linux1394-devel@lists.sourceforge.net -W: http://www.linux1394.org/ -T: git kernel.org:/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6.git -S: Maintained - -FIRMWARE LOADER (request_firmware) -L: linux-kernel@vger.kernel.org -S: Orphan - -FPU EMULATOR -P: Bill Metzenthen -M: billm@suburbia.net -W: http://suburbia.net/~billm/floating-point/emulator/ -S: Maintained - -FRAME RELAY DLCI/FRAD (Sangoma drivers too) -P: Mike McLagan -M: mike.mclagan@linux.org -L: netdev@vger.kernel.org -S: Maintained - FREEVXFS FILESYSTEM P: Christoph Hellwig M: hch@infradead.org @@ -1854,6 +1853,13 @@ L: gigaset307x-common@lists.sourceforge.net W: http://gigaset307x.sourceforge.net/ S: Maintained +HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER +P: Robert Love +M: rlove@rlove.org +M: linux-kernel@vger.kernel.org +W: http://www.kernel.org/pub/linux/kernel/people/rml/hdaps/ +S: Maintained + HARDWARE MONITORING L: lm-sensors@lm-sensors.org W: http://www.lm-sensors.org/ @@ -1862,13 +1868,6 @@ S: Orphan HARDWARE RANDOM NUMBER GENERATOR CORE S: Orphan -HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER -P: Robert Love -M: rlove@rlove.org -M: linux-kernel@vger.kernel.org -W: http://www.kernel.org/pub/linux/kernel/people/rml/hdaps/ -S: Maintained - HARMONY SOUND DRIVER P: Kyle McMartin M: kyle@mcmartin.ca @@ -1882,6 +1881,24 @@ L: linux-kernel@vger.kernel.org W: http://www.nyx.net/~arobinso S: Maintained +HEWLETT-PACKARD FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA +P: Chirag Kantharia +M: chirag.kantharia@hp.com +L: iss_storagedev@hp.com +S: Maintained + +HEWLETT-PACKARD SMART2 RAID DRIVER +P: Chirag Kantharia +M: chirag.kantharia@hp.com +L: iss_storagedev@hp.com +S: Maintained + +HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss) +P: Mike Miller +M: mike.miller@hp.com +L: iss_storagedev@hp.com +S: Supported + HFS FILESYSTEM P: Roman Zippel M: zippel@linux-m68k.org @@ -1895,6 +1912,14 @@ L: linux-nvidia@lists.surfsouth.com W: http://drama.obuda.kando.hu/~fero/cgi-bin/hgafb.shtml S: Maintained +HIBERNATION (aka Software Suspend, aka swsusp) +P: Pavel Machek +M: pavel@suse.cz +P: Rafael J. Wysocki +M: rjw@sisk.pl +L: linux-pm@lists.linux-foundation.org +S: Supported + HID CORE LAYER P: Jiri Kosina M: jkosina@suse.cz @@ -1927,24 +1952,6 @@ M: jes@trained-monkey.org L: linux-hippi@sunsite.dk S: Maintained -HEWLETT-PACKARD FIBRE CHANNEL 64-bit/66MHz PCI non-intelligent HBA -P: Chirag Kantharia -M: chirag.kantharia@hp.com -L: iss_storagedev@hp.com -S: Maintained - -HEWLETT-PACKARD SMART2 RAID DRIVER -P: Chirag Kantharia -M: chirag.kantharia@hp.com -L: iss_storagedev@hp.com -S: Maintained - -HEWLETT-PACKARD SMART CISS RAID DRIVER (cciss) -P: Mike Miller -M: mike.miller@hp.com -L: iss_storagedev@hp.com -S: Supported - HOST AP DRIVER P: Jouni Malinen M: j@w1.fi @@ -1953,16 +1960,16 @@ L: linux-wireless@vger.kernel.org W: http://hostap.epitest.fi/ S: Maintained -HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series -P: Jaroslav Kysela -M: perex@perex.cz -S: Maintained - HP COMPAQ TC1100 TABLET WMI EXTRAS DRIVER P: Carlos Corbacho M: carlos@strangeworlds.co.uk S: Odd Fixes +HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series +P: Jaroslav Kysela +M: perex@perex.cz +S: Maintained + HPET: High Precision Event Timers driver (drivers/char/hpet.c) P: Clemens Ladisch M: clemens@ladisch.de @@ -2042,14 +2049,6 @@ W: http://www.ia64-linux.org/ T: git kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git S: Maintained -SN-IA64 (Itanium) SUB-PLATFORM -P: Jes Sorensen -M: jes@sgi.com -L: linux-altix@sgi.com -L: linux-ia64@vger.kernel.org -W: http://www.sgi.com/altix -S: Maintained - IBM MCA SCSI SUBSYSTEM DRIVER P: Michael Lang M: langa2@kph.uni-mainz.de @@ -2133,14 +2132,6 @@ W: http://www.openib.org/ T: git kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git S: Supported -INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS -P: Dmitry Torokhov -M: dmitry.torokhov@gmail.com -M: dtor@mail.ru -L: linux-input@vger.kernel.org -T: git kernel.org:/pub/scm/linux/kernel/git/dtor/input.git -S: Maintained - INOTIFY P: John McCutchan M: ttb@tentacle.dhs.org @@ -2149,6 +2140,14 @@ M: rml@novell.com L: linux-kernel@vger.kernel.org S: Maintained +INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN) DRIVERS +P: Dmitry Torokhov +M: dmitry.torokhov@gmail.com +M: dtor@mail.ru +L: linux-input@vger.kernel.org +T: git kernel.org:/pub/scm/linux/kernel/git/dtor/input.git +S: Maintained + INTEL FRAMEBUFFER DRIVER (excluding 810 and 815) P: Sylvain Meyer M: sylvain.meyer@worldonline.fr @@ -2253,7 +2252,7 @@ M: pfg@sgi.com L: linux-mips@linux-mips.org S: Maintained -IP MASQUERADING: +IP MASQUERADING P: Juanjo Ciarlante M: jjciarla@raiz.uncu.edu.ar S: Maintained @@ -2268,7 +2267,7 @@ M: jesse@icplus.com.tw L: netdev@vger.kernel.org S: Maintained -IPATH DRIVER: +IPATH DRIVER P: Ralph Campbell M: infinipath@qlogic.com L: general@lists.openfabrics.org @@ -2282,13 +2281,25 @@ L: openipmi-developer@lists.sourceforge.net W: http://openipmi.sourceforge.net/ S: Supported -IPX NETWORK LAYER -P: Arnaldo Carvalho de Melo -M: acme@ghostprotocols.net +IPS SCSI RAID DRIVER +P: Adaptec OEM Raid Solutions +M: aacraid@adaptec.com +L: linux-scsi@vger.kernel.org +W: http://www.adaptec.com/ +S: Maintained + +IPVS +P: Wensong Zhang +M: wensong@linux-vs.org +P: Simon Horman +M: horms@verge.net.au +P: Julian Anastasov +M: ja@ssi.bg L: netdev@vger.kernel.org +L: lvs-devel@vger.kernel.org S: Maintained -IPWIRELES DRIVER +IPWIRELESS DRIVER P: Jiri Kosina M: jkosina@suse.cz P: David Sterba @@ -2296,6 +2307,12 @@ M: dsterba@suse.cz S: Maintained T: git://git.kernel.org/pub/scm/linux/kernel/git/jikos/ipwireless_cs.git +IPX NETWORK LAYER +P: Arnaldo Carvalho de Melo +M: acme@ghostprotocols.net +L: netdev@vger.kernel.org +S: Maintained + IRDA SUBSYSTEM P: Samuel Ortiz M: samuel@sortiz.org @@ -2303,6 +2320,11 @@ L: irda-users@lists.sourceforge.net (subscribers-only) W: http://irda.sourceforge.net/ S: Maintained +ISAPNP +P: Jaroslav Kysela +M: perex@perex.cz +S: Maintained + ISCSI P: Mike Christie M: michaelc@cs.wisc.edu @@ -2311,11 +2333,6 @@ W: www.open-iscsi.org T: git kernel.org:/pub/scm/linux/kernel/mnc/linux-2.6-iscsi.git S: Maintained -ISAPNP -P: Jaroslav Kysela -M: perex@perex.cz -S: Maintained - ISDN SUBSYSTEM P: Karsten Keil M: kkeil@suse.de @@ -2340,6 +2357,14 @@ L: video4linux-list@redhat.com W: http://www.ivtvdriver.org S: Maintained +JFS FILESYSTEM +P: Dave Kleikamp +M: shaggy@austin.ibm.com +L: jfs-discussion@lists.sourceforge.net +W: http://jfs.sourceforge.net/ +T: git kernel.org:/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git +S: Supported + JME NETWORK DRIVER P: Guo-Fu Tseng M: cooldavid@cooldavid.org @@ -2353,24 +2378,6 @@ L: linux-mtd@lists.infradead.org W: http://www.linux-mtd.infradead.org/doc/jffs2.html S: Maintained -UBI FILE SYSTEM (UBIFS) -P: Artem Bityutskiy -M: dedekind@infradead.org -P: Adrian Hunter -M: ext-adrian.hunter@nokia.com -L: linux-mtd@lists.infradead.org -T: git git://git.infradead.org/~dedekind/ubifs-2.6.git -W: http://www.linux-mtd.infradead.org/doc/ubifs.html -S: Maintained - -JFS FILESYSTEM -P: Dave Kleikamp -M: shaggy@austin.ibm.com -L: jfs-discussion@lists.sourceforge.net -W: http://jfs.sourceforge.net/ -T: git kernel.org:/pub/scm/linux/kernel/git/shaggy/jfs-2.6.git -S: Supported - JOURNALLING LAYER FOR BLOCK DEVICES (JBD) P: Stephen Tweedie, Andrew Morton M: sct@redhat.com, akpm@linux-foundation.org @@ -2607,11 +2614,6 @@ W: http://www.pasemi.com/ L: linuxppc-dev@ozlabs.org S: Supported -LLC (802.2) -P: Arnaldo Carvalho de Melo -M: acme@ghostprotocols.net -S: Maintained - LINUX SECURITY MODULE (LSM) FRAMEWORK P: Chris Wright M: chrisw@sous-sol.org @@ -2619,6 +2621,11 @@ L: linux-security-module@vger.kernel.org T: git kernel.org:/pub/scm/linux/kernel/git/chrisw/lsm-2.6.git S: Supported +LLC (802.2) +P: Arnaldo Carvalho de Melo +M: acme@ghostprotocols.net +S: Maintained + LM83 HARDWARE MONITOR DRIVER P: Jean Delvare M: khali@linux-fr.org @@ -2719,14 +2726,6 @@ M: kaber@trash.net L: netdev@vger.kernel.org S: Maintained -MARVELL YUKON / SYSKONNECT DRIVER -P: Mirko Lindner -M: mlindner@syskonnect.de -P: Ralph Roesler -M: rroesler@syskonnect.de -W: http://www.syskonnect.com -S: Supported - MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7 P: Michael Kerrisk M: mtk.manpages@gmail.com @@ -2746,6 +2745,14 @@ M: buytenh@marvell.com L: netdev@vger.kernel.org S: Supported +MARVELL YUKON / SYSKONNECT DRIVER +P: Mirko Lindner +M: mlindner@syskonnect.de +P: Ralph Roesler +M: rroesler@syskonnect.de +W: http://www.syskonnect.com +S: Supported + MATROX FRAMEBUFFER DRIVER P: Petr Vandrovec M: vandrove@vc.cvut.cz @@ -2782,15 +2789,6 @@ L: linux-mm@kvack.org L: linux-kernel@vger.kernel.org S: Maintained -MEI MN10300/AM33 PORT -P: David Howells -M: dhowells@redhat.com -P: Koichi Yasutake -M: yasutake.koichi@jp.panasonic.com -L: linux-am33-list@redhat.com (moderated for non-subscribers) -W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/ -S: Maintained - MEMORY TECHNOLOGY DEVICES (MTD) P: David Woodhouse M: dwmw2@infradead.org @@ -2799,14 +2797,6 @@ L: linux-mtd@lists.infradead.org T: git git://git.infradead.org/mtd-2.6.git S: Maintained -UNSORTED BLOCK IMAGES (UBI) -P: Artem Bityutskiy -M: dedekind@infradead.org -W: http://www.linux-mtd.infradead.org/ -L: linux-mtd@lists.infradead.org -T: git git://git.infradead.org/~dedekind/ubi-2.6.git -S: Maintained - MICROTEK X6 SCANNER P: Oliver Neukum M: oliver@neukum.name @@ -3038,17 +3028,6 @@ L: netdev@vger.kernel.org W: http://www.netxen.com S: Supported -IPVS -P: Wensong Zhang -M: wensong@linux-vs.org -P: Simon Horman -M: horms@verge.net.au -P: Julian Anastasov -M: ja@ssi.bg -L: netdev@vger.kernel.org -L: lvs-devel@vger.kernel.org -S: Maintained - NFS, SUNRPC, AND LOCKD CLIENTS P: Trond Myklebust M: Trond.Myklebust@netapp.com @@ -3094,22 +3073,6 @@ M: adaplas@gmail.com L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers) S: Maintained -OPENCORES I2C BUS DRIVER -P: Peter Korsgaard -M: jacmet@sunsite.dk -L: i2c@lm-sensors.org -S: Maintained - -ORACLE CLUSTER FILESYSTEM 2 (OCFS2) -P: Mark Fasheh -M: mfasheh@suse.com -P: Joel Becker -M: joel.becker@oracle.com -L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers) -W: http://oss.oracle.com/projects/ocfs2/ -T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git -S: Supported - OMFS FILESYSTEM P: Bob Copeland M: me@bobcopeland.com @@ -3145,12 +3108,28 @@ L: osst-users@lists.sourceforge.net L: linux-scsi@vger.kernel.org S: Maintained +OPENCORES I2C BUS DRIVER +P: Peter Korsgaard +M: jacmet@sunsite.dk +L: i2c@lm-sensors.org +S: Maintained + OPROFILE P: Robert Richter M: robert.richter@amd.com L: oprofile-list@lists.sf.net S: Maintained +ORACLE CLUSTER FILESYSTEM 2 (OCFS2) +P: Mark Fasheh +M: mfasheh@suse.com +P: Joel Becker +M: joel.becker@oracle.com +L: ocfs2-devel@oss.oracle.com (moderated for non-subscribers) +W: http://oss.oracle.com/projects/ocfs2/ +T: git git://git.kernel.org/pub/scm/linux/kernel/git/mfasheh/ocfs2.git +S: Supported + ORINOCO DRIVER P: Pavel Roskin M: proski@gnu.org @@ -3162,6 +3141,14 @@ L: orinoco-devel@lists.sourceforge.net W: http://www.nongnu.org/orinoco/ S: Maintained +P54 WIRELESS DRIVER +P: Michael Wu +M: flamingice@sourmilk.net +L: linux-wireless@vger.kernel.org +W: http://prism54.org +T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git +S: Maintained + PA SEMI ETHERNET DRIVER P: Olof Johansson M: olof@lixom.net @@ -3174,10 +3161,32 @@ M: olof@lixom.net L: i2c@lm-sensors.org S: Maintained +PANASONIC MN10300/AM33 PORT +P: David Howells +M: dhowells@redhat.com +P: Koichi Yasutake +M: yasutake.koichi@jp.panasonic.com +L: linux-am33-list@redhat.com (moderated for non-subscribers) +W: ftp://ftp.redhat.com/pub/redhat/gnupro/AM33/ +S: Maintained + PARALLEL PORT SUPPORT L: linux-parport@lists.infradead.org (subscribers-only) S: Orphan +PARAVIRT_OPS INTERFACE +P: Jeremy Fitzhardinge +M: jeremy@xensource.com +P: Chris Wright +M: chrisw@sous-sol.org +P: Zachary Amsden +M: zach@vmware.com +P: Rusty Russell +M: rusty@rustcorp.com.au +L: virtualization@lists.osdl.org +L: linux-kernel@vger.kernel.org +S: Supported + PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES P: Tim Waugh M: tim@cyberelk.net @@ -3197,19 +3206,6 @@ W: http://www.parisc-linux.org/ T: git kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6.git S: Maintained -PARAVIRT_OPS INTERFACE -P: Jeremy Fitzhardinge -M: jeremy@xensource.com -P: Chris Wright -M: chrisw@sous-sol.org -P: Zachary Amsden -M: zach@vmware.com -P: Rusty Russell -M: rusty@rustcorp.com.au -L: virtualization@lists.osdl.org -L: linux-kernel@vger.kernel.org -S: Supported - PC87360 HARDWARE MONITORING DRIVER P: Jim Cromie M: jim.cromie@gmail.com @@ -3339,14 +3335,6 @@ L: kpreempt-tech@lists.sourceforge.net W: ftp://ftp.kernel.org/pub/linux/kernel/people/rml/preempt-kernel S: Supported -P54 WIRELESS DRIVER -P: Michael Wu -M: flamingice@sourmilk.net -L: linux-wireless@vger.kernel.org -W: http://prism54.org -T: git kernel.org:/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git -S: Maintained - PRISM54 WIRELESS DRIVER P: Luis R. Rodriguez M: mcgrof@gmail.com @@ -3437,13 +3425,7 @@ M: paulus@samba.org L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers) S: Maintained -RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER -P: Corey Thomas -M: coreythomas@charter.net -L: linux-wireless@vger.kernel.org -S: Maintained - -RALINK RT2X00 WLAN DRIVER +RALINK RT2X00 WIRELESS LAN DRIVER P: rt2x00 project L: linux-wireless@vger.kernel.org L: rt2400-devel@lists.sourceforge.net @@ -3468,6 +3450,18 @@ M: mporter@kernel.crashing.org L: linux-kernel@vger.kernel.org S: Maintained +RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER +P: Corey Thomas +M: coreythomas@charter.net +L: linux-wireless@vger.kernel.org +S: Maintained + +RCUTORTURE MODULE +P: Josh Triplett +M: josh@freedesktop.org +L: linux-kernel@vger.kernel.org +S: Maintained + RDC R-321X SoC P: Florian Fainelli M: florian.fainelli@telecomint.eu @@ -3487,12 +3481,6 @@ W: http://www.rdrop.com/users/paulmck/rclock/ L: linux-kernel@vger.kernel.org S: Supported -RCUTORTURE MODULE -P: Josh Triplett -M: josh@freedesktop.org -L: linux-kernel@vger.kernel.org -S: Maintained - REAL TIME CLOCK DRIVER P: Paul Gortmaker M: p_gortmaker@yahoo.com @@ -3516,6 +3504,9 @@ L: netdev@vger.kernel.org S: Maintained F: net/rfkill +RISCOM8 DRIVER +S: Orphan + ROCKETPORT DRIVER P: Comtrol Corp. W: http://www.comtrol.com @@ -3528,9 +3519,6 @@ L: linux-hams@vger.kernel.org W: http://www.linux-ax25.org/ S: Maintained -RISCOM8 DRIVER -S: Orphan - RTL818X WIRELESS DRIVER P: Michael Wu M: flamingice@sourmilk.net @@ -3670,6 +3658,12 @@ M: saschasommer@freenet.de L: sdricohcs-devel@lists.sourceforge.net (subscribers-only) S: Maintained +SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER +P: Pierre Ossman +M: drzeus-sdhci@drzeus.cx +L: sdhci-devel@list.drzeus.cx +S: Maintained + SECURITY CONTACT P: Security Officers M: security@kernel.org @@ -3693,19 +3687,13 @@ P: Jiri Slaby M: jirislaby@gmail.com S: Maintained -SERIAL ATA (SATA) SUBSYSTEM: +SERIAL ATA (SATA) SUBSYSTEM P: Jeff Garzik M: jgarzik@pobox.com L: linux-ide@vger.kernel.org T: git kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git S: Supported -SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER -P: Pat Gefre -M: pfg@sgi.com -L: linux-ia64@vger.kernel.org -S: Supported - SFC NETWORK DRIVER P: Steve Hodgson P: Ben Hutchings @@ -3713,6 +3701,17 @@ P: Robert Stonehouse M: linux-net-drivers@solarflare.com S: Supported +SGI GRU DRIVER +P: Jack Steiner +M: steiner@sgi.com +S: Maintained + +SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER +P: Pat Gefre +M: pfg@sgi.com +L: linux-ia64@vger.kernel.org +S: Supported + SGI VISUAL WORKSTATION 320 AND 540 P: Andrey Panin M: pazke@donpac.ru @@ -3720,16 +3719,24 @@ L: linux-visws-devel@lists.sf.net W: http://linux-visws.sf.net S: Maintained for 2.6. -SGI GRU DRIVER -P: Jack Steiner -M: steiner@sgi.com -S: Maintained - SGI XP/XPC/XPNET DRIVER P: Dean Nelson M: dcn@sgi.com S: Maintained +SHARP LH SUPPORT (LH7952X & LH7A40X) +P: Marc Singer +M: elf@buici.com +W: http://projects.buici.com/arm +L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) +S: Maintained + +SHPC HOTPLUG DRIVER +P: Kristen Carlson Accardi +M: kristen.c.accardi@intel.com +L: linux-pci@vger.kernel.org +S: Supported + SIMTEC EB110ATX (Chalice CATS) P: Ben Dooks P: Vincent Sanders @@ -3775,6 +3782,12 @@ M: thomas@winischhofer.net W: http://www.winischhofer.at/linuxsisusbvga.shtml S: Maintained +SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS +P: Stephen Hemminger +M: shemminger@linux-foundation.org +L: netdev@vger.kernel.org +S: Maintained + SLAB ALLOCATOR P: Christoph Lameter M: cl@linux-foundation.org @@ -3801,12 +3814,25 @@ P: Ben Nizette M: bn@niasdigital.com S: Maintained +SN-IA64 (Itanium) SUB-PLATFORM +P: Jes Sorensen +M: jes@sgi.com +L: linux-altix@sgi.com +L: linux-ia64@vger.kernel.org +W: http://www.sgi.com/altix +S: Maintained + SOC-CAMERA V4L2 SUBSYSTEM P: Guennadi Liakhovetski M: g.liakhovetski@gmx.de L: video4linux-list@redhat.com S: Maintained +SOEKRIS NET48XX LED SUPPORT +P: Chris Boot +M: bootc@bootc.net +S: Maintained + SOFTWARE RAID (Multiple Disks) SUPPORT P: Ingo Molnar M: mingo@redhat.com @@ -3815,24 +3841,6 @@ M: neilb@suse.de L: linux-raid@vger.kernel.org S: Supported -HIBERNATION (aka Software Suspend, aka swsusp): -P: Pavel Machek -M: pavel@suse.cz -P: Rafael J. Wysocki -M: rjw@sisk.pl -L: linux-pm@lists.linux-foundation.org -S: Supported - -SUSPEND TO RAM: -P: Len Brown -M: len.brown@intel.com -P: Pavel Machek -M: pavel@suse.cz -P: Rafael J. Wysocki -M: rjw@sisk.pl -L: linux-pm@lists.linux-foundation.org -S: Supported - SONIC NETWORK DRIVER P: Thomas Bogendoerfer M: tsbogend@alpha.franken.de @@ -3877,59 +3885,7 @@ L: alsa-devel@alsa-project.org (subscribers-only) W: http://alsa-project.org/main/index.php/ASoC S: Supported -SPI SUBSYSTEM -P: David Brownell -M: dbrownell@users.sourceforge.net -L: spi-devel-general@lists.sourceforge.net -S: Maintained - -SPU FILE SYSTEM -P: Jeremy Kerr -M: jk@ozlabs.org -L: linuxppc-dev@ozlabs.org -L: cbe-oss-dev@ozlabs.org -W: http://www.ibm.com/developerworks/power/cell/ -S: Supported - -STABLE BRANCH: -P: Greg Kroah-Hartman -M: greg@kroah.com -P: Chris Wright -M: chrisw@sous-sol.org -L: stable@kernel.org -S: Maintained - -SHARP LH SUPPORT (LH7952X & LH7A40X) -P: Marc Singer -M: elf@buici.com -W: http://projects.buici.com/arm -L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only) -S: Maintained - -SHPC HOTPLUG DRIVER -P: Kristen Carlson Accardi -M: kristen.c.accardi@intel.com -L: linux-pci@vger.kernel.org -S: Supported - -SECURE DIGITAL HOST CONTROLLER INTERFACE DRIVER -P: Pierre Ossman -M: drzeus-sdhci@drzeus.cx -L: sdhci-devel@list.drzeus.cx -S: Maintained - -SKGE, SKY2 10/100/1000 GIGABIT ETHERNET DRIVERS -P: Stephen Hemminger -M: shemminger@linux-foundation.org -L: netdev@vger.kernel.org -S: Maintained - -SOEKRIS NET48XX LED SUPPORT -P: Chris Boot -M: bootc@bootc.net -S: Maintained - -SPARC (sparc32): +SPARC (sparc32) P: William L. Irwin M: wli@holomorphy.com L: sparclinux@vger.kernel.org @@ -3941,6 +3897,12 @@ M: R.E.Wolff@BitWizard.nl L: linux-kernel@vger.kernel.org ? S: Supported +SPI SUBSYSTEM +P: David Brownell +M: dbrownell@users.sourceforge.net +L: spi-devel-general@lists.sourceforge.net +S: Maintained + SPIDERNET NETWORK DRIVER for CELL P: Ishizaki Kou M: kou.ishizaki@toshiba.co.jp @@ -3949,12 +3911,28 @@ M: jens@de.ibm.com L: netdev@vger.kernel.org S: Supported +SPU FILE SYSTEM +P: Jeremy Kerr +M: jk@ozlabs.org +L: linuxppc-dev@ozlabs.org +L: cbe-oss-dev@ozlabs.org +W: http://www.ibm.com/developerworks/power/cell/ +S: Supported + SRM (Alpha) environment access P: Jan-Benedict Glaw M: jbglaw@lug-owl.de L: linux-kernel@vger.kernel.org S: Maintained +STABLE BRANCH: +P: Greg Kroah-Hartman +M: greg@kroah.com +P: Chris Wright +M: chrisw@sous-sol.org +L: stable@kernel.org +S: Maintained + STARFIRE/DURALAN NETWORK DRIVER P: Ion Badulescu M: ionut@cs.columbia.edu @@ -3970,6 +3948,12 @@ M: laredo@gnu.org W: http://www.stradis.com/ S: Maintained +SUN3/3X +P: Sam Creasey +M: sammy@sammy.net +W: http://sammy.net/sun3/ +S: Maintained + SUPERH P: Paul Mundt M: lethal@linux-sh.org @@ -3978,11 +3962,15 @@ W: http://www.linux-sh.org T: git kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6.git S: Supported -SUN3/3X -P: Sam Creasey -M: sammy@sammy.net -W: http://sammy.net/sun3/ -S: Maintained +SUSPEND TO RAM +P: Len Brown +M: len.brown@intel.com +P: Pavel Machek +M: pavel@suse.cz +P: Rafael J. Wysocki +M: rjw@sisk.pl +L: linux-pm@lists.linux-foundation.org +S: Supported SVGA HANDLING P: Martin Mares @@ -4028,7 +4016,7 @@ P: Mark Gross M: mark.gross@intel.com S: Supported -TENSILICA XTENSA PORT (xtensa): +TENSILICA XTENSA PORT (xtensa) P: Chris Zankel M: chris@zankel.net S: Maintained @@ -4142,6 +4130,16 @@ M: ballabio_dario@emc.com L: linux-scsi@vger.kernel.org S: Maintained +UBI FILE SYSTEM (UBIFS) +P: Artem Bityutskiy +M: dedekind@infradead.org +P: Adrian Hunter +M: ext-adrian.hunter@nokia.com +L: linux-mtd@lists.infradead.org +T: git git://git.infradead.org/~dedekind/ubifs-2.6.git +W: http://www.linux-mtd.infradead.org/doc/ubifs.html +S: Maintained + UCLINUX (AND M68KNOMMU) P: Greg Ungerer M: gerg@uclinux.org @@ -4167,7 +4165,7 @@ M: dushistov@mail.ru L: linux-kernel@vger.kernel.org S: Maintained -UltraSPARC (sparc64): +UltraSPARC (sparc64) P: David S. Miller M: davem@davemloft.net L: sparclinux@vger.kernel.org @@ -4181,6 +4179,14 @@ L: linux-kernel@vger.kernel.org W: http://www.kernel.dk S: Maintained +UNSORTED BLOCK IMAGES (UBI) +P: Artem Bityutskiy +M: dedekind@infradead.org +W: http://www.linux-mtd.infradead.org/ +L: linux-mtd@lists.infradead.org +T: git git://git.infradead.org/~dedekind/ubi-2.6.git +S: Maintained + USB ACM DRIVER P: Oliver Neukum M: oliver@neukum.name @@ -4450,7 +4456,7 @@ L: video4linux-list@redhat.com W: http://royale.zerezo.com/zr364xx/ S: Maintained -USER-MODE LINUX +USER-MODE LINUX (UML) P: Jeff Dike M: jdike@addtoit.com L: user-mode-linux-devel@lists.sourceforge.net @@ -4474,7 +4480,7 @@ W: http://kernel.org/~kzak/util-linux-ng/ T: git://git.kernel.org/pub/scm/utils/util-linux-ng/util-linux-ng.git S: Maintained -VFAT/FAT/MSDOS FILESYSTEM: +VFAT/FAT/MSDOS FILESYSTEM P: OGAWA Hirofumi M: hirofumi@mail.parknet.co.jp L: linux-kernel@vger.kernel.org @@ -4497,7 +4503,7 @@ M: romieu@fr.zoreil.com L: netdev@vger.kernel.org S: Maintained -VIDEO FOR LINUX +VIDEO FOR LINUX (V4L) P: Mauro Carvalho Chehab M: mchehab@infradead.org M: v4l-dvb-maintainer@linuxtv.org @@ -4603,6 +4609,17 @@ M: eis@baty.hanse.de L: linux-x25@vger.kernel.org S: Maintained +X86 ARCHITECTURE (32-BIT AND 64-BIT) +P: Thomas Gleixner +M: tglx@linutronix.de +P: Ingo Molnar +M: mingo@redhat.com +P: H. Peter Anvin +M: hpa@zytor.com +L: linux-kernel@vger.kernel.org +T: git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git +S: Maintained + XEN HYPERVISOR INTERFACE P: Jeremy Fitzhardinge M: jeremy@xensource.com @@ -4634,17 +4651,6 @@ M: jacmet@sunsite.dk L: linux-serial@vger.kernel.org S: Maintained -X86 ARCHITECTURE (32-BIT AND 64-BIT) -P: Thomas Gleixner -M: tglx@linutronix.de -P: Ingo Molnar -M: mingo@redhat.com -P: H. Peter Anvin -M: hpa@zytor.com -L: linux-kernel@vger.kernel.org -T: git://git.kernel.org/pub/scm/linux/kernel/git/x86/linux-2.6-x86.git -S: Maintained - YAM DRIVER FOR AX.25 P: Jean-Paul Roubelat M: jpr@f6fbb.org diff --git a/arch/Kconfig b/arch/Kconfig index 364c6dadde0..0267babe5eb 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -13,6 +13,20 @@ config OPROFILE If unsure, say N. +config OPROFILE_IBS + bool "OProfile AMD IBS support (EXPERIMENTAL)" + default n + depends on OPROFILE && SMP && X86 + help + Instruction-Based Sampling (IBS) is a new profiling + technique that provides rich, precise program performance + information. IBS is introduced by AMD Family10h processors + (AMD Opteron Quad-Core processor “Barcelona”) to overcome + the limitations of conventional performance counter + sampling. + + If unsure, say N. + config HAVE_OPROFILE def_bool n diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 1bec55d63ef..ee35226c44e 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -5,6 +5,7 @@ config ALPHA bool default y + select HAVE_AOUT select HAVE_IDE select HAVE_OPROFILE help @@ -68,9 +69,6 @@ config AUTO_IRQ_AFFINITY depends on SMP default y -config ARCH_SUPPORTS_AOUT - def_bool y - source "init/Kconfig" diff --git a/arch/alpha/include/asm/statfs.h b/arch/alpha/include/asm/statfs.h index ad15830baef..de35cd438a1 100644 --- a/arch/alpha/include/asm/statfs.h +++ b/arch/alpha/include/asm/statfs.h @@ -1,6 +1,10 @@ #ifndef _ALPHA_STATFS_H #define _ALPHA_STATFS_H +/* Alpha is the only 64-bit platform with 32-bit statfs. And doesn't + even seem to implement statfs64 */ +#define __statfs_word __u32 + #include <asm-generic/statfs.h> #endif diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index efeed65b4a6..4853f9df37b 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -8,6 +8,7 @@ mainmenu "Linux Kernel Configuration" config ARM bool default y + select HAVE_AOUT select HAVE_IDE select RTC_LIB select SYS_SUPPORTS_APM_EMULATION @@ -140,9 +141,6 @@ config GENERIC_CALIBRATE_DELAY bool default y -config ARCH_SUPPORTS_AOUT - def_bool y - config ARCH_MAY_HAVE_PC_FDC bool diff --git a/arch/arm/include/asm/statfs.h b/arch/arm/include/asm/statfs.h index a02e6a8c3d7..079447c05ba 100644 --- a/arch/arm/include/asm/statfs.h +++ b/arch/arm/include/asm/statfs.h @@ -1,42 +1,12 @@ #ifndef _ASMARM_STATFS_H #define _ASMARM_STATFS_H -#ifndef __KERNEL_STRICT_NAMES -# include <linux/types.h> -typedef __kernel_fsid_t fsid_t; -#endif - -struct statfs { - __u32 f_type; - __u32 f_bsize; - __u32 f_blocks; - __u32 f_bfree; - __u32 f_bavail; - __u32 f_files; - __u32 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; - /* * With EABI there is 4 bytes of padding added to this structure. * Let's pack it so the padding goes away to simplify dual ABI support. * Note that user space does NOT have to pack this structure. */ -struct statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -} __attribute__ ((packed,aligned(4))); +#define ARCH_PACK_STATFS64 __attribute__((packed,aligned(4))) +#include <asm-generic/statfs.h> #endif diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index ba99a203552..d006085ed7e 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -26,23 +26,6 @@ EXPORT_SYMBOL(dma_spin_lock); static dma_t dma_chan[MAX_DMA_CHANNELS]; /* - * Get dma list for /proc/dma - */ -int get_dma_list(char *buf) -{ - dma_t *dma; - char *p = buf; - int i; - - for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++) - if (dma->lock) - p += sprintf(p, "%2d: %14s %s\n", i, - dma->d_ops->type, dma->device_id); - - return p - buf; -} - -/* * Request DMA channel * * On certain platforms, we have to allocate an interrupt as well... diff --git a/arch/arm/mach-orion5x/include/mach/orion5x.h b/arch/arm/mach-orion5x/include/mach/orion5x.h index e67c843baa0..9f5ce1ce584 100644 --- a/arch/arm/mach-orion5x/include/mach/orion5x.h +++ b/arch/arm/mach-orion5x/include/mach/orion5x.h @@ -157,9 +157,11 @@ #define CPU_CONF ORION5X_BRIDGE_REG(0x100) #define CPU_CTRL ORION5X_BRIDGE_REG(0x104) #define CPU_RESET_MASK ORION5X_BRIDGE_REG(0x108) +#define WDT_RESET 0x0002 #define CPU_SOFT_RESET ORION5X_BRIDGE_REG(0x10c) #define POWER_MNG_CTRL_REG ORION5X_BRIDGE_REG(0x11C) #define BRIDGE_CAUSE ORION5X_BRIDGE_REG(0x110) +#define WDT_INT_REQ 0x0008 #define BRIDGE_MASK ORION5X_BRIDGE_REG(0x114) #define BRIDGE_INT_TIMER0 0x0002 #define BRIDGE_INT_TIMER1 0x0004 diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index a716ecd1db2..97187fa0ae5 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -441,16 +441,8 @@ static inline void omap_init_uwire(void) {} #if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) -#ifdef CONFIG_ARCH_OMAP24XX -#define OMAP_WDT_BASE 0x48022000 -#else -#define OMAP_WDT_BASE 0xfffeb000 -#endif - static struct resource wdt_resources[] = { { - .start = OMAP_WDT_BASE, - .end = OMAP_WDT_BASE + 0x4f, .flags = IORESOURCE_MEM, }, }; @@ -464,6 +456,19 @@ static struct platform_device omap_wdt_device = { static void omap_init_wdt(void) { + if (cpu_is_omap16xx()) + wdt_resources[0].start = 0xfffeb000; + else if (cpu_is_omap2420()) + wdt_resources[0].start = 0x48022000; /* WDT2 */ + else if (cpu_is_omap2430()) + wdt_resources[0].start = 0x49016000; /* WDT2 */ + else if (cpu_is_omap343x()) + wdt_resources[0].start = 0x48314000; /* WDT2 */ + else + return; + + wdt_resources[0].end = wdt_resources[0].start + 0x4f; + (void) platform_device_register(&omap_wdt_device); } #else diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c index b8286f1ce85..6c54580a66d 100644 --- a/arch/avr32/boards/atngw100/setup.c +++ b/arch/avr32/boards/atngw100/setup.c @@ -9,6 +9,7 @@ */ #include <linux/clk.h> #include <linux/etherdevice.h> +#include <linux/gpio.h> #include <linux/irq.h> #include <linux/i2c.h> #include <linux/i2c-gpio.h> @@ -53,8 +54,11 @@ static struct spi_board_info spi0_board_info[] __initdata = { }; static struct mci_platform_data __initdata mci0_data = { - .detect_pin = GPIO_PIN_PC(25), - .wp_pin = GPIO_PIN_PE(0), + .slot[0] = { + .bus_width = 4, + .detect_pin = GPIO_PIN_PC(25), + .wp_pin = GPIO_PIN_PE(0), + }, }; /* @@ -190,7 +194,7 @@ static int __init atngw100_init(void) * PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus), * but it's not available off-board. */ - at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP); + at32_select_periph(GPIO_PIOB_BASE, 1 << 28, 0, AT32_GPIOF_PULLUP); at32_select_gpio(i2c_gpio_data.sda_pin, AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH); at32_select_gpio(i2c_gpio_data.scl_pin, @@ -204,6 +208,15 @@ postcore_initcall(atngw100_init); static int __init atngw100_arch_init(void) { + /* PB30 is the otherwise unused jumper on the mainboard, with an + * external pullup; the jumper grounds it. Use it however you + * like, including letting U-Boot or Linux tweak boot sequences. + */ + at32_select_gpio(GPIO_PIN_PB(30), 0); + gpio_request(GPIO_PIN_PB(30), "j15"); + gpio_direction_input(GPIO_PIN_PB(30)); + gpio_export(GPIO_PIN_PB(30), false); + /* set_irq_type() after the arch_initcall for EIC has run, and * before the I2C subsystem could try using this IRQ. */ diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c index dfc3443e23a..29e5b51a7fd 100644 --- a/arch/avr32/boards/atstk1000/atstk1002.c +++ b/arch/avr32/boards/atstk1000/atstk1002.c @@ -232,7 +232,7 @@ static void __init atstk1002_setup_extdac(void) goto err_set_clk; } - at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0); at73c213_data.dac_clk = gclk; err_set_clk: @@ -264,16 +264,20 @@ void __init setup_board(void) #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM +static struct mci_platform_data __initdata mci0_data = { + .slot[0] = { + .bus_width = 4, + /* MMC card detect requires MACB0 *NOT* be used */ #ifdef CONFIG_BOARD_ATSTK1002_SW6_CUSTOM -static struct mci_platform_data __initdata mci0_data = { - .detect_pin = GPIO_PIN_PC(14), /* gpio30/sdcd */ - .wp_pin = GPIO_PIN_PC(15), /* gpio31/sdwp */ -}; -#define MCI_PDATA &mci0_data + .detect_pin = GPIO_PIN_PC(14), /* gpio30/sdcd */ + .wp_pin = GPIO_PIN_PC(15), /* gpio31/sdwp */ #else -#define MCI_PDATA NULL + .detect_pin = -ENODEV, + .wp_pin = -ENODEV, #endif /* SW6 for sd{cd,wp} routing */ + }, +}; #endif /* SW2 for MMC signal routing */ @@ -326,13 +330,14 @@ static int __init atstk1002_init(void) at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM - at32_add_device_mci(0, MCI_PDATA); + at32_add_device_mci(0, &mci0_data); #endif #ifdef CONFIG_BOARD_ATSTK1002_SW5_CUSTOM set_hw_addr(at32_add_device_eth(1, ð_data[1])); #else at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size, 0); + fbmem_start, fbmem_size, + ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL); #endif at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c index 0cf664174c1..be089d7f37e 100644 --- a/arch/avr32/boards/atstk1000/atstk1003.c +++ b/arch/avr32/boards/atstk1000/atstk1003.c @@ -19,6 +19,7 @@ #include <linux/spi/spi.h> #include <asm/setup.h> +#include <asm/atmel-mci.h> #include <mach/at32ap700x.h> #include <mach/board.h> @@ -66,6 +67,16 @@ static struct spi_board_info spi1_board_info[] __initdata = { { } }; #endif +#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM +static struct mci_platform_data __initdata mci0_data = { + .slot[0] = { + .bus_width = 4, + .detect_pin = -ENODEV, + .wp_pin = -ENODEV, + }, +}; +#endif + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static void __init atstk1003_setup_extdac(void) { @@ -84,7 +95,7 @@ static void __init atstk1003_setup_extdac(void) goto err_set_clk; } - at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0); at73c213_data.dac_clk = gclk; err_set_clk: @@ -154,7 +165,7 @@ static int __init atstk1003_init(void) at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM - at32_add_device_mci(0, NULL); + at32_add_device_mci(0, &mci0_data); #endif at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c index 50a5273e591..248ef237c16 100644 --- a/arch/avr32/boards/atstk1000/atstk1004.c +++ b/arch/avr32/boards/atstk1000/atstk1004.c @@ -21,6 +21,7 @@ #include <video/atmel_lcdc.h> #include <asm/setup.h> +#include <asm/atmel-mci.h> #include <mach/at32ap700x.h> #include <mach/board.h> @@ -71,6 +72,16 @@ static struct spi_board_info spi1_board_info[] __initdata = { { } }; #endif +#ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM +static struct mci_platform_data __initdata mci0_data = { + .slot[0] = { + .bus_width = 4, + .detect_pin = -ENODEV, + .wp_pin = -ENODEV, + }, +}; +#endif + #ifdef CONFIG_BOARD_ATSTK1000_EXTDAC static void __init atstk1004_setup_extdac(void) { @@ -89,7 +100,7 @@ static void __init atstk1004_setup_extdac(void) goto err_set_clk; } - at32_select_periph(GPIO_PIN_PA(30), GPIO_PERIPH_A, 0); + at32_select_periph(GPIO_PIOA_BASE, (1 << 30), GPIO_PERIPH_A, 0); at73c213_data.dac_clk = gclk; err_set_clk: @@ -137,10 +148,11 @@ static int __init atstk1004_init(void) at32_add_device_spi(1, spi1_board_info, ARRAY_SIZE(spi1_board_info)); #endif #ifndef CONFIG_BOARD_ATSTK100X_SW2_CUSTOM - at32_add_device_mci(0, NULL); + at32_add_device_mci(0, &mci0_data); #endif at32_add_device_lcdc(0, &atstk1000_lcdc_data, - fbmem_start, fbmem_size, 0); + fbmem_start, fbmem_size, + ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL); at32_add_device_usba(0, NULL); #ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM at32_add_device_ssc(0, ATMEL_SSC_TX); diff --git a/arch/avr32/include/asm/a.out.h b/arch/avr32/include/asm/a.out.h deleted file mode 100644 index e46375a34a7..00000000000 --- a/arch/avr32/include/asm/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ASM_AVR32_A_OUT_H -#define __ASM_AVR32_A_OUT_H - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __ASM_AVR32_A_OUT_H */ diff --git a/arch/avr32/include/asm/atmel-mci.h b/arch/avr32/include/asm/atmel-mci.h index c2ea6e1c9aa..59f3fadd0b6 100644 --- a/arch/avr32/include/asm/atmel-mci.h +++ b/arch/avr32/include/asm/atmel-mci.h @@ -1,9 +1,39 @@ #ifndef __ASM_AVR32_ATMEL_MCI_H #define __ASM_AVR32_ATMEL_MCI_H -struct mci_platform_data { +#define ATMEL_MCI_MAX_NR_SLOTS 2 + +struct dma_slave; + +/** + * struct mci_slot_pdata - board-specific per-slot configuration + * @bus_width: Number of data lines wired up the slot + * @detect_pin: GPIO pin wired to the card detect switch + * @wp_pin: GPIO pin wired to the write protect sensor + * + * If a given slot is not present on the board, @bus_width should be + * set to 0. The other fields are ignored in this case. + * + * Any pins that aren't available should be set to a negative value. + * + * Note that support for multiple slots is experimental -- some cards + * might get upset if we don't get the clock management exactly right. + * But in most cases, it should work just fine. + */ +struct mci_slot_pdata { + unsigned int bus_width; int detect_pin; int wp_pin; }; +/** + * struct mci_platform_data - board-specific MMC/SDcard configuration + * @dma_slave: DMA slave interface to use in data transfers, or NULL. + * @slot: Per-slot configuration data. + */ +struct mci_platform_data { + struct dma_slave *dma_slave; + struct mci_slot_pdata slot[ATMEL_MCI_MAX_NR_SLOTS]; +}; + #endif /* __ASM_AVR32_ATMEL_MCI_H */ diff --git a/arch/avr32/include/asm/byteorder.h b/arch/avr32/include/asm/byteorder.h index d77b48ba733..8e3af02076d 100644 --- a/arch/avr32/include/asm/byteorder.h +++ b/arch/avr32/include/asm/byteorder.h @@ -7,6 +7,9 @@ #include <asm/types.h> #include <linux/compiler.h> +#define __BIG_ENDIAN +#define __SWAB_64_THRU_32__ + #ifdef __CHECKER__ extern unsigned long __builtin_bswap_32(unsigned long x); extern unsigned short __builtin_bswap_16(unsigned short x); @@ -17,15 +20,18 @@ extern unsigned short __builtin_bswap_16(unsigned short x); * the result. */ #if !(__GNUC__ == 4 && __GNUC_MINOR__ < 2) -#define __arch__swab32(x) __builtin_bswap_32(x) -#define __arch__swab16(x) __builtin_bswap_16(x) -#endif +static inline __attribute_const__ __u16 __arch_swab16(__u16 val) +{ + return __builtin_bswap_16(val); +} +#define __arch_swab16 __arch_swab16 -#if !defined(__STRICT_ANSI__) || defined(__KERNEL__) -# define __BYTEORDER_HAS_U64__ -# define __SWAB_64_THRU_32__ +static inline __attribute_const__ __u32 __arch_swab32(__u32 val) +{ + return __builtin_bswap_32(val); +} +#define __arch_swab32 __arch_swab32 #endif -#include <linux/byteorder/big_endian.h> - +#include <linux/byteorder.h> #endif /* __ASM_AVR32_BYTEORDER_H */ diff --git a/arch/avr32/include/asm/io.h b/arch/avr32/include/asm/io.h index a520f77ead9..22c97ef9220 100644 --- a/arch/avr32/include/asm/io.h +++ b/arch/avr32/include/asm/io.h @@ -160,6 +160,14 @@ BUILDIO_IOPORT(l, u32) #define readw_relaxed readw #define readl_relaxed readl +#define readb_be __raw_readb +#define readw_be __raw_readw +#define readl_be __raw_readl + +#define writeb_be __raw_writeb +#define writew_be __raw_writew +#define writel_be __raw_writel + #define __BUILD_MEMORY_STRING(bwl, type) \ static inline void writes##bwl(volatile void __iomem *addr, \ const void *data, unsigned int count) \ diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 2c08ac992ac..134d5302b6d 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -9,6 +9,7 @@ #include <linux/module.h> #include <linux/kallsyms.h> #include <linux/fs.h> +#include <linux/pm.h> #include <linux/ptrace.h> #include <linux/reboot.h> #include <linux/tick.h> @@ -20,7 +21,7 @@ #include <mach/pm.h> -void (*pm_power_off)(void) = NULL; +void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); /* diff --git a/arch/avr32/kernel/setup.c b/arch/avr32/kernel/setup.c index d8e623c426c..5c7083916c3 100644 --- a/arch/avr32/kernel/setup.c +++ b/arch/avr32/kernel/setup.c @@ -283,6 +283,25 @@ static int __init early_parse_fbmem(char *p) } early_param("fbmem", early_parse_fbmem); +/* + * Pick out the memory size. We look for mem=size@start, + * where start and size are "size[KkMmGg]" + */ +static int __init early_mem(char *p) +{ + resource_size_t size, start; + + start = system_ram->start; + size = memparse(p, &p); + if (*p == '@') + start = memparse(p + 1, &p); + + system_ram->start = start; + system_ram->end = system_ram->start + size - 1; + return 0; +} +early_param("mem", early_mem); + static int __init parse_tag_core(struct tag *tag) { if (tag->hdr.size > 2) { diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c index e01dbe4ebb4..813b6844cdf 100644 --- a/arch/avr32/mach-at32ap/at32ap700x.c +++ b/arch/avr32/mach-at32ap/at32ap700x.c @@ -82,8 +82,9 @@ static struct platform_device _name##_id##_device = { \ .num_resources = ARRAY_SIZE(_name##_id##_resource), \ } -#define select_peripheral(pin, periph, flags) \ - at32_select_periph(GPIO_PIN_##pin, GPIO_##periph, flags) +#define select_peripheral(port, pin_mask, periph, flags) \ + at32_select_periph(GPIO_##port##_BASE, pin_mask, \ + GPIO_##periph, flags) #define DEV_CLK(_name, devname, bus, _index) \ static struct clk devname##_##_name = { \ @@ -871,6 +872,7 @@ static struct clk atmel_psif1_pclk = { struct platform_device *__init at32_add_device_psif(unsigned int id) { struct platform_device *pdev; + u32 pin_mask; if (!(id == 0 || id == 1)) return NULL; @@ -881,20 +883,22 @@ struct platform_device *__init at32_add_device_psif(unsigned int id) switch (id) { case 0: + pin_mask = (1 << 8) | (1 << 9); /* CLOCK & DATA */ + if (platform_device_add_resources(pdev, atmel_psif0_resource, ARRAY_SIZE(atmel_psif0_resource))) goto err_add_resources; atmel_psif0_pclk.dev = &pdev->dev; - select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */ - select_peripheral(PA(9), PERIPH_A, 0); /* DATA */ + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); break; case 1: + pin_mask = (1 << 11) | (1 << 12); /* CLOCK & DATA */ + if (platform_device_add_resources(pdev, atmel_psif1_resource, ARRAY_SIZE(atmel_psif1_resource))) goto err_add_resources; atmel_psif1_pclk.dev = &pdev->dev; - select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */ - select_peripheral(PB(12), PERIPH_A, 0); /* DATA */ + select_peripheral(PIOB, pin_mask, PERIPH_A, 0); break; default: return NULL; @@ -958,26 +962,30 @@ DEV_CLK(usart, atmel_usart3, pba, 6); static inline void configure_usart0_pins(void) { - select_peripheral(PA(8), PERIPH_B, 0); /* RXD */ - select_peripheral(PA(9), PERIPH_B, 0); /* TXD */ + u32 pin_mask = (1 << 8) | (1 << 9); /* RXD & TXD */ + + select_peripheral(PIOA, pin_mask, PERIPH_B, 0); } static inline void configure_usart1_pins(void) { - select_peripheral(PA(17), PERIPH_A, 0); /* RXD */ - select_peripheral(PA(18), PERIPH_A, 0); /* TXD */ + u32 pin_mask = (1 << 17) | (1 << 18); /* RXD & TXD */ + + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); } static inline void configure_usart2_pins(void) { - select_peripheral(PB(26), PERIPH_B, 0); /* RXD */ - select_peripheral(PB(27), PERIPH_B, 0); /* TXD */ + u32 pin_mask = (1 << 26) | (1 << 27); /* RXD & TXD */ + + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); } static inline void configure_usart3_pins(void) { - select_peripheral(PB(18), PERIPH_B, 0); /* RXD */ - select_peripheral(PB(17), PERIPH_B, 0); /* TXD */ + u32 pin_mask = (1 << 18) | (1 << 17); /* RXD & TXD */ + + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); } static struct platform_device *__initdata at32_usarts[4]; @@ -1057,59 +1065,69 @@ struct platform_device *__init at32_add_device_eth(unsigned int id, struct eth_platform_data *data) { struct platform_device *pdev; + u32 pin_mask; switch (id) { case 0: pdev = &macb0_device; - select_peripheral(PC(3), PERIPH_A, 0); /* TXD0 */ - select_peripheral(PC(4), PERIPH_A, 0); /* TXD1 */ - select_peripheral(PC(7), PERIPH_A, 0); /* TXEN */ - select_peripheral(PC(8), PERIPH_A, 0); /* TXCK */ - select_peripheral(PC(9), PERIPH_A, 0); /* RXD0 */ - select_peripheral(PC(10), PERIPH_A, 0); /* RXD1 */ - select_peripheral(PC(13), PERIPH_A, 0); /* RXER */ - select_peripheral(PC(15), PERIPH_A, 0); /* RXDV */ - select_peripheral(PC(16), PERIPH_A, 0); /* MDC */ - select_peripheral(PC(17), PERIPH_A, 0); /* MDIO */ + pin_mask = (1 << 3); /* TXD0 */ + pin_mask |= (1 << 4); /* TXD1 */ + pin_mask |= (1 << 7); /* TXEN */ + pin_mask |= (1 << 8); /* TXCK */ + pin_mask |= (1 << 9); /* RXD0 */ + pin_mask |= (1 << 10); /* RXD1 */ + pin_mask |= (1 << 13); /* RXER */ + pin_mask |= (1 << 15); /* RXDV */ + pin_mask |= (1 << 16); /* MDC */ + pin_mask |= (1 << 17); /* MDIO */ if (!data->is_rmii) { - select_peripheral(PC(0), PERIPH_A, 0); /* COL */ - select_peripheral(PC(1), PERIPH_A, 0); /* CRS */ - select_peripheral(PC(2), PERIPH_A, 0); /* TXER */ - select_peripheral(PC(5), PERIPH_A, 0); /* TXD2 */ - select_peripheral(PC(6), PERIPH_A, 0); /* TXD3 */ - select_peripheral(PC(11), PERIPH_A, 0); /* RXD2 */ - select_peripheral(PC(12), PERIPH_A, 0); /* RXD3 */ - select_peripheral(PC(14), PERIPH_A, 0); /* RXCK */ - select_peripheral(PC(18), PERIPH_A, 0); /* SPD */ + pin_mask |= (1 << 0); /* COL */ + pin_mask |= (1 << 1); /* CRS */ + pin_mask |= (1 << 2); /* TXER */ + pin_mask |= (1 << 5); /* TXD2 */ + pin_mask |= (1 << 6); /* TXD3 */ + pin_mask |= (1 << 11); /* RXD2 */ + pin_mask |= (1 << 12); /* RXD3 */ + pin_mask |= (1 << 14); /* RXCK */ + pin_mask |= (1 << 18); /* SPD */ } + + select_peripheral(PIOC, pin_mask, PERIPH_A, 0); + break; case 1: pdev = &macb1_device; - select_peripheral(PD(13), PERIPH_B, 0); /* TXD0 */ - select_peripheral(PD(14), PERIPH_B, 0); /* TXD1 */ - select_peripheral(PD(11), PERIPH_B, 0); /* TXEN */ - select_peripheral(PD(12), PERIPH_B, 0); /* TXCK */ - select_peripheral(PD(10), PERIPH_B, 0); /* RXD0 */ - select_peripheral(PD(6), PERIPH_B, 0); /* RXD1 */ - select_peripheral(PD(5), PERIPH_B, 0); /* RXER */ - select_peripheral(PD(4), PERIPH_B, 0); /* RXDV */ - select_peripheral(PD(3), PERIPH_B, 0); /* MDC */ - select_peripheral(PD(2), PERIPH_B, 0); /* MDIO */ + pin_mask = (1 << 13); /* TXD0 */ + pin_mask |= (1 << 14); /* TXD1 */ + pin_mask |= (1 << 11); /* TXEN */ + pin_mask |= (1 << 12); /* TXCK */ + pin_mask |= (1 << 10); /* RXD0 */ + pin_mask |= (1 << 6); /* RXD1 */ + pin_mask |= (1 << 5); /* RXER */ + pin_mask |= (1 << 4); /* RXDV */ + pin_mask |= (1 << 3); /* MDC */ + pin_mask |= (1 << 2); /* MDIO */ + + if (!data->is_rmii) + pin_mask |= (1 << 15); /* SPD */ + + select_peripheral(PIOD, pin_mask, PERIPH_B, 0); if (!data->is_rmii) { - select_peripheral(PC(19), PERIPH_B, 0); /* COL */ - select_peripheral(PC(23), PERIPH_B, 0); /* CRS */ - select_peripheral(PC(26), PERIPH_B, 0); /* TXER */ - select_peripheral(PC(27), PERIPH_B, 0); /* TXD2 */ - select_peripheral(PC(28), PERIPH_B, 0); /* TXD3 */ - select_peripheral(PC(29), PERIPH_B, 0); /* RXD2 */ - select_peripheral(PC(30), PERIPH_B, 0); /* RXD3 */ - select_peripheral(PC(24), PERIPH_B, 0); /* RXCK */ - select_peripheral(PD(15), PERIPH_B, 0); /* SPD */ + pin_mask = (1 << 19); /* COL */ + pin_mask |= (1 << 23); /* CRS */ + pin_mask |= (1 << 26); /* TXER */ + pin_mask |= (1 << 27); /* TXD2 */ + pin_mask |= (1 << 28); /* TXD3 */ + pin_mask |= (1 << 29); /* RXD2 */ + pin_mask |= (1 << 30); /* RXD3 */ + pin_mask |= (1 << 24); /* RXCK */ + + select_peripheral(PIOC, pin_mask, PERIPH_B, 0); } break; @@ -1177,23 +1195,28 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) { GPIO_PIN_PB(2), GPIO_PIN_PB(3), GPIO_PIN_PB(4), GPIO_PIN_PA(27), }; struct platform_device *pdev; + u32 pin_mask; switch (id) { case 0: pdev = &atmel_spi0_device; + pin_mask = (1 << 1) | (1 << 2); /* MOSI & SCK */ + /* pullup MISO so a level is always defined */ - select_peripheral(PA(0), PERIPH_A, AT32_GPIOF_PULLUP); - select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */ - select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ + select_peripheral(PIOA, (1 << 0), PERIPH_A, AT32_GPIOF_PULLUP); + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); + at32_spi_setup_slaves(0, b, n, spi0_pins); break; case 1: pdev = &atmel_spi1_device; + pin_mask = (1 << 1) | (1 << 5); /* MOSI */ + /* pullup MISO so a level is always defined */ - select_peripheral(PB(0), PERIPH_B, AT32_GPIOF_PULLUP); - select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */ - select_peripheral(PB(5), PERIPH_B, 0); /* SCK */ + select_peripheral(PIOB, (1 << 0), PERIPH_B, AT32_GPIOF_PULLUP); + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); + at32_spi_setup_slaves(1, b, n, spi1_pins); break; @@ -1226,6 +1249,7 @@ struct platform_device *__init at32_add_device_twi(unsigned int id, unsigned int n) { struct platform_device *pdev; + u32 pin_mask; if (id != 0) return NULL; @@ -1238,8 +1262,9 @@ struct platform_device *__init at32_add_device_twi(unsigned int id, ARRAY_SIZE(atmel_twi0_resource))) goto err_add_resources; - select_peripheral(PA(6), PERIPH_A, 0); /* SDA */ - select_peripheral(PA(7), PERIPH_A, 0); /* SDL */ + pin_mask = (1 << 6) | (1 << 7); /* SDA & SDL */ + + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); atmel_twi0_pclk.dev = &pdev->dev; @@ -1272,10 +1297,16 @@ static struct clk atmel_mci0_pclk = { struct platform_device *__init at32_add_device_mci(unsigned int id, struct mci_platform_data *data) { - struct mci_platform_data _data; struct platform_device *pdev; + struct dw_dma_slave *dws; + u32 pioa_mask; + u32 piob_mask; - if (id != 0) + if (id != 0 || !data) + return NULL; + + /* Must have at least one usable slot */ + if (!data->slot[0].bus_width && !data->slot[1].bus_width) return NULL; pdev = platform_device_alloc("atmel_mci", id); @@ -1286,28 +1317,80 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data) ARRAY_SIZE(atmel_mci0_resource))) goto fail; - if (!data) { - data = &_data; - memset(data, -1, sizeof(struct mci_platform_data)); - data->detect_pin = GPIO_PIN_NONE; - data->wp_pin = GPIO_PIN_NONE; - } + if (data->dma_slave) + dws = kmemdup(to_dw_dma_slave(data->dma_slave), + sizeof(struct dw_dma_slave), GFP_KERNEL); + else + dws = kzalloc(sizeof(struct dw_dma_slave), GFP_KERNEL); + + dws->slave.dev = &pdev->dev; + dws->slave.dma_dev = &dw_dmac0_device.dev; + dws->slave.reg_width = DMA_SLAVE_WIDTH_32BIT; + dws->cfg_hi = (DWC_CFGH_SRC_PER(0) + | DWC_CFGH_DST_PER(1)); + dws->cfg_lo &= ~(DWC_CFGL_HS_DST_POL + | DWC_CFGL_HS_SRC_POL); + + data->dma_slave = &dws->slave; if (platform_device_add_data(pdev, data, sizeof(struct mci_platform_data))) goto fail; - select_peripheral(PA(10), PERIPH_A, 0); /* CLK */ - select_peripheral(PA(11), PERIPH_A, 0); /* CMD */ - select_peripheral(PA(12), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PA(13), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */ - select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */ + /* CLK line is common to both slots */ + pioa_mask = 1 << 10; - if (gpio_is_valid(data->detect_pin)) - at32_select_gpio(data->detect_pin, 0); - if (gpio_is_valid(data->wp_pin)) - at32_select_gpio(data->wp_pin, 0); + switch (data->slot[0].bus_width) { + case 4: + pioa_mask |= 1 << 13; /* DATA1 */ + pioa_mask |= 1 << 14; /* DATA2 */ + pioa_mask |= 1 << 15; /* DATA3 */ + /* fall through */ + case 1: + pioa_mask |= 1 << 11; /* CMD */ + pioa_mask |= 1 << 12; /* DATA0 */ + + if (gpio_is_valid(data->slot[0].detect_pin)) + at32_select_gpio(data->slot[0].detect_pin, 0); + if (gpio_is_valid(data->slot[0].wp_pin)) + at32_select_gpio(data->slot[0].wp_pin, 0); + break; + case 0: + /* Slot is unused */ + break; + default: + goto fail; + } + + select_peripheral(PIOA, pioa_mask, PERIPH_A, 0); + piob_mask = 0; + + switch (data->slot[1].bus_width) { + case 4: + piob_mask |= 1 << 8; /* DATA1 */ + piob_mask |= 1 << 9; /* DATA2 */ + piob_mask |= 1 << 10; /* DATA3 */ + /* fall through */ + case 1: + piob_mask |= 1 << 6; /* CMD */ + piob_mask |= 1 << 7; /* DATA0 */ + select_peripheral(PIOB, piob_mask, PERIPH_B, 0); + + if (gpio_is_valid(data->slot[1].detect_pin)) + at32_select_gpio(data->slot[1].detect_pin, 0); + if (gpio_is_valid(data->slot[1].wp_pin)) + at32_select_gpio(data->slot[1].wp_pin, 0); + break; + case 0: + /* Slot is unused */ + break; + default: + if (!data->slot[0].bus_width) + goto fail; + + data->slot[1].bus_width = 0; + break; + } atmel_mci0_pclk.dev = &pdev->dev; @@ -1353,13 +1436,14 @@ static struct clk atmel_lcdfb0_pixclk = { struct platform_device *__init at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, unsigned long fbmem_start, unsigned long fbmem_len, - unsigned int pin_config) + u64 pin_mask) { struct platform_device *pdev; struct atmel_lcdfb_info *info; struct fb_monspecs *monspecs; struct fb_videomode *modedb; unsigned int modedb_size; + u32 portc_mask, portd_mask, porte_mask; /* * Do a deep copy of the fb data, monspecs and modedb. Make @@ -1381,76 +1465,21 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, case 0: pdev = &atmel_lcdfb0_device; - switch (pin_config) { - case 0: - select_peripheral(PC(19), PERIPH_A, 0); /* CC */ - select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ - select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ - select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ - select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */ - select_peripheral(PC(24), PERIPH_A, 0); /* MODE */ - select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ - select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */ - select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */ - select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */ - select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ - select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ - select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ - select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */ - select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */ - select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */ - select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */ - select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */ - select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ - select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ - select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ - select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */ - select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */ - select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */ - select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */ - select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */ - select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */ - select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ - select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ - break; - case 1: - select_peripheral(PE(0), PERIPH_B, 0); /* CC */ - select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */ - select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */ - select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */ - select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */ - select_peripheral(PE(2), PERIPH_B, 0); /* MODE */ - select_peripheral(PC(25), PERIPH_A, 0); /* PWR */ - select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */ - select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */ - select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */ - select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */ - select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */ - select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */ - select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */ - select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */ - select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */ - select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */ - select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */ - select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */ - select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */ - select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */ - select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */ - select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */ - select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */ - select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */ - select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */ - select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */ - select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */ - select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */ - select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */ - select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */ - break; - default: - goto err_invalid_id; - } + if (pin_mask == 0ULL) + /* Default to "full" lcdc control signals and 24bit */ + pin_mask = ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL; + + /* LCDC on port C */ + portc_mask = (pin_mask & 0xfff80000) >> 19; + select_peripheral(PIOC, portc_mask, PERIPH_A, 0); + + /* LCDC on port D */ + portd_mask = pin_mask & 0x0003ffff; + select_peripheral(PIOD, portd_mask, PERIPH_A, 0); + + /* LCDC on port E */ + porte_mask = (pin_mask >> 32) & 0x0007ffff; + select_peripheral(PIOE, porte_mask, PERIPH_B, 0); clk_set_parent(&atmel_lcdfb0_pixclk, &pll0); clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0)); @@ -1499,6 +1528,7 @@ static struct clk atmel_pwm0_mck = { struct platform_device *__init at32_add_device_pwm(u32 mask) { struct platform_device *pdev; + u32 pin_mask; if (!mask) return NULL; @@ -1514,14 +1544,21 @@ struct platform_device *__init at32_add_device_pwm(u32 mask) if (platform_device_add_data(pdev, &mask, sizeof(mask))) goto out_free_pdev; + pin_mask = 0; if (mask & (1 << 0)) - select_peripheral(PA(28), PERIPH_A, 0); + pin_mask |= (1 << 28); if (mask & (1 << 1)) - select_peripheral(PA(29), PERIPH_A, 0); + pin_mask |= (1 << 29); + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); + + pin_mask = 0; if (mask & (1 << 2)) - select_peripheral(PA(21), PERIPH_B, 0); + pin_mask |= (1 << 21); if (mask & (1 << 3)) - select_peripheral(PA(22), PERIPH_B, 0); + pin_mask |= (1 << 22); + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_B, 0); atmel_pwm0_mck.dev = &pdev->dev; @@ -1562,52 +1599,65 @@ struct platform_device *__init at32_add_device_ssc(unsigned int id, unsigned int flags) { struct platform_device *pdev; + u32 pin_mask = 0; switch (id) { case 0: pdev = &ssc0_device; if (flags & ATMEL_SSC_RF) - select_peripheral(PA(21), PERIPH_A, 0); /* RF */ + pin_mask |= (1 << 21); /* RF */ if (flags & ATMEL_SSC_RK) - select_peripheral(PA(22), PERIPH_A, 0); /* RK */ + pin_mask |= (1 << 22); /* RK */ if (flags & ATMEL_SSC_TK) - select_peripheral(PA(23), PERIPH_A, 0); /* TK */ + pin_mask |= (1 << 23); /* TK */ if (flags & ATMEL_SSC_TF) - select_peripheral(PA(24), PERIPH_A, 0); /* TF */ + pin_mask |= (1 << 24); /* TF */ if (flags & ATMEL_SSC_TD) - select_peripheral(PA(25), PERIPH_A, 0); /* TD */ + pin_mask |= (1 << 25); /* TD */ if (flags & ATMEL_SSC_RD) - select_peripheral(PA(26), PERIPH_A, 0); /* RD */ + pin_mask |= (1 << 26); /* RD */ + + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_A, 0); + break; case 1: pdev = &ssc1_device; if (flags & ATMEL_SSC_RF) - select_peripheral(PA(0), PERIPH_B, 0); /* RF */ + pin_mask |= (1 << 0); /* RF */ if (flags & ATMEL_SSC_RK) - select_peripheral(PA(1), PERIPH_B, 0); /* RK */ + pin_mask |= (1 << 1); /* RK */ if (flags & ATMEL_SSC_TK) - select_peripheral(PA(2), PERIPH_B, 0); /* TK */ + pin_mask |= (1 << 2); /* TK */ if (flags & ATMEL_SSC_TF) - select_peripheral(PA(3), PERIPH_B, 0); /* TF */ + pin_mask |= (1 << 3); /* TF */ if (flags & ATMEL_SSC_TD) - select_peripheral(PA(4), PERIPH_B, 0); /* TD */ + pin_mask |= (1 << 4); /* TD */ if (flags & ATMEL_SSC_RD) - select_peripheral(PA(5), PERIPH_B, 0); /* RD */ + pin_mask |= (1 << 5); /* RD */ + + if (pin_mask > 0) + select_peripheral(PIOA, pin_mask, PERIPH_B, 0); + break; case 2: pdev = &ssc2_device; if (flags & ATMEL_SSC_TD) - select_peripheral(PB(13), PERIPH_A, 0); /* TD */ + pin_mask |= (1 << 13); /* TD */ if (flags & ATMEL_SSC_RD) - select_peripheral(PB(14), PERIPH_A, 0); /* RD */ + pin_mask |= (1 << 14); /* RD */ if (flags & ATMEL_SSC_TK) - select_peripheral(PB(15), PERIPH_A, 0); /* TK */ + pin_mask |= (1 << 15); /* TK */ if (flags & ATMEL_SSC_TF) - select_peripheral(PB(16), PERIPH_A, 0); /* TF */ + pin_mask |= (1 << 16); /* TF */ if (flags & ATMEL_SSC_RF) - select_peripheral(PB(17), PERIPH_A, 0); /* RF */ + pin_mask |= (1 << 17); /* RF */ if (flags & ATMEL_SSC_RK) - select_peripheral(PB(18), PERIPH_A, 0); /* RK */ + pin_mask |= (1 << 18); /* RK */ + + if (pin_mask > 0) + select_peripheral(PIOB, pin_mask, PERIPH_A, 0); + break; default: return NULL; @@ -1745,14 +1795,15 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, unsigned int cs, unsigned int extint) { static unsigned int extint_pin_map[4] __initdata = { - GPIO_PIN_PB(25), - GPIO_PIN_PB(26), - GPIO_PIN_PB(27), - GPIO_PIN_PB(28), + (1 << 25), + (1 << 26), + (1 << 27), + (1 << 28), }; static bool common_pins_initialized __initdata = false; unsigned int extint_pin; int ret; + u32 pin_mask; if (extint >= ARRAY_SIZE(extint_pin_map)) return -EINVAL; @@ -1766,7 +1817,8 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, if (ret) return ret; - select_peripheral(PE(21), PERIPH_A, 0); /* NCS4 -> OE_N */ + /* NCS4 -> OE_N */ + select_peripheral(PIOE, (1 << 21), PERIPH_A, 0); hmatrix_sfr_set_bits(HMATRIX_SLAVE_EBI, HMATRIX_EBI_CF0_ENABLE); break; case 5: @@ -1776,7 +1828,8 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, if (ret) return ret; - select_peripheral(PE(22), PERIPH_A, 0); /* NCS5 -> OE_N */ + /* NCS5 -> OE_N */ + select_peripheral(PIOE, (1 << 22), PERIPH_A, 0); hmatrix_sfr_set_bits(HMATRIX_SLAVE_EBI, HMATRIX_EBI_CF1_ENABLE); break; default: @@ -1784,14 +1837,17 @@ static int __init at32_init_ide_or_cf(struct platform_device *pdev, } if (!common_pins_initialized) { - select_peripheral(PE(19), PERIPH_A, 0); /* CFCE1 -> CS0_N */ - select_peripheral(PE(20), PERIPH_A, 0); /* CFCE2 -> CS1_N */ - select_peripheral(PE(23), PERIPH_A, 0); /* CFRNW -> DIR */ - select_peripheral(PE(24), PERIPH_A, 0); /* NWAIT <- IORDY */ + pin_mask = (1 << 19); /* CFCE1 -> CS0_N */ + pin_mask |= (1 << 20); /* CFCE2 -> CS1_N */ + pin_mask |= (1 << 23); /* CFRNW -> DIR */ + pin_mask |= (1 << 24); /* NWAIT <- IORDY */ + + select_peripheral(PIOE, pin_mask, PERIPH_A, 0); + common_pins_initialized = true; } - at32_select_periph(extint_pin, GPIO_PERIPH_A, AT32_GPIOF_DEGLITCH); + select_peripheral(PIOB, extint_pin, PERIPH_A, AT32_GPIOF_DEGLITCH); pdev->resource[1].start = EIM_IRQ_BASE + extint; pdev->resource[1].end = pdev->resource[1].start; @@ -1930,6 +1986,7 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data) { struct platform_device *pdev; struct ac97c_platform_data _data; + u32 pin_mask; if (id != 0) return NULL; @@ -1956,10 +2013,10 @@ at32_add_device_ac97c(unsigned int id, struct ac97c_platform_data *data) sizeof(struct ac97c_platform_data))) goto fail; - select_peripheral(PB(20), PERIPH_B, 0); /* SDO */ - select_peripheral(PB(21), PERIPH_B, 0); /* SYNC */ - select_peripheral(PB(22), PERIPH_B, 0); /* SCLK */ - select_peripheral(PB(23), PERIPH_B, 0); /* SDI */ + pin_mask = (1 << 20) | (1 << 21); /* SDO & SYNC */ + pin_mask |= (1 << 22) | (1 << 23); /* SCLK & SDI */ + + select_peripheral(PIOB, pin_mask, PERIPH_B, 0); /* TODO: gpio_is_valid(data->reset_pin) with kernel 2.6.26. */ if (data->reset_pin != GPIO_PIN_NONE) @@ -2001,6 +2058,7 @@ static struct clk abdac0_sample_clk = { struct platform_device *__init at32_add_device_abdac(unsigned int id) { struct platform_device *pdev; + u32 pin_mask; if (id != 0) return NULL; @@ -2013,10 +2071,10 @@ struct platform_device *__init at32_add_device_abdac(unsigned int id) ARRAY_SIZE(abdac0_resource))) goto err_add_resources; - select_peripheral(PB(20), PERIPH_A, 0); /* DATA1 */ - select_peripheral(PB(21), PERIPH_A, 0); /* DATA0 */ - select_peripheral(PB(22), PERIPH_A, 0); /* DATAN1 */ - select_peripheral(PB(23), PERIPH_A, 0); /* DATAN0 */ + pin_mask = (1 << 20) | (1 << 22); /* DATA1 & DATAN1 */ + pin_mask |= (1 << 21) | (1 << 23); /* DATA0 & DATAN0 */ + + select_peripheral(PIOB, pin_mask, PERIPH_A, 0); abdac0_pclk.dev = &pdev->dev; abdac0_sample_clk.dev = &pdev->dev; @@ -2073,7 +2131,7 @@ static struct clk gclk4 = { .index = 4, }; -struct clk *at32_clock_list[] = { +static __initdata struct clk *init_clocks[] = { &osc32k, &osc0, &osc1, @@ -2137,7 +2195,6 @@ struct clk *at32_clock_list[] = { &gclk3, &gclk4, }; -unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); void __init setup_platform(void) { @@ -2168,14 +2225,19 @@ void __init setup_platform(void) genclk_init_parent(&abdac0_sample_clk); /* - * Turn on all clocks that have at least one user already, and - * turn off everything else. We only do this for module - * clocks, and even though it isn't particularly pretty to - * check the address of the mode function, it should do the - * trick... + * Build initial dynamic clock list by registering all clocks + * from the array. + * At the same time, turn on all clocks that have at least one + * user already, and turn off everything else. We only do this + * for module clocks, and even though it isn't particularly + * pretty to check the address of the mode function, it should + * do the trick... */ - for (i = 0; i < ARRAY_SIZE(at32_clock_list); i++) { - struct clk *clk = at32_clock_list[i]; + for (i = 0; i < ARRAY_SIZE(init_clocks); i++) { + struct clk *clk = init_clocks[i]; + + /* first, register clock */ + at32_clk_register(clk); if (clk->users == 0) continue; diff --git a/arch/avr32/mach-at32ap/clock.c b/arch/avr32/mach-at32ap/clock.c index 6c27ddac5ad..138a00a2a2d 100644 --- a/arch/avr32/mach-at32ap/clock.c +++ b/arch/avr32/mach-at32ap/clock.c @@ -15,24 +15,40 @@ #include <linux/err.h> #include <linux/device.h> #include <linux/string.h> +#include <linux/list.h> #include <mach/chip.h> #include "clock.h" +/* at32 clock list */ +static LIST_HEAD(at32_clock_list); + static DEFINE_SPINLOCK(clk_lock); +static DEFINE_SPINLOCK(clk_list_lock); + +void at32_clk_register(struct clk *clk) +{ + spin_lock(&clk_list_lock); + /* add the new item to the end of the list */ + list_add_tail(&clk->list, &at32_clock_list); + spin_unlock(&clk_list_lock); +} struct clk *clk_get(struct device *dev, const char *id) { - int i; + struct clk *clk; - for (i = 0; i < at32_nr_clocks; i++) { - struct clk *clk = at32_clock_list[i]; + spin_lock(&clk_list_lock); - if (clk->dev == dev && strcmp(id, clk->name) == 0) + list_for_each_entry(clk, &at32_clock_list, list) { + if (clk->dev == dev && strcmp(id, clk->name) == 0) { + spin_unlock(&clk_list_lock); return clk; + } } + spin_unlock(&clk_list_lock); return ERR_PTR(-ENOENT); } EXPORT_SYMBOL(clk_get); @@ -203,8 +219,8 @@ dump_clock(struct clk *parent, struct clkinf *r) /* cost of this scan is small, but not linear... */ r->nest = nest + NEST_DELTA; - for (i = 3; i < at32_nr_clocks; i++) { - clk = at32_clock_list[i]; + + list_for_each_entry(clk, &at32_clock_list, list) { if (clk->parent == parent) dump_clock(clk, r); } @@ -215,6 +231,7 @@ static int clk_show(struct seq_file *s, void *unused) { struct clkinf r; int i; + struct clk *clk; /* show all the power manager registers */ seq_printf(s, "MCCTRL = %8x\n", pm_readl(MCCTRL)); @@ -234,14 +251,25 @@ static int clk_show(struct seq_file *s, void *unused) seq_printf(s, "\n"); - /* show clock tree as derived from the three oscillators - * we "know" are at the head of the list - */ r.s = s; r.nest = 0; - dump_clock(at32_clock_list[0], &r); - dump_clock(at32_clock_list[1], &r); - dump_clock(at32_clock_list[2], &r); + /* protected from changes on the list while dumping */ + spin_lock(&clk_list_lock); + + /* show clock tree as derived from the three oscillators */ + clk = clk_get(NULL, "osc32k"); + dump_clock(clk, &r); + clk_put(clk); + + clk = clk_get(NULL, "osc0"); + dump_clock(clk, &r); + clk_put(clk); + + clk = clk_get(NULL, "osc1"); + dump_clock(clk, &r); + clk_put(clk); + + spin_unlock(&clk_list_lock); return 0; } diff --git a/arch/avr32/mach-at32ap/clock.h b/arch/avr32/mach-at32ap/clock.h index bb8e1f29583..623bf0e9a1e 100644 --- a/arch/avr32/mach-at32ap/clock.h +++ b/arch/avr32/mach-at32ap/clock.h @@ -12,8 +12,13 @@ * published by the Free Software Foundation. */ #include <linux/clk.h> +#include <linux/list.h> + + +void at32_clk_register(struct clk *clk); struct clk { + struct list_head list; /* linking element */ const char *name; /* Clock name/function */ struct device *dev; /* Device the clock is used by */ struct clk *parent; /* Parent clock, if any */ @@ -25,6 +30,3 @@ struct clk { u16 users; /* Enabled if non-zero */ u16 index; /* Sibling index */ }; - -extern struct clk *at32_clock_list[]; -extern unsigned int at32_nr_clocks; diff --git a/arch/avr32/mach-at32ap/include/mach/at32ap700x.h b/arch/avr32/mach-at32ap/include/mach/at32ap700x.h index 1e9852d65cc..a77d372f6f3 100644 --- a/arch/avr32/mach-at32ap/include/mach/at32ap700x.h +++ b/arch/avr32/mach-at32ap/include/mach/at32ap700x.h @@ -83,4 +83,132 @@ #define HMATRIX_BASE 0xfff00800 #define SDRAMC_BASE 0xfff03800 +/* LCDC on port C */ +#define ATMEL_LCDC_PC_CC (1ULL << 19) +#define ATMEL_LCDC_PC_HSYNC (1ULL << 20) +#define ATMEL_LCDC_PC_PCLK (1ULL << 21) +#define ATMEL_LCDC_PC_VSYNC (1ULL << 22) +#define ATMEL_LCDC_PC_DVAL (1ULL << 23) +#define ATMEL_LCDC_PC_MODE (1ULL << 24) +#define ATMEL_LCDC_PC_PWR (1ULL << 25) +#define ATMEL_LCDC_PC_DATA0 (1ULL << 26) +#define ATMEL_LCDC_PC_DATA1 (1ULL << 27) +#define ATMEL_LCDC_PC_DATA2 (1ULL << 28) +#define ATMEL_LCDC_PC_DATA3 (1ULL << 29) +#define ATMEL_LCDC_PC_DATA4 (1ULL << 30) +#define ATMEL_LCDC_PC_DATA5 (1ULL << 31) + +/* LCDC on port D */ +#define ATMEL_LCDC_PD_DATA6 (1ULL << 0) +#define ATMEL_LCDC_PD_DATA7 (1ULL << 1) +#define ATMEL_LCDC_PD_DATA8 (1ULL << 2) +#define ATMEL_LCDC_PD_DATA9 (1ULL << 3) +#define ATMEL_LCDC_PD_DATA10 (1ULL << 4) +#define ATMEL_LCDC_PD_DATA11 (1ULL << 5) +#define ATMEL_LCDC_PD_DATA12 (1ULL << 6) +#define ATMEL_LCDC_PD_DATA13 (1ULL << 7) +#define ATMEL_LCDC_PD_DATA14 (1ULL << 8) +#define ATMEL_LCDC_PD_DATA15 (1ULL << 9) +#define ATMEL_LCDC_PD_DATA16 (1ULL << 10) +#define ATMEL_LCDC_PD_DATA17 (1ULL << 11) +#define ATMEL_LCDC_PD_DATA18 (1ULL << 12) +#define ATMEL_LCDC_PD_DATA19 (1ULL << 13) +#define ATMEL_LCDC_PD_DATA20 (1ULL << 14) +#define ATMEL_LCDC_PD_DATA21 (1ULL << 15) +#define ATMEL_LCDC_PD_DATA22 (1ULL << 16) +#define ATMEL_LCDC_PD_DATA23 (1ULL << 17) + +/* LCDC on port E */ +#define ATMEL_LCDC_PE_CC (1ULL << (32 + 0)) +#define ATMEL_LCDC_PE_DVAL (1ULL << (32 + 1)) +#define ATMEL_LCDC_PE_MODE (1ULL << (32 + 2)) +#define ATMEL_LCDC_PE_DATA0 (1ULL << (32 + 3)) +#define ATMEL_LCDC_PE_DATA1 (1ULL << (32 + 4)) +#define ATMEL_LCDC_PE_DATA2 (1ULL << (32 + 5)) +#define ATMEL_LCDC_PE_DATA3 (1ULL << (32 + 6)) +#define ATMEL_LCDC_PE_DATA4 (1ULL << (32 + 7)) +#define ATMEL_LCDC_PE_DATA8 (1ULL << (32 + 8)) +#define ATMEL_LCDC_PE_DATA9 (1ULL << (32 + 9)) +#define ATMEL_LCDC_PE_DATA10 (1ULL << (32 + 10)) +#define ATMEL_LCDC_PE_DATA11 (1ULL << (32 + 11)) +#define ATMEL_LCDC_PE_DATA12 (1ULL << (32 + 12)) +#define ATMEL_LCDC_PE_DATA16 (1ULL << (32 + 13)) +#define ATMEL_LCDC_PE_DATA17 (1ULL << (32 + 14)) +#define ATMEL_LCDC_PE_DATA18 (1ULL << (32 + 15)) +#define ATMEL_LCDC_PE_DATA19 (1ULL << (32 + 16)) +#define ATMEL_LCDC_PE_DATA20 (1ULL << (32 + 17)) +#define ATMEL_LCDC_PE_DATA21 (1ULL << (32 + 18)) + + +#define ATMEL_LCDC(PORT, PIN) (ATMEL_LCDC_##PORT##_##PIN) + + +#define ATMEL_LCDC_PRI_24B_DATA ( \ + ATMEL_LCDC(PC, DATA0) | ATMEL_LCDC(PC, DATA1) | \ + ATMEL_LCDC(PC, DATA2) | ATMEL_LCDC(PC, DATA3) | \ + ATMEL_LCDC(PC, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PD, DATA6) | ATMEL_LCDC(PD, DATA7) | \ + ATMEL_LCDC(PD, DATA8) | ATMEL_LCDC(PD, DATA9) | \ + ATMEL_LCDC(PD, DATA10) | ATMEL_LCDC(PD, DATA11) | \ + ATMEL_LCDC(PD, DATA12) | ATMEL_LCDC(PD, DATA13) | \ + ATMEL_LCDC(PD, DATA14) | ATMEL_LCDC(PD, DATA15) | \ + ATMEL_LCDC(PD, DATA16) | ATMEL_LCDC(PD, DATA17) | \ + ATMEL_LCDC(PD, DATA18) | ATMEL_LCDC(PD, DATA19) | \ + ATMEL_LCDC(PD, DATA20) | ATMEL_LCDC(PD, DATA21) | \ + ATMEL_LCDC(PD, DATA22) | ATMEL_LCDC(PD, DATA23)) + +#define ATMEL_LCDC_ALT_24B_DATA ( \ + ATMEL_LCDC(PE, DATA0) | ATMEL_LCDC(PE, DATA1) | \ + ATMEL_LCDC(PE, DATA2) | ATMEL_LCDC(PE, DATA3) | \ + ATMEL_LCDC(PE, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PD, DATA6) | ATMEL_LCDC(PD, DATA7) | \ + ATMEL_LCDC(PE, DATA8) | ATMEL_LCDC(PE, DATA9) | \ + ATMEL_LCDC(PE, DATA10) | ATMEL_LCDC(PE, DATA11) | \ + ATMEL_LCDC(PE, DATA12) | ATMEL_LCDC(PD, DATA13) | \ + ATMEL_LCDC(PD, DATA14) | ATMEL_LCDC(PD, DATA15) | \ + ATMEL_LCDC(PE, DATA16) | ATMEL_LCDC(PE, DATA17) | \ + ATMEL_LCDC(PE, DATA18) | ATMEL_LCDC(PE, DATA19) | \ + ATMEL_LCDC(PE, DATA20) | ATMEL_LCDC(PE, DATA21) | \ + ATMEL_LCDC(PD, DATA22) | ATMEL_LCDC(PD, DATA23)) + +#define ATMEL_LCDC_PRI_15B_DATA ( \ + ATMEL_LCDC(PC, DATA0) | ATMEL_LCDC(PC, DATA1) | \ + ATMEL_LCDC(PC, DATA2) | ATMEL_LCDC(PC, DATA3) | \ + ATMEL_LCDC(PC, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PD, DATA8) | ATMEL_LCDC(PD, DATA9) | \ + ATMEL_LCDC(PD, DATA10) | ATMEL_LCDC(PD, DATA11) | \ + ATMEL_LCDC(PD, DATA12) | ATMEL_LCDC(PD, DATA16) | \ + ATMEL_LCDC(PD, DATA17) | ATMEL_LCDC(PD, DATA18) | \ + ATMEL_LCDC(PD, DATA19) | ATMEL_LCDC(PD, DATA20)) + +#define ATMEL_LCDC_ALT_15B_DATA ( \ + ATMEL_LCDC(PE, DATA0) | ATMEL_LCDC(PE, DATA1) | \ + ATMEL_LCDC(PE, DATA2) | ATMEL_LCDC(PE, DATA3) | \ + ATMEL_LCDC(PE, DATA4) | ATMEL_LCDC(PC, DATA5) | \ + ATMEL_LCDC(PE, DATA8) | ATMEL_LCDC(PE, DATA9) | \ + ATMEL_LCDC(PE, DATA10) | ATMEL_LCDC(PE, DATA11) | \ + ATMEL_LCDC(PE, DATA12) | ATMEL_LCDC(PE, DATA16) | \ + ATMEL_LCDC(PE, DATA17) | ATMEL_LCDC(PE, DATA18) | \ + ATMEL_LCDC(PE, DATA19) | ATMEL_LCDC(PE, DATA20)) + +#define ATMEL_LCDC_PRI_CONTROL ( \ + ATMEL_LCDC(PC, CC) | ATMEL_LCDC(PC, DVAL) | \ + ATMEL_LCDC(PC, MODE) | ATMEL_LCDC(PC, PWR)) + +#define ATMEL_LCDC_ALT_CONTROL ( \ + ATMEL_LCDC(PE, CC) | ATMEL_LCDC(PE, DVAL) | \ + ATMEL_LCDC(PE, MODE) | ATMEL_LCDC(PC, PWR)) + +#define ATMEL_LCDC_CONTROL ( \ + ATMEL_LCDC(PC, HSYNC) | ATMEL_LCDC(PC, VSYNC) | \ + ATMEL_LCDC(PC, PCLK)) + +#define ATMEL_LCDC_PRI_24BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_PRI_24B_DATA) + +#define ATMEL_LCDC_ALT_24BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_ALT_24B_DATA) + +#define ATMEL_LCDC_PRI_15BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_PRI_15B_DATA) + +#define ATMEL_LCDC_ALT_15BIT (ATMEL_LCDC_CONTROL | ATMEL_LCDC_ALT_15B_DATA) + #endif /* __ASM_ARCH_AT32AP700X_H__ */ diff --git a/arch/avr32/mach-at32ap/include/mach/board.h b/arch/avr32/mach-at32ap/include/mach/board.h index e60e9076544..c48386d66bc 100644 --- a/arch/avr32/mach-at32ap/include/mach/board.h +++ b/arch/avr32/mach-at32ap/include/mach/board.h @@ -43,7 +43,7 @@ struct atmel_lcdfb_info; struct platform_device * at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data, unsigned long fbmem_start, unsigned long fbmem_len, - unsigned int pin_config); + u64 pin_mask); struct usba_platform_data; struct platform_device * diff --git a/arch/avr32/mach-at32ap/include/mach/io.h b/arch/avr32/mach-at32ap/include/mach/io.h index 4ec6abc68ea..22ea79b7405 100644 --- a/arch/avr32/mach-at32ap/include/mach/io.h +++ b/arch/avr32/mach-at32ap/include/mach/io.h @@ -1,8 +1,7 @@ #ifndef __ASM_AVR32_ARCH_AT32AP_IO_H #define __ASM_AVR32_ARCH_AT32AP_IO_H -/* For "bizarre" halfword swapping */ -#include <linux/byteorder/swabb.h> +#include <linux/swab.h> #if defined(CONFIG_AP700X_32_BIT_SMC) # define __swizzle_addr_b(addr) (addr ^ 3UL) diff --git a/arch/avr32/mach-at32ap/include/mach/portmux.h b/arch/avr32/mach-at32ap/include/mach/portmux.h index b1abe6b4e4e..21c79373b53 100644 --- a/arch/avr32/mach-at32ap/include/mach/portmux.h +++ b/arch/avr32/mach-at32ap/include/mach/portmux.h @@ -21,9 +21,10 @@ #define AT32_GPIOF_DEGLITCH 0x00000008 /* (IN) Filter glitches */ #define AT32_GPIOF_MULTIDRV 0x00000010 /* Enable multidriver option */ -void at32_select_periph(unsigned int pin, unsigned int periph, - unsigned long flags); +void at32_select_periph(unsigned int port, unsigned int pin, + unsigned int periph, unsigned long flags); void at32_select_gpio(unsigned int pin, unsigned long flags); +void at32_deselect_pin(unsigned int pin); void at32_reserve_pin(unsigned int pin); #endif /* __ASM_ARCH_PORTMUX_H__ */ diff --git a/arch/avr32/mach-at32ap/pdc.c b/arch/avr32/mach-at32ap/pdc.c index 1040bda4fda..61ab15aae97 100644 --- a/arch/avr32/mach-at32ap/pdc.c +++ b/arch/avr32/mach-at32ap/pdc.c @@ -35,7 +35,6 @@ static int __init pdc_probe(struct platform_device *pdev) } static struct platform_driver pdc_driver = { - .probe = pdc_probe, .driver = { .name = "pdc", }, @@ -43,6 +42,6 @@ static struct platform_driver pdc_driver = { static int __init pdc_init(void) { - return platform_driver_register(&pdc_driver); + return platform_driver_probe(&pdc_driver, pdc_probe); } arch_initcall(pdc_init); diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c index 405ee6bad4c..ed81a8bcb22 100644 --- a/arch/avr32/mach-at32ap/pio.c +++ b/arch/avr32/mach-at32ap/pio.c @@ -50,35 +50,48 @@ static struct pio_device *gpio_to_pio(unsigned int gpio) } /* Pin multiplexing API */ +static DEFINE_SPINLOCK(pio_lock); -void __init at32_select_periph(unsigned int pin, unsigned int periph, - unsigned long flags) +void __init at32_select_periph(unsigned int port, u32 pin_mask, + unsigned int periph, unsigned long flags) { struct pio_device *pio; - unsigned int pin_index = pin & 0x1f; - u32 mask = 1 << pin_index; - pio = gpio_to_pio(pin); + /* assign and verify pio */ + pio = gpio_to_pio(port); if (unlikely(!pio)) { - printk("pio: invalid pin %u\n", pin); + printk(KERN_WARNING "pio: invalid port %u\n", port); goto fail; } - if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask) - || gpiochip_is_requested(&pio->chip, pin_index))) { - printk("%s: pin %u is busy\n", pio->name, pin_index); + /* Test if any of the requested pins is already muxed */ + spin_lock(&pio_lock); + if (unlikely(pio->pinmux_mask & pin_mask)) { + printk(KERN_WARNING "%s: pin(s) busy (requested 0x%x, busy 0x%x)\n", + pio->name, pin_mask, pio->pinmux_mask & pin_mask); + spin_unlock(&pio_lock); goto fail; } - pio_writel(pio, PUER, mask); + pio->pinmux_mask |= pin_mask; + + /* enable pull ups */ + pio_writel(pio, PUER, pin_mask); + + /* select either peripheral A or B */ if (periph) - pio_writel(pio, BSR, mask); + pio_writel(pio, BSR, pin_mask); else - pio_writel(pio, ASR, mask); + pio_writel(pio, ASR, pin_mask); + + /* enable peripheral control */ + pio_writel(pio, PDR, pin_mask); - pio_writel(pio, PDR, mask); + /* Disable pull ups if not requested. */ if (!(flags & AT32_GPIOF_PULLUP)) - pio_writel(pio, PUDR, mask); + pio_writel(pio, PUDR, pin_mask); + + spin_unlock(&pio_lock); return; @@ -134,6 +147,25 @@ fail: dump_stack(); } +/* + * Undo a previous pin reservation. Will not affect the hardware + * configuration. + */ +void at32_deselect_pin(unsigned int pin) +{ + struct pio_device *pio; + unsigned int pin_index = pin & 0x1f; + + pio = gpio_to_pio(pin); + if (unlikely(!pio)) { + printk("pio: invalid pin %u\n", pin); + dump_stack(); + return; + } + + clear_bit(pin_index, &pio->pinmux_mask); +} + /* Reserve a pin, preventing anyone else from changing its configuration. */ void __init at32_reserve_pin(unsigned int pin) { @@ -382,7 +414,6 @@ static int __init pio_probe(struct platform_device *pdev) } static struct platform_driver pio_driver = { - .probe = pio_probe, .driver = { .name = "pio", }, @@ -390,7 +421,7 @@ static struct platform_driver pio_driver = { static int __init pio_init(void) { - return platform_driver_register(&pio_driver); + return platform_driver_probe(&pio_driver, pio_probe); } postcore_initcall(pio_init); diff --git a/arch/avr32/oprofile/Makefile b/arch/avr32/oprofile/Makefile index 1fe81c3c1e8..e0eb520e028 100644 --- a/arch/avr32/oprofile/Makefile +++ b/arch/avr32/oprofile/Makefile @@ -5,4 +5,4 @@ oprofile-y := $(addprefix ../../../drivers/oprofile/, \ event_buffer.o oprofile_files.o \ oprofilefs.o oprofile_stats.o \ timer_int.o) -oprofile-y += op_model_avr32.o +oprofile-y += op_model_avr32.o backtrace.o diff --git a/arch/avr32/oprofile/backtrace.c b/arch/avr32/oprofile/backtrace.c new file mode 100644 index 00000000000..75d9ad6f99c --- /dev/null +++ b/arch/avr32/oprofile/backtrace.c @@ -0,0 +1,81 @@ +/* + * AVR32 specific backtracing code for oprofile + * + * Copyright 2008 Weinmann GmbH + * + * Author: Nikolaus Voss <n.voss@weinmann.de> + * + * Based on i386 oprofile backtrace code by John Levon and David Smith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/oprofile.h> +#include <linux/sched.h> +#include <linux/uaccess.h> + +/* The first two words of each frame on the stack look like this if we have + * frame pointers */ +struct frame_head { + unsigned long lr; + struct frame_head *fp; +}; + +/* copied from arch/avr32/kernel/process.c */ +static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) +{ + return (p > (unsigned long)tinfo) + && (p < (unsigned long)tinfo + THREAD_SIZE - 3); +} + +/* copied from arch/x86/oprofile/backtrace.c */ +static struct frame_head *dump_user_backtrace(struct frame_head *head) +{ + struct frame_head bufhead[2]; + + /* Also check accessibility of one struct frame_head beyond */ + if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) + return NULL; + if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) + return NULL; + + oprofile_add_trace(bufhead[0].lr); + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (bufhead[0].fp <= head) + return NULL; + + return bufhead[0].fp; +} + +void avr32_backtrace(struct pt_regs * const regs, unsigned int depth) +{ + /* Get first frame pointer */ + struct frame_head *head = (struct frame_head *)(regs->r7); + + if (!user_mode(regs)) { +#ifdef CONFIG_FRAME_POINTER + /* + * Traverse the kernel stack from frame to frame up to + * "depth" steps. + */ + while (depth-- && valid_stack_ptr(task_thread_info(current), + (unsigned long)head)) { + oprofile_add_trace(head->lr); + if (head->fp <= head) + break; + head = head->fp; + } +#endif + } else { + /* Assume we have frame pointers in user mode process */ + while (depth-- && head) + head = dump_user_backtrace(head); + } +} + + diff --git a/arch/avr32/oprofile/op_model_avr32.c b/arch/avr32/oprofile/op_model_avr32.c index df42325c7f8..a3e9b3c4845 100644 --- a/arch/avr32/oprofile/op_model_avr32.c +++ b/arch/avr32/oprofile/op_model_avr32.c @@ -22,6 +22,8 @@ #define AVR32_PERFCTR_IRQ_GROUP 0 #define AVR32_PERFCTR_IRQ_LINE 1 +void avr32_backtrace(struct pt_regs * const regs, unsigned int depth); + enum { PCCNT, PCNT0, PCNT1, NR_counter }; struct avr32_perf_counter { @@ -223,6 +225,8 @@ int __init oprofile_arch_init(struct oprofile_operations *ops) memcpy(ops, &avr32_perf_counter_ops, sizeof(struct oprofile_operations)); + ops->backtrace = avr32_backtrace; + printk(KERN_INFO "oprofile: using AVR32 performance monitoring.\n"); return 0; diff --git a/arch/blackfin/Kconfig b/arch/blackfin/Kconfig index 4154ff1101f..8102c79aaa9 100644 --- a/arch/blackfin/Kconfig +++ b/arch/blackfin/Kconfig @@ -162,16 +162,28 @@ config BF549 config BF561 bool "BF561" help - Not Supported Yet - Work in progress - BF561 Processor Support. + BF561 Processor Support. endchoice +config BF_REV_MIN + int + default 0 if (BF52x || BF54x) + default 2 if (BF537 || BF536 || BF534) + default 3 if (BF561 ||BF533 || BF532 || BF531) + +config BF_REV_MAX + int + default 2 if (BF52x || BF54x) + default 3 if (BF537 || BF536 || BF534) + default 5 if (BF561) + default 6 if (BF533 || BF532 || BF531) + choice prompt "Silicon Rev" - default BF_REV_0_1 if BF527 - default BF_REV_0_2 if BF537 - default BF_REV_0_3 if BF533 - default BF_REV_0_0 if BF549 + default BF_REV_0_1 if (BF52x || BF54x) + default BF_REV_0_2 if (BF534 || BF536 || BF537) + default BF_REV_0_3 if (BF531 || BF532 || BF533 || BF561) config BF_REV_0_0 bool "0.0" @@ -183,7 +195,7 @@ config BF_REV_0_1 config BF_REV_0_2 bool "0.2" - depends on (BF537 || BF536 || BF534) + depends on (BF52x || BF537 || BF536 || BF534 || BF54x) config BF_REV_0_3 bool "0.3" @@ -197,6 +209,10 @@ config BF_REV_0_5 bool "0.5" depends on (BF561 || BF533 || BF532 || BF531) +config BF_REV_0_6 + bool "0.6" + depends on (BF533 || BF532 || BF531) + config BF_REV_ANY bool "any" @@ -249,7 +265,7 @@ config MEM_MT48LC8M32B2B5_7 config MEM_MT48LC32M16A2TG_75 bool - depends on (BFIN527_EZKIT || BFIN532_IP0X || BLACKSTAMP) + depends on (BFIN527_EZKIT || BFIN532_IP0X || BLACKSTAMP || BFIN526_EZBRD) default y source "arch/blackfin/mach-bf527/Kconfig" @@ -286,13 +302,20 @@ config BOOT_LOAD memory region is used to capture NULL pointer references as well as some core kernel functions. +config ROM_BASE + hex "Kernel ROM Base" + default "0x20040000" + range 0x20000000 0x20400000 if !(BF54x || BF561) + range 0x20000000 0x30000000 if (BF54x || BF561) + help + comment "Clock/PLL Setup" config CLKIN_HZ int "Frequency of the crystal on the board in Hz" default "11059200" if BFIN533_STAMP default "27000000" if BFIN533_EZKIT - default "25000000" if (BFIN537_STAMP || BFIN527_EZKIT || H8606_HVSISTEMAS || BLACKSTAMP) + default "25000000" if (BFIN537_STAMP || BFIN527_EZKIT || H8606_HVSISTEMAS || BLACKSTAMP || BFIN526_EZBRD) default "30000000" if BFIN561_EZKIT default "24576000" if PNAV10 default "10000000" if BFIN532_IP0X @@ -332,7 +355,7 @@ config VCO_MULT default "22" if BFIN533_BLUETECHNIX_CM default "20" if (BFIN537_BLUETECHNIX_CM || BFIN527_BLUETECHNIX_CM || BFIN561_BLUETECHNIX_CM) default "20" if BFIN561_EZKIT - default "16" if (H8606_HVSISTEMAS || BLACKSTAMP) + default "16" if (H8606_HVSISTEMAS || BLACKSTAMP || BFIN526_EZBRD) help This controls the frequency of the on-chip PLL. This can be between 1 and 64. PLL Frequency = (Crystal Frequency) * (this setting) @@ -368,14 +391,6 @@ config SCLK_DIV This can be between 1 and 15 System Clock = (PLL frequency) / (this setting) -config MAX_MEM_SIZE - int "Max SDRAM Memory Size in MBytes" - depends on !MPU - default 512 - help - This is the max memory size that the kernel will create CPLB - tables for. Your system will not be able to handle any more. - choice prompt "DDR SDRAM Chip Type" depends on BFIN_KERNEL_CLOCK @@ -389,6 +404,14 @@ config MEM_MT46V32M16_5B bool "MT46V32M16_5B" endchoice +config MAX_MEM_SIZE + int "Max SDRAM Memory Size in MBytes" + depends on !MPU + default 512 + help + This is the max memory size that the kernel will create CPLB + tables for. Your system will not be able to handle any more. + # # Max & Min Speeds for various Chips # @@ -455,8 +478,6 @@ config CYCLES_CLOCKSOURCE source kernel/time/Kconfig -comment "Memory Setup" - comment "Misc" choice @@ -622,6 +643,15 @@ config CPLB_SWITCH_TAB_L1 If enabled, the CPLB Switch Tables are linked into L1 data memory. (less latency) +config APP_STACK_L1 + bool "Support locating application stack in L1 Scratch Memory" + default y + help + If enabled the application stack can be located in L1 + scratch memory (less latency). + + Currently only works with FLAT binaries. + comment "Speed Optimizations" config BFIN_INS_LOWOVERHEAD bool "ins[bwl] low overhead, higher interrupt latency" @@ -755,6 +785,13 @@ config BFIN_WT endchoice +config BFIN_L2_CACHEABLE + bool "Cache L2 SRAM" + depends on (BFIN_DCACHE || BFIN_ICACHE) && (BF54x || BF561) + default n + help + Select to make L2 SRAM cacheable in L1 data and instruction cache. + config MPU bool "Enable the memory protection unit (EXPERIMENTAL)" default n diff --git a/arch/blackfin/Kconfig.debug b/arch/blackfin/Kconfig.debug index c468624d55f..3ad25983ec9 100644 --- a/arch/blackfin/Kconfig.debug +++ b/arch/blackfin/Kconfig.debug @@ -2,6 +2,22 @@ menu "Kernel hacking" source "lib/Kconfig.debug" +config HAVE_ARCH_KGDB + def_bool y + +config DEBUG_VERBOSE + bool "Verbose fault messages" + default y + select PRINTK + help + When a program crashes due to an exception, or the kernel detects + an internal error, the kernel can print a not so brief message + explaining what the problem was. This debugging information is + useful to developers and kernel hackers when tracking down problems, + but mostly meaningless to other people. This is always helpful for + debugging but serves no purpose on a production system. + Most people should say N here. + config DEBUG_MMRS bool "Generate Blackfin MMR tree" select DEBUG_FS @@ -22,6 +38,44 @@ config DEBUG_HWERR hardware error interrupts and need to know where they are coming from. +config DEBUG_DOUBLEFAULT + bool "Debug Double Faults" + default n + help + If an exception is caused while executing code within the exception + handler, the NMI handler, the reset vector, or in emulator mode, + a double fault occurs. On the Blackfin, this is a unrecoverable + event. You have two options: + - RESET exactly when double fault occurs. The excepting + instruction address is stored in RETX, where the next kernel + boot will print it out. + - Print debug message. This is much more error prone, although + easier to handle. It is error prone since: + - The excepting instruction is not committed. + - All writebacks from the instruction are prevented. + - The generated exception is not taken. + - The EXCAUSE field is updated with an unrecoverable event + The only way to check this is to see if EXCAUSE contains the + unrecoverable event value at every exception return. By selecting + this option, you are skipping over the faulting instruction, and + hoping things stay together enough to print out a debug message. + + This does add a little kernel code, but is the only method to debug + double faults - if unsure say "Y" + +choice + prompt "Double Fault Failure Method" + default DEBUG_DOUBLEFAULT_PRINT + depends on DEBUG_DOUBLEFAULT + +config DEBUG_DOUBLEFAULT_PRINT + bool "Print" + +config DEBUG_DOUBLEFAULT_RESET + bool "Reset" + +endchoice + config DEBUG_ICACHE_CHECK bool "Check Instruction cache coherency" depends on DEBUG_KERNEL @@ -143,6 +197,7 @@ config DEBUG_BFIN_NO_KERN_HWTRACE config EARLY_PRINTK bool "Early printk" default n + select SERIAL_CORE_CONSOLE help This option enables special console drivers which allow the kernel to print messages very early in the bootup process. diff --git a/arch/blackfin/Makefile b/arch/blackfin/Makefile index eac0533d6e4..6bf50977850 100644 --- a/arch/blackfin/Makefile +++ b/arch/blackfin/Makefile @@ -67,6 +67,7 @@ rev-$(CONFIG_BF_REV_0_2) := 0.2 rev-$(CONFIG_BF_REV_0_3) := 0.3 rev-$(CONFIG_BF_REV_0_4) := 0.4 rev-$(CONFIG_BF_REV_0_5) := 0.5 +rev-$(CONFIG_BF_REV_0_6) := 0.6 rev-$(CONFIG_BF_REV_NONE) := none rev-$(CONFIG_BF_REV_ANY) := any diff --git a/arch/blackfin/configs/BF526-EZBRD_defconfig b/arch/blackfin/configs/BF526-EZBRD_defconfig new file mode 100644 index 00000000000..c33bf6f8381 --- /dev/null +++ b/arch/blackfin/configs/BF526-EZBRD_defconfig @@ -0,0 +1,1427 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.26.3 +# Thu Aug 28 16:49:53 2008 +# +# CONFIG_MMU is not set +# CONFIG_FPU is not set +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set +CONFIG_BLACKFIN=y +CONFIG_ZONE_DMA=y +CONFIG_GENERIC_FIND_NEXT_BIT=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_GENERIC_GPIO=y +CONFIG_FORCE_MAX_ZONEORDER=14 +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_AUDIT is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +# CONFIG_GROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +CONFIG_SYSFS_DEPRECATED_V2=y +# CONFIG_RELAY is not set +# CONFIG_NAMESPACES is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_SYSCTL_SYSCALL_CHECK=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_COMPAT_BRK=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_TIMERFD=y +CONFIG_EVENTFD=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +# CONFIG_PROFILING is not set +# CONFIG_MARKERS is not set +CONFIG_HAVE_OPROFILE=y +# CONFIG_HAVE_KPROBES is not set +# CONFIG_HAVE_KRETPROBES is not set +# CONFIG_HAVE_DMA_ATTRS is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +CONFIG_TINY_SHMEM=y +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +# CONFIG_MODULE_FORCE_LOAD is not set +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_NOOP=y +CONFIG_DEFAULT_IOSCHED="noop" +CONFIG_CLASSIC_RCU=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set + +# +# Blackfin Processor Options +# + +# +# Processor and Board Settings +# +# CONFIG_BF522 is not set +# CONFIG_BF523 is not set +# CONFIG_BF524 is not set +# CONFIG_BF525 is not set +CONFIG_BF526=y +# CONFIG_BF527 is not set +# CONFIG_BF531 is not set +# CONFIG_BF532 is not set +# CONFIG_BF533 is not set +# CONFIG_BF534 is not set +# CONFIG_BF536 is not set +# CONFIG_BF537 is not set +# CONFIG_BF542 is not set +# CONFIG_BF544 is not set +# CONFIG_BF547 is not set +# CONFIG_BF548 is not set +# CONFIG_BF549 is not set +# CONFIG_BF561 is not set +CONFIG_BF_REV_0_0=y +# CONFIG_BF_REV_0_1 is not set +# CONFIG_BF_REV_0_2 is not set +# CONFIG_BF_REV_0_3 is not set +# CONFIG_BF_REV_0_4 is not set +# CONFIG_BF_REV_0_5 is not set +# CONFIG_BF_REV_ANY is not set +# CONFIG_BF_REV_NONE is not set +CONFIG_BF52x=y +CONFIG_MEM_MT48LC32M16A2TG_75=y +# CONFIG_BFIN527_EZKIT is not set +# CONFIG_BFIN527_BLUETECHNIX_CM is not set +CONFIG_BFIN526_EZBRD=y + +# +# BF527 Specific Configuration +# + +# +# Alternative Multiplexing Scheme +# +# CONFIG_BF527_SPORT0_PORTF is not set +CONFIG_BF527_SPORT0_PORTG=y +CONFIG_BF527_SPORT0_TSCLK_PG10=y +# CONFIG_BF527_SPORT0_TSCLK_PG14 is not set +CONFIG_BF527_UART1_PORTF=y +# CONFIG_BF527_UART1_PORTG is not set +# CONFIG_BF527_NAND_D_PORTF is not set +CONFIG_BF527_NAND_D_PORTH=y + +# +# Interrupt Priority Assignment +# + +# +# Priority +# +CONFIG_IRQ_PLL_WAKEUP=7 +CONFIG_IRQ_DMA0_ERROR=7 +CONFIG_IRQ_DMAR0_BLK=7 +CONFIG_IRQ_DMAR1_BLK=7 +CONFIG_IRQ_DMAR0_OVR=7 +CONFIG_IRQ_DMAR1_OVR=7 +CONFIG_IRQ_PPI_ERROR=7 +CONFIG_IRQ_MAC_ERROR=7 +CONFIG_IRQ_SPORT0_ERROR=7 +CONFIG_IRQ_SPORT1_ERROR=7 +CONFIG_IRQ_UART0_ERROR=7 +CONFIG_IRQ_UART1_ERROR=7 +CONFIG_IRQ_RTC=8 +CONFIG_IRQ_PPI=8 +CONFIG_IRQ_SPORT0_RX=9 +CONFIG_IRQ_SPORT0_TX=9 +CONFIG_IRQ_SPORT1_RX=9 +CONFIG_IRQ_SPORT1_TX=9 +CONFIG_IRQ_TWI=10 +CONFIG_IRQ_SPI=10 +CONFIG_IRQ_UART0_RX=10 +CONFIG_IRQ_UART0_TX=10 +CONFIG_IRQ_UART1_RX=10 +CONFIG_IRQ_UART1_TX=10 +CONFIG_IRQ_OPTSEC=11 +CONFIG_IRQ_CNT=11 +CONFIG_IRQ_MAC_RX=11 +CONFIG_IRQ_PORTH_INTA=11 +CONFIG_IRQ_MAC_TX=11 +CONFIG_IRQ_PORTH_INTB=11 +CONFIG_IRQ_TMR0=12 +CONFIG_IRQ_TMR1=12 +CONFIG_IRQ_TMR2=12 +CONFIG_IRQ_TMR3=12 +CONFIG_IRQ_TMR4=12 +CONFIG_IRQ_TMR5=12 +CONFIG_IRQ_TMR6=12 +CONFIG_IRQ_TMR7=12 +CONFIG_IRQ_PORTG_INTA=12 +CONFIG_IRQ_PORTG_INTB=12 +CONFIG_IRQ_MEM_DMA0=13 +CONFIG_IRQ_MEM_DMA1=13 +CONFIG_IRQ_WATCH=13 +CONFIG_IRQ_PORTF_INTA=13 +CONFIG_IRQ_PORTF_INTB=13 +CONFIG_IRQ_SPI_ERROR=7 +CONFIG_IRQ_NFC_ERROR=7 +CONFIG_IRQ_HDMA_ERROR=7 +CONFIG_IRQ_HDMA=7 +CONFIG_IRQ_USB_EINT=10 +CONFIG_IRQ_USB_INT0=11 +CONFIG_IRQ_USB_INT1=11 +CONFIG_IRQ_USB_INT2=11 +CONFIG_IRQ_USB_DMA=11 + +# +# Board customizations +# +# CONFIG_CMDLINE_BOOL is not set +CONFIG_BOOT_LOAD=0x1000 + +# +# Clock/PLL Setup +# +CONFIG_CLKIN_HZ=25000000 +# CONFIG_BFIN_KERNEL_CLOCK is not set +CONFIG_MAX_MEM_SIZE=512 +CONFIG_MAX_VCO_HZ=400000000 +CONFIG_MIN_VCO_HZ=50000000 +CONFIG_MAX_SCLK_HZ=133333333 +CONFIG_MIN_SCLK_HZ=27000000 + +# +# Kernel Timer/Scheduler +# +# CONFIG_HZ_100 is not set +CONFIG_HZ_250=y +# CONFIG_HZ_300 is not set +# CONFIG_HZ_1000 is not set +CONFIG_HZ=250 +# CONFIG_SCHED_HRTICK is not set +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +# CONFIG_CYCLES_CLOCKSOURCE is not set +# CONFIG_TICK_ONESHOT is not set +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y + +# +# Memory Setup +# + +# +# Misc +# +CONFIG_BFIN_SCRATCH_REG_RETN=y +# CONFIG_BFIN_SCRATCH_REG_RETE is not set +# CONFIG_BFIN_SCRATCH_REG_CYCLES is not set + +# +# Blackfin Kernel Optimizations +# + +# +# Memory Optimizations +# +CONFIG_I_ENTRY_L1=y +CONFIG_EXCPT_IRQ_SYSC_L1=y +CONFIG_DO_IRQ_L1=y +CONFIG_CORE_TIMER_IRQ_L1=y +CONFIG_IDLE_L1=y +# CONFIG_SCHEDULE_L1 is not set +CONFIG_ARITHMETIC_OPS_L1=y +CONFIG_ACCESS_OK_L1=y +# CONFIG_MEMSET_L1 is not set +# CONFIG_MEMCPY_L1 is not set +# CONFIG_SYS_BFIN_SPINLOCK_L1 is not set +# CONFIG_IP_CHECKSUM_L1 is not set +CONFIG_CACHELINE_ALIGNED_L1=y +# CONFIG_SYSCALL_TAB_L1 is not set +# CONFIG_CPLB_SWITCH_TAB_L1 is not set + +# +# Speed Optimizations +# +CONFIG_BFIN_INS_LOWOVERHEAD=y +CONFIG_RAMKERNEL=y +# CONFIG_ROMKERNEL is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_VIRT_TO_BUS=y +CONFIG_BFIN_GPTIMERS=y +CONFIG_BFIN_DMA_5XX=y +# CONFIG_DMA_UNCACHED_4M is not set +# CONFIG_DMA_UNCACHED_2M is not set +CONFIG_DMA_UNCACHED_1M=y +# CONFIG_DMA_UNCACHED_NONE is not set + +# +# Cache Support +# +CONFIG_BFIN_ICACHE=y +CONFIG_BFIN_DCACHE=y +# CONFIG_BFIN_DCACHE_BANKA is not set +# CONFIG_BFIN_ICACHE_LOCK is not set +CONFIG_BFIN_WB=y +# CONFIG_BFIN_WT is not set +# CONFIG_MPU is not set + +# +# Asynchonous Memory Configuration +# + +# +# EBIU_AMGCTL Global Control +# +CONFIG_C_AMCKEN=y +CONFIG_C_CDPRIO=y +# CONFIG_C_AMBEN is not set +# CONFIG_C_AMBEN_B0 is not set +# CONFIG_C_AMBEN_B0_B1 is not set +# CONFIG_C_AMBEN_B0_B1_B2 is not set +CONFIG_C_AMBEN_ALL=y + +# +# EBIU_AMBCTL Control +# +CONFIG_BANK_0=0x7BB0 +CONFIG_BANK_1=0x5554 +CONFIG_BANK_2=0x7BB0 +CONFIG_BANK_3=0xFFC0 + +# +# Bus options (PCI, PCMCIA, EISA, MCA, ISA) +# +# CONFIG_ARCH_SUPPORTS_MSI is not set +# CONFIG_PCCARD is not set + +# +# Executable file formats +# +CONFIG_BINFMT_ELF_FDPIC=y +CONFIG_BINFMT_FLAT=y +CONFIG_BINFMT_ZFLAT=y +# CONFIG_BINFMT_SHARED_FLAT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +# CONFIG_PM is not set +CONFIG_ARCH_SUSPEND_POSSIBLE=y +# CONFIG_PM_WAKEUP_BY_GPIO is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_XFRM_STATISTICS is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +# CONFIG_INET_LRO is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_NETLABEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_CAN is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +# CONFIG_WIRELESS_EXT is not set +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AR7_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=m +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +CONFIG_MTD_JEDECPROBE=m +CONFIG_MTD_GEN_PROBE=m +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_CFI_INTELEXT is not set +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_RAM=y +CONFIG_MTD_ROM=m +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_GPIO_ADDR is not set +# CONFIG_MTD_UCLINUX is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=m +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +CONFIG_MTD_NAND_BFIN=m +CONFIG_BFIN_NAND_BASE=0x20212000 +CONFIG_BFIN_NAND_CLE=2 +CONFIG_BFIN_NAND_ALE=1 +CONFIG_BFIN_NAND_READY=3 +CONFIG_MTD_NAND_IDS=m +# CONFIG_MTD_NAND_BF5XX is not set +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +# CONFIG_BLK_DEV_XIP is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +# CONFIG_ENCLOSURE_SERVICES is not set +CONFIG_HAVE_IDE=y +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +CONFIG_PHYLIB=y + +# +# MII PHY device drivers +# +# CONFIG_MARVELL_PHY is not set +# CONFIG_DAVICOM_PHY is not set +# CONFIG_QSEMI_PHY is not set +# CONFIG_LXT_PHY is not set +# CONFIG_CICADA_PHY is not set +# CONFIG_VITESSE_PHY is not set +# CONFIG_SMSC_PHY is not set +# CONFIG_BROADCOM_PHY is not set +# CONFIG_ICPLUS_PHY is not set +# CONFIG_REALTEK_PHY is not set +# CONFIG_FIXED_PHY is not set +# CONFIG_MDIO_BITBANG is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_BFIN_MAC=y +CONFIG_BFIN_TX_DESC_NUM=10 +CONFIG_BFIN_RX_DESC_NUM=20 +CONFIG_BFIN_MAC_RMII=y +# CONFIG_SMC91X is not set +# CONFIG_SMSC911X is not set +# CONFIG_DM9000 is not set +# CONFIG_ENC28J60 is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_B44 is not set +CONFIG_NETDEV_1000=y +# CONFIG_E1000E_ENABLED is not set +# CONFIG_AX88180 is not set +CONFIG_NETDEV_10000=y + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +# CONFIG_IWLWIFI_LEDS is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set +# CONFIG_PHONE is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_UINPUT is not set +# CONFIG_TWI_KEYPAD is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +# CONFIG_AD9960 is not set +# CONFIG_SPI_ADC_BF533 is not set +# CONFIG_BF5xx_PPIFCD is not set +# CONFIG_BFIN_SIMPLE_TIMER is not set +# CONFIG_BF5xx_PPI is not set +# CONFIG_BFIN_SPORT is not set +# CONFIG_BFIN_TIMER_LATENCY is not set +# CONFIG_TWI_LCD is not set +CONFIG_SIMPLE_GPIO=m +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +CONFIG_DEVKMEM=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_BFIN=y +CONFIG_SERIAL_BFIN_CONSOLE=y +CONFIG_SERIAL_BFIN_DMA=y +# CONFIG_SERIAL_BFIN_PIO is not set +# CONFIG_SERIAL_BFIN_UART0 is not set +CONFIG_SERIAL_BFIN_UART1=y +# CONFIG_BFIN_UART1_CTSRTS is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_SERIAL_BFIN_SPORT is not set +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +CONFIG_BFIN_OTP=y +# CONFIG_BFIN_OTP_WRITE_ENABLE is not set + +# +# CAN, the car bus and industrial fieldbus +# +# CONFIG_CAN4LINUX is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_HELPER_AUTO=y + +# +# I2C Hardware Bus support +# +CONFIG_I2C_BLACKFIN_TWI=y +CONFIG_I2C_BLACKFIN_TWI_CLK_KHZ=50 +# CONFIG_I2C_GPIO is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set +# CONFIG_I2C_PCA_PLATFORM is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_AD5252 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_PCF8575 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_BFIN=y +# CONFIG_SPI_BITBANG is not set + +# +# SPI Protocol Masters +# +# CONFIG_SPI_AT25 is not set +# CONFIG_SPI_SPIDEV is not set +# CONFIG_SPI_TLE62X0 is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_AD7418 is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1029 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ADT7470 is not set +# CONFIG_SENSORS_ADT7473 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_F71805F is not set +# CONFIG_SENSORS_F71882FG is not set +# CONFIG_SENSORS_F75375S is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM70 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_LM93 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_MAX6650 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_PC87427 is not set +# CONFIG_SENSORS_DME1737 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47M192 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_ADS7828 is not set +# CONFIG_SENSORS_THMC50 is not set +# CONFIG_SENSORS_VT1211 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83791D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83793 is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83L786NG is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_HWMON_DEBUG_CHIP is not set +# CONFIG_THERMAL is not set +# CONFIG_THERMAL_HWMON is not set +CONFIG_WATCHDOG=y +# CONFIG_WATCHDOG_NOWAYOUT is not set + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_BFIN_WDT=y + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_PASIC3 is not set + +# +# Multimedia devices +# + +# +# Multimedia core support +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_VIDEO_MEDIA is not set + +# +# Multimedia drivers +# +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +# CONFIG_FB is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +CONFIG_SOUND=m + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=m +CONFIG_SND_TIMER=m +CONFIG_SND_PCM=m +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=m +CONFIG_SND_PCM_OSS=m +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# SPI devices +# + +# +# ALSA Blackfin devices +# +# CONFIG_SND_BLACKFIN_AD1836 is not set +# CONFIG_SND_BFIN_AD73311 is not set +# CONFIG_SND_BFIN_AD73322 is not set + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set + +# +# System on Chip audio support +# +CONFIG_SND_SOC=m +CONFIG_SND_BF5XX_I2S=m +CONFIG_SND_BF5XX_SOC_SSM2602=m +# CONFIG_SND_BF5XX_AC97 is not set +CONFIG_SND_BF5XX_SOC_SPORT=m +CONFIG_SND_BF5XX_SOC_I2S=m +CONFIG_SND_BF5XX_SPORT_NUM=0 + +# +# ALSA SoC audio for Freescale SOCs +# + +# +# SoC Audio for the Texas Instruments OMAP +# +CONFIG_SND_SOC_SSM2602=m + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set +# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set +# CONFIG_USB_OTG_WHITELIST is not set +CONFIG_USB_OTG_BLACKLIST_HUB=y + +# +# USB Host Controller Drivers +# +# CONFIG_USB_C67X00_HCD is not set +# CONFIG_USB_ISP116X_HCD is not set +# CONFIG_USB_ISP1760_HCD is not set +# CONFIG_USB_ISP1362_HCD is not set +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set +CONFIG_USB_MUSB_HDRC=y +CONFIG_USB_MUSB_SOC=y + +# +# Blackfin high speed USB support +# +CONFIG_USB_MUSB_HOST=y +# CONFIG_USB_MUSB_PERIPHERAL is not set +# CONFIG_USB_MUSB_OTG is not set +CONFIG_USB_MUSB_HDRC_HCD=y +CONFIG_MUSB_PIO_ONLY=y +CONFIG_USB_MUSB_LOGLEVEL=0 + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_WDM is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +CONFIG_USB_MON=y + +# +# USB port drivers +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_SISUSBVGA is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_ISIGHTFW is not set +# CONFIG_USB_GADGET is not set +# CONFIG_MMC is not set +# CONFIG_MEMSTICK is not set +# CONFIG_NEW_LEDS is not set +# CONFIG_ACCESSIBILITY is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set +# CONFIG_RTC_DRV_S35390A is not set +# CONFIG_RTC_DRV_FM3130 is not set + +# +# SPI RTC drivers +# +# CONFIG_RTC_DRV_MAX6902 is not set +# CONFIG_RTC_DRV_R9701 is not set +# CONFIG_RTC_DRV_RS5C348 is not set + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_DS1511 is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_BFIN=y +# CONFIG_UIO is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_DNOTIFY is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_YAFFS_FS=m +CONFIG_YAFFS_YAFFS1=y +# CONFIG_YAFFS_9BYTE_TAGS is not set +# CONFIG_YAFFS_DOES_ECC is not set +CONFIG_YAFFS_YAFFS2=y +CONFIG_YAFFS_AUTO_YAFFS2=y +# CONFIG_YAFFS_DISABLE_LAZY_LOAD is not set +# CONFIG_YAFFS_DISABLE_WIDE_TNODES is not set +# CONFIG_YAFFS_ALWAYS_CHECK_CHUNK_ERASED is not set +CONFIG_YAFFS_SHORT_NAMES_IN_RAM=y +CONFIG_JFFS2_FS=m +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +# CONFIG_JFFS2_LZO is not set +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_NETWORK_FILESYSTEMS=y +CONFIG_NFS_FS=m +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFSD is not set +CONFIG_LOCKD=m +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=m +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=m +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_NLS=m +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# 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_DLM is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_FRAME_WARN=1024 +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_SAMPLES is not set +CONFIG_DEBUG_MMRS=y +CONFIG_DEBUG_HUNT_FOR_ZERO=y +CONFIG_DEBUG_BFIN_HWTRACE_ON=y +CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_OFF=y +# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_ONE is not set +# CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION_TWO is not set +CONFIG_DEBUG_BFIN_HWTRACE_COMPRESSION=0 +# CONFIG_DEBUG_BFIN_HWTRACE_EXPAND is not set +# CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE is not set +CONFIG_EARLY_PRINTK=y +CONFIG_CPLB_INFO=y +CONFIG_ACCESS_CHECK=y + +# +# Security options +# +# CONFIG_KEYS is not set +CONFIG_SECURITY=y +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_CAPABILITIES is not set +# CONFIG_SECURITY_ROOTPLUG is not set +CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR=0 +CONFIG_CRYPTO=y + +# +# Crypto core or helper +# +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Authenticated Encryption with Associated Data +# +# CONFIG_CRYPTO_CCM is not set +# CONFIG_CRYPTO_GCM is not set +# CONFIG_CRYPTO_SEQIV is not set + +# +# Block modes +# +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_CTR is not set +# CONFIG_CRYPTO_CTS is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_XTS is not set + +# +# Hash modes +# +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set + +# +# Digest +# +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_WP512 is not set + +# +# Ciphers +# +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_SALSA20 is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_TWOFISH is not set + +# +# Compression +# +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_GENERIC_FIND_FIRST_BIT is not set +CONFIG_CRC_CCITT=m +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=m +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/arch/blackfin/configs/H8606_defconfig b/arch/blackfin/configs/H8606_defconfig index ba0bee90b7e..1fc31f1b762 100644 --- a/arch/blackfin/configs/H8606_defconfig +++ b/arch/blackfin/configs/H8606_defconfig @@ -1,6 +1,6 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.22.12 +# Linux kernel version: 2.6.22.14 # # CONFIG_MMU is not set # CONFIG_FPU is not set @@ -192,7 +192,7 @@ CONFIG_CLKIN_HZ=25000000 # CONFIG_BFIN_KERNEL_CLOCK is not set CONFIG_MAX_VCO_HZ=400000000 CONFIG_MIN_VCO_HZ=50000000 -CONFIG_MAX_SCLK_HZ=133000000 +CONFIG_MAX_SCLK_HZ=133333333 CONFIG_MIN_SCLK_HZ=27000000 # @@ -516,7 +516,7 @@ CONFIG_MTD_COMPLEX_MAPPINGS=y # # CONFIG_MTD_DATAFLASH is not set CONFIG_MTD_M25P80=y -CONFIG_M25PXX_USE_FAST_READ=y +# CONFIG_M25PXX_USE_FAST_READ is not set # CONFIG_MTD_SLRAM is not set # CONFIG_MTD_PHRAM is not set # CONFIG_MTD_MTDRAM is not set @@ -635,25 +635,25 @@ CONFIG_INPUT=y # CONFIG_INPUT_MOUSEDEV is not set # CONFIG_INPUT_JOYDEV is not set # CONFIG_INPUT_TSDEV is not set -CONFIG_INPUT_EVDEV=m +CONFIG_INPUT_EVDEV=y # CONFIG_INPUT_EVBUG is not set # # Input Device Drivers # -# CONFIG_INPUT_KEYBOARD is not set +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_KEYBOARD_GPIO=y # CONFIG_INPUT_MOUSE is not set # CONFIG_INPUT_JOYSTICK is not set # CONFIG_INPUT_TABLET is not set # CONFIG_INPUT_TOUCHSCREEN is not set -CONFIG_INPUT_MISC=y -# CONFIG_INPUT_ATI_REMOTE is not set -# CONFIG_INPUT_ATI_REMOTE2 is not set -# CONFIG_INPUT_KEYSPAN_REMOTE is not set -# CONFIG_INPUT_POWERMATE is not set -# CONFIG_INPUT_YEALINK is not set -# CONFIG_INPUT_UINPUT is not set -# CONFIG_BF53X_PFBUTTONS is not set +# CONFIG_INPUT_MISC is not set # # Hardware I/O ports @@ -681,7 +681,15 @@ CONFIG_BFIN_TIMER_LATENCY=y # # Serial drivers # -# CONFIG_SERIAL_8250 is not set +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +# CONFIG_SERIAL_8250_MANY_PORTS is not set +CONFIG_SERIAL_8250_SHARE_IRQ=y +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_RSA is not set # # Non-8250 serial port support diff --git a/arch/blackfin/include/asm/a.out.h b/arch/blackfin/include/asm/a.out.h deleted file mode 100644 index 6c3d652ebd3..00000000000 --- a/arch/blackfin/include/asm/a.out.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef __BFIN_A_OUT_H__ -#define __BFIN_A_OUT_H__ - -struct exec { - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __BFIN_A_OUT_H__ */ diff --git a/arch/blackfin/include/asm/bfin-global.h b/arch/blackfin/include/asm/bfin-global.h index 7ba70de66f2..56dcb0a2d24 100644 --- a/arch/blackfin/include/asm/bfin-global.h +++ b/arch/blackfin/include/asm/bfin-global.h @@ -63,7 +63,6 @@ extern void bfin_dcache_init(void); extern void init_exception_vectors(void); extern void program_IAR(void); -extern void bfin_reset(void); extern asmlinkage void lower_to_irq14(void); extern asmlinkage void bfin_return_from_exception(void); extern asmlinkage void evt14_softirq(void); @@ -92,6 +91,8 @@ extern int sram_free(const void*); extern void *sram_alloc_with_lsl(size_t, unsigned long); extern int sram_free_with_lsl(const void*); +extern void *isram_memcpy(void *dest, const void *src, size_t n); + extern const char bfin_board_name[]; extern unsigned long bfin_sic_iwr[]; @@ -104,7 +105,7 @@ extern char _stext_l1[], _etext_l1[], _sdata_l1[], _edata_l1[], _sbss_l1[], _stext_l2[], _etext_l2[], _sdata_l2[], _edata_l2[], _sbss_l2[], _ebss_l2[], _l2_lma_start[]; -/* only used when CONFIG_MTD_UCLINUX */ +/* only used when MTD_UCLINUX */ extern unsigned long memory_mtd_start, memory_mtd_end, mtd_size; #ifdef CONFIG_BFIN_ICACHE_LOCK diff --git a/arch/blackfin/include/asm/bfrom.h b/arch/blackfin/include/asm/bfrom.h new file mode 100644 index 00000000000..cfe8024c3b2 --- /dev/null +++ b/arch/blackfin/include/asm/bfrom.h @@ -0,0 +1,85 @@ +/* Blackfin on-chip ROM API + * + * Copyright 2008 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __BFROM_H__ +#define __BFROM_H__ + +#include <linux/types.h> + +/* Possible syscontrol action flags */ +#define SYSCTRL_READ 0x00000000 /* read registers */ +#define SYSCTRL_WRITE 0x00000001 /* write registers */ +#define SYSCTRL_SYSRESET 0x00000002 /* perform system reset */ +#define SYSCTRL_CORERESET 0x00000004 /* perform core reset */ +#define SYSCTRL_SOFTRESET 0x00000006 /* perform core and system reset */ +#define SYSCTRL_VRCTL 0x00000010 /* read/write VR_CTL register */ +#define SYSCTRL_EXTVOLTAGE 0x00000020 /* VDDINT supplied externally */ +#define SYSCTRL_INTVOLTAGE 0x00000000 /* VDDINT generated by on-chip regulator */ +#define SYSCTRL_OTPVOLTAGE 0x00000040 /* For Factory Purposes Only */ +#define SYSCTRL_PLLCTL 0x00000100 /* read/write PLL_CTL register */ +#define SYSCTRL_PLLDIV 0x00000200 /* read/write PLL_DIV register */ +#define SYSCTRL_LOCKCNT 0x00000400 /* read/write PLL_LOCKCNT register */ +#define SYSCTRL_PLLSTAT 0x00000800 /* read/write PLL_STAT register */ + +typedef struct ADI_SYSCTRL_VALUES { + uint16_t uwVrCtl; + uint16_t uwPllCtl; + uint16_t uwPllDiv; + uint16_t uwPllLockCnt; + uint16_t uwPllStat; +} ADI_SYSCTRL_VALUES; + +static uint32_t (* const bfrom_SysControl)(uint32_t action_flags, ADI_SYSCTRL_VALUES *power_settings, void *reserved) = (void *)0xEF000038; + +/* We need a dedicated function since we need to screw with the stack pointer + * when resetting. The on-chip ROM will save/restore registers on the stack + * when doing a system reset, so the stack cannot be outside of the chip. + */ +__attribute__((__noreturn__)) +static inline void bfrom_SoftReset(void *new_stack) +{ + while (1) + __asm__ __volatile__( + "sp = %[stack];" + "jump (%[bfrom_syscontrol]);" + : : [bfrom_syscontrol] "p"(bfrom_SysControl), + "q0"(SYSCTRL_SOFTRESET), + "q1"(0), + "q2"(NULL), + [stack] "p"(new_stack) + ); +} + +/* OTP Functions */ +static uint32_t (* const bfrom_OtpCommand)(uint32_t command, uint32_t value) = (void *)0xEF000018; +static uint32_t (* const bfrom_OtpRead)(uint32_t page, uint32_t flags, uint64_t *page_content) = (void *)0xEF00001A; +static uint32_t (* const bfrom_OtpWrite)(uint32_t page, uint32_t flags, uint64_t *page_content) = (void *)0xEF00001C; + +/* otp command: defines for "command" */ +#define OTP_INIT 0x00000001 +#define OTP_CLOSE 0x00000002 + +/* otp read/write: defines for "flags" */ +#define OTP_LOWER_HALF 0x00000000 /* select upper/lower 64-bit half (bit 0) */ +#define OTP_UPPER_HALF 0x00000001 +#define OTP_NO_ECC 0x00000010 /* do not use ECC */ +#define OTP_LOCK 0x00000020 /* sets page protection bit for page */ +#define OTP_CHECK_FOR_PREV_WRITE 0x00000080 + +/* Return values for all functions */ +#define OTP_SUCCESS 0x00000000 +#define OTP_MASTER_ERROR 0x001 +#define OTP_WRITE_ERROR 0x003 +#define OTP_READ_ERROR 0x005 +#define OTP_ACC_VIO_ERROR 0x009 +#define OTP_DATA_MULT_ERROR 0x011 +#define OTP_ECC_MULT_ERROR 0x021 +#define OTP_PREV_WR_ERROR 0x041 +#define OTP_DATA_SB_WARN 0x100 +#define OTP_ECC_SB_WARN 0x200 + +#endif diff --git a/arch/blackfin/include/asm/cacheflush.h b/arch/blackfin/include/asm/cacheflush.h index d81a77545a0..5ef9e35e9c3 100644 --- a/arch/blackfin/include/asm/cacheflush.h +++ b/arch/blackfin/include/asm/cacheflush.h @@ -30,8 +30,6 @@ #ifndef _BLACKFIN_CACHEFLUSH_H #define _BLACKFIN_CACHEFLUSH_H -#include <asm/cplb.h> - extern void blackfin_icache_dcache_flush_range(unsigned int, unsigned int); extern void blackfin_icache_flush_range(unsigned int, unsigned int); extern void blackfin_dcache_flush_range(unsigned int, unsigned int); diff --git a/arch/blackfin/include/asm/cplb.h b/arch/blackfin/include/asm/cplb.h index 05d6f05fb74..9e8b4035fce 100644 --- a/arch/blackfin/include/asm/cplb.h +++ b/arch/blackfin/include/asm/cplb.h @@ -55,7 +55,13 @@ #endif #define L1_DMEMORY (CPLB_LOCK | CPLB_COMMON) -#define L2_MEMORY (CPLB_COMMON) +#ifdef CONFIG_BFIN_L2_CACHEABLE +#define L2_IMEMORY (SDRAM_IGENERIC) +#define L2_DMEMORY (SDRAM_DGENERIC) +#else +#define L2_IMEMORY (CPLB_COMMON) +#define L2_DMEMORY (CPLB_COMMON) +#endif #define SDRAM_DNON_CHBL (CPLB_COMMON) #define SDRAM_EBIU (CPLB_COMMON) #define SDRAM_OOPS (CPLB_VALID | ANOMALY_05000158_WORKAROUND | CPLB_LOCK | CPLB_DIRTY) diff --git a/arch/blackfin/include/asm/cplbinit.h b/arch/blackfin/include/asm/cplbinit.h index 0eb1c1b685a..d179b747ff0 100644 --- a/arch/blackfin/include/asm/cplbinit.h +++ b/arch/blackfin/include/asm/cplbinit.h @@ -90,6 +90,20 @@ extern u_long dpdt_swapcount_table[]; extern unsigned long reserved_mem_dcache_on; extern unsigned long reserved_mem_icache_on; -extern void generate_cpl_tables(void); +extern void generate_cplb_tables(void); + +static inline int bfin_addr_dcachable(unsigned long addr) +{ +#ifdef CONFIG_BFIN_DCACHE + if (addr < (_ramend - DMA_UNCACHED_REGION)) + return 1; +#endif + + if (reserved_mem_dcache_on && + addr >= _ramend && addr < physical_mem_end) + return 1; + + return 0; +} #endif diff --git a/arch/blackfin/include/asm/cpumask.h b/arch/blackfin/include/asm/cpumask.h deleted file mode 100644 index b20a8e9012c..00000000000 --- a/arch/blackfin/include/asm/cpumask.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _ASM_BLACKFIN_CPUMASK_H -#define _ASM_BLACKFIN_CPUMASK_H - -#include <asm-generic/cpumask.h> - -#endif /* _ASM_BLACKFIN_CPUMASK_H */ diff --git a/arch/blackfin/include/asm/dma-mapping.h b/arch/blackfin/include/asm/dma-mapping.h index 1a13c2fc366..ede748d67ef 100644 --- a/arch/blackfin/include/asm/dma-mapping.h +++ b/arch/blackfin/include/asm/dma-mapping.h @@ -80,4 +80,15 @@ extern int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries, enum dma_data_direction direction); +static inline void dma_sync_single_for_cpu(struct device *dev, + dma_addr_t handle, size_t size, + enum dma_data_direction dir) +{ +} + +static inline void dma_sync_single_for_device(struct device *dev, + dma_addr_t handle, size_t size, + enum dma_data_direction dir) +{ +} #endif /* _BLACKFIN_DMA_MAPPING_H */ diff --git a/arch/blackfin/include/asm/kgdb.h b/arch/blackfin/include/asm/kgdb.h index 0f73847fd6b..26ebac6646d 100644 --- a/arch/blackfin/include/asm/kgdb.h +++ b/arch/blackfin/include/asm/kgdb.h @@ -124,9 +124,16 @@ enum regnames { /* Number of bytes of registers. */ #define NUMREGBYTES BFIN_NUM_REGS*4 -#define BREAKPOINT() asm(" EXCPT 2;"); -#define BREAK_INSTR_SIZE 2 -#define HW_BREAKPOINT_NUM 6 +static inline void arch_kgdb_breakpoint(void) +{ + asm(" EXCPT 2;"); +} +#define BREAK_INSTR_SIZE 2 +#define CACHE_FLUSH_IS_SAFE 1 +#define HW_INST_WATCHPOINT_NUM 6 +#define HW_WATCHPOINT_NUM 8 +#define TYPE_INST_WATCHPOINT 0 +#define TYPE_DATA_WATCHPOINT 1 /* Instruction watchpoint address control register bits mask */ #define WPPWR 0x1 @@ -163,10 +170,11 @@ enum regnames { #define WPDAEN1 0x8 #define WPDCNTEN0 0x10 #define WPDCNTEN1 0x20 + #define WPDSRC0 0xc0 -#define WPDACC0 0x300 +#define WPDACC0_OFFSET 8 #define WPDSRC1 0xc00 -#define WPDACC1 0x3000 +#define WPDACC1_OFFSET 12 /* Watchpoint status register bits mask */ #define STATIA0 0x1 @@ -178,7 +186,4 @@ enum regnames { #define STATDA0 0x40 #define STATDA1 0x80 -extern void kgdb_print(const char *fmt, ...); -extern void init_kgdb_uart(void); - #endif diff --git a/arch/blackfin/include/asm/mmu_context.h b/arch/blackfin/include/asm/mmu_context.h index 8529552a981..35593dda2a4 100644 --- a/arch/blackfin/include/asm/mmu_context.h +++ b/arch/blackfin/include/asm/mmu_context.h @@ -45,49 +45,12 @@ extern unsigned long l1_stack_len; extern int l1sram_free(const void*); extern void *l1sram_alloc_max(void*); -static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) -{ -} - -/* Called when creating a new context during fork() or execve(). */ -static inline int -init_new_context(struct task_struct *tsk, struct mm_struct *mm) -{ -#ifdef CONFIG_MPU - unsigned long p = __get_free_pages(GFP_KERNEL, page_mask_order); - mm->context.page_rwx_mask = (unsigned long *)p; - memset(mm->context.page_rwx_mask, 0, - page_mask_nelts * 3 * sizeof(long)); -#endif - return 0; -} - static inline void free_l1stack(void) { nr_l1stack_tasks--; if (nr_l1stack_tasks == 0) l1sram_free(l1_stack_base); } -static inline void destroy_context(struct mm_struct *mm) -{ - struct sram_list_struct *tmp; - - if (current_l1_stack_save == mm->context.l1_stack_save) - current_l1_stack_save = NULL; - if (mm->context.l1_stack_save) - free_l1stack(); - - while ((tmp = mm->context.sram_list)) { - mm->context.sram_list = tmp->next; - sram_free(tmp->addr); - kfree(tmp); - } -#ifdef CONFIG_MPU - if (current_rwx_mask == mm->context.page_rwx_mask) - current_rwx_mask = NULL; - free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order); -#endif -} static inline unsigned long alloc_l1stack(unsigned long length, unsigned long *stack_base) @@ -134,6 +97,7 @@ static inline void switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_m } #endif +#ifdef CONFIG_APP_STACK_L1 /* L1 stack switching. */ if (!next_mm->context.l1_stack_save) return; @@ -144,6 +108,7 @@ static inline void switch_mm(struct mm_struct *prev_mm, struct mm_struct *next_m } current_l1_stack_save = next_mm->context.l1_stack_save; memcpy(l1_stack_base, current_l1_stack_save, l1_stack_len); +#endif } #ifdef CONFIG_MPU @@ -180,4 +145,44 @@ static inline void update_protections(struct mm_struct *mm) } #endif +static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) +{ +} + +/* Called when creating a new context during fork() or execve(). */ +static inline int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ +#ifdef CONFIG_MPU + unsigned long p = __get_free_pages(GFP_KERNEL, page_mask_order); + mm->context.page_rwx_mask = (unsigned long *)p; + memset(mm->context.page_rwx_mask, 0, + page_mask_nelts * 3 * sizeof(long)); +#endif + return 0; +} + +static inline void destroy_context(struct mm_struct *mm) +{ + struct sram_list_struct *tmp; + +#ifdef CONFIG_APP_STACK_L1 + if (current_l1_stack_save == mm->context.l1_stack_save) + current_l1_stack_save = 0; + if (mm->context.l1_stack_save) + free_l1stack(); +#endif + + while ((tmp = mm->context.sram_list)) { + mm->context.sram_list = tmp->next; + sram_free(tmp->addr); + kfree(tmp); + } +#ifdef CONFIG_MPU + if (current_rwx_mask == mm->context.page_rwx_mask) + current_rwx_mask = NULL; + free_pages((unsigned long)mm->context.page_rwx_mask, page_mask_order); +#endif +} + #endif diff --git a/arch/blackfin/include/asm/processor.h b/arch/blackfin/include/asm/processor.h index 6f3995b119d..e3e9b41fa8d 100644 --- a/arch/blackfin/include/asm/processor.h +++ b/arch/blackfin/include/asm/processor.h @@ -134,6 +134,12 @@ static inline uint32_t __pure bfin_revid(void) return revid; } +static inline uint16_t __pure bfin_cpuid(void) +{ + return (bfin_read_CHIPID() & CHIPID_FAMILY) >> 12; + +} + static inline uint32_t __pure bfin_compiled_revid(void) { #if defined(CONFIG_BF_REV_0_0) diff --git a/arch/blackfin/include/asm/ptrace.h b/arch/blackfin/include/asm/ptrace.h index a45a80e54ad..e3f086dc726 100644 --- a/arch/blackfin/include/asm/ptrace.h +++ b/arch/blackfin/include/asm/ptrace.h @@ -158,6 +158,8 @@ extern void show_regs(struct pt_regs *); #define PT_SEQSTAT 8 #define PT_IPEND 4 +#define PT_ORIG_R0 208 +#define PT_ORIG_P0 212 #define PT_SYSCFG 216 #define PT_TEXT_ADDR 220 #define PT_TEXT_END_ADDR 224 diff --git a/arch/blackfin/include/asm/traps.h b/arch/blackfin/include/asm/traps.h index f0e5f940d9c..34f7295fb07 100644 --- a/arch/blackfin/include/asm/traps.h +++ b/arch/blackfin/include/asm/traps.h @@ -59,6 +59,9 @@ level " or a 16-bit register is accessed with a 32-bit instruction.\n" #define HWC_x3(level) \ "External Memory Addressing Error\n" +#define EXC_0x04(level) \ + "Unimplmented exception occured\n" \ + level " - Maybe you forgot to install a custom exception handler?\n" #define HWC_x12(level) \ "Performance Monitor Overflow\n" #define HWC_x18(level) \ @@ -84,7 +87,7 @@ level " a particular processor implementation.\n" #define EXC_0x22(level) \ "Illegal instruction combination\n" \ - level " - See section for multi-issue rules in the ADSP-BF53x Blackfin\n" \ + level " - See section for multi-issue rules in the Blackfin\n" \ level " Processor Instruction Set Reference.\n" #define EXC_0x23(level) \ "Data access CPLB protection violation\n" \ diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c index 881afe9082c..9bb85dd5ccb 100644 --- a/arch/blackfin/kernel/asm-offsets.c +++ b/arch/blackfin/kernel/asm-offsets.c @@ -60,6 +60,7 @@ int main(void) DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE); /* offsets into the pt_regs */ + DEFINE(PT_ORIG_R0, offsetof(struct pt_regs, orig_r0)); DEFINE(PT_ORIG_P0, offsetof(struct pt_regs, orig_p0)); DEFINE(PT_ORIG_PC, offsetof(struct pt_regs, orig_pc)); DEFINE(PT_R0, offsetof(struct pt_regs, r0)); diff --git a/arch/blackfin/kernel/bfin_dma_5xx.c b/arch/blackfin/kernel/bfin_dma_5xx.c index 93229b3d6e3..339293d677c 100644 --- a/arch/blackfin/kernel/bfin_dma_5xx.c +++ b/arch/blackfin/kernel/bfin_dma_5xx.c @@ -117,15 +117,14 @@ int request_dma(unsigned int channel, char *device_id) #ifdef CONFIG_BF54x if (channel >= CH_UART2_RX && channel <= CH_UART3_TX) { - if (strncmp(device_id, "BFIN_UART", 9) == 0) { - dma_ch[channel].regs->peripheral_map &= 0x0FFF; - dma_ch[channel].regs->peripheral_map |= + unsigned int per_map; + per_map = dma_ch[channel].regs->peripheral_map & 0xFFF; + if (strncmp(device_id, "BFIN_UART", 9) == 0) + dma_ch[channel].regs->peripheral_map = per_map | ((channel - CH_UART2_RX + 0xC)<<12); - } else { - dma_ch[channel].regs->peripheral_map &= 0x0FFF; - dma_ch[channel].regs->peripheral_map |= + else + dma_ch[channel].regs->peripheral_map = per_map | ((channel - CH_UART2_RX + 0x6)<<12); - } } #endif diff --git a/arch/blackfin/kernel/bfin_gpio.c b/arch/blackfin/kernel/bfin_gpio.c index ecbd141e0ef..6e08f425bb4 100644 --- a/arch/blackfin/kernel/bfin_gpio.c +++ b/arch/blackfin/kernel/bfin_gpio.c @@ -231,14 +231,14 @@ inline int check_gpio(unsigned gpio) } #endif -void gpio_error(unsigned gpio) +static void gpio_error(unsigned gpio) { printk(KERN_ERR "bfin-gpio: GPIO %d wasn't requested!\n", gpio); } static void set_label(unsigned short ident, const char *label) { - if (label && str_ident) { + if (label) { strncpy(str_ident[ident].name, label, RESOURCE_LABEL_SIZE); str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; @@ -247,9 +247,6 @@ static void set_label(unsigned short ident, const char *label) static char *get_label(unsigned short ident) { - if (!str_ident) - return "UNKNOWN"; - return (*str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"); } @@ -260,7 +257,7 @@ static int cmp_label(unsigned short ident, const char *label) printk(KERN_ERR "Please provide none-null label\n"); } - if (label && str_ident) + if (label) return strncmp(str_ident[ident].name, label, strlen(label)); else diff --git a/arch/blackfin/kernel/cplb-mpu/cplbinit.c b/arch/blackfin/kernel/cplb-mpu/cplbinit.c index 48060105346..55af729f849 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbinit.c @@ -36,7 +36,7 @@ struct cplb_entry dcplb_tbl[MAX_CPLBS]; int first_switched_icplb, first_switched_dcplb; int first_mask_dcplb; -void __init generate_cpl_tables(void) +void __init generate_cplb_tables(void) { int i_d, i_i; unsigned long addr; @@ -83,8 +83,18 @@ void __init generate_cpl_tables(void) dcplb_tbl[i_d].addr = L1_DATA_A_START; dcplb_tbl[i_d++].data = L1_DMEMORY | PAGE_SIZE_4MB; #endif +#if L1_CODE_LENGTH > 0 icplb_tbl[i_i].addr = L1_CODE_START; icplb_tbl[i_i++].data = L1_IMEMORY | PAGE_SIZE_4MB; +#endif + + /* Cover L2 memory */ +#if L2_LENGTH > 0 + dcplb_tbl[i_d].addr = L2_START; + dcplb_tbl[i_d++].data = L2_DMEMORY | PAGE_SIZE_1MB; + icplb_tbl[i_i].addr = L2_START; + icplb_tbl[i_i++].data = L2_IMEMORY | PAGE_SIZE_1MB; +#endif first_mask_dcplb = i_d; first_switched_dcplb = i_d + (1 << page_mask_order); diff --git a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c index 99f2831e296..5094677fd09 100644 --- a/arch/blackfin/kernel/cplb-mpu/cplbmgr.c +++ b/arch/blackfin/kernel/cplb-mpu/cplbmgr.c @@ -322,9 +322,11 @@ int cplb_hdr(int seqstat, struct pt_regs *regs) void flush_switched_cplbs(void) { int i; + unsigned long flags; nr_cplb_flush++; + local_irq_save(flags); disable_icplb(); for (i = first_switched_icplb; i < MAX_CPLBS; i++) { icplb_tbl[i].data = 0; @@ -338,6 +340,8 @@ void flush_switched_cplbs(void) bfin_write32(DCPLB_DATA0 + i * 4, 0); } enable_dcplb(); + local_irq_restore(flags); + } void set_mask_dcplbs(unsigned long *masks) @@ -345,10 +349,15 @@ void set_mask_dcplbs(unsigned long *masks) int i; unsigned long addr = (unsigned long)masks; unsigned long d_data; - current_rwx_mask = masks; + unsigned long flags; - if (!masks) + if (!masks) { + current_rwx_mask = masks; return; + } + + local_irq_save(flags); + current_rwx_mask = masks; d_data = CPLB_SUPV_WR | CPLB_VALID | CPLB_DIRTY | PAGE_SIZE_4KB; #ifdef CONFIG_BFIN_DCACHE @@ -367,4 +376,5 @@ void set_mask_dcplbs(unsigned long *masks) addr += PAGE_SIZE; } enable_dcplb(); + local_irq_restore(flags); } diff --git a/arch/blackfin/kernel/cplb-nompu/cplbinit.c b/arch/blackfin/kernel/cplb-nompu/cplbinit.c index 728f708d398..301252e8444 100644 --- a/arch/blackfin/kernel/cplb-nompu/cplbinit.c +++ b/arch/blackfin/kernel/cplb-nompu/cplbinit.c @@ -168,8 +168,8 @@ static struct cplb_desc cplb_data[] = { .end = L2_START + L2_LENGTH, .psize = SIZE_1M, .attr = SWITCH_T | I_CPLB | D_CPLB, - .i_conf = L2_MEMORY, - .d_conf = L2_MEMORY, + .i_conf = L2_IMEMORY, + .d_conf = L2_DMEMORY, .valid = (L2_LENGTH > 0), .name = "L2 Memory", }, @@ -308,7 +308,7 @@ __fill_data_cplbtab(struct cplb_tab *t, int i, u32 a_start, u32 a_end) } } -void __init generate_cpl_tables(void) +void __init generate_cplb_tables(void) { u16 i, j, process; diff --git a/arch/blackfin/kernel/early_printk.c b/arch/blackfin/kernel/early_printk.c index 60f67f90fe3..1f4e3d2e090 100644 --- a/arch/blackfin/kernel/early_printk.c +++ b/arch/blackfin/kernel/early_printk.c @@ -35,6 +35,9 @@ extern struct console *bfin_earlyserial_init(unsigned int port, unsigned int cflag); #endif +#ifdef CONFIG_BFIN_JTAG_COMM +extern struct console *bfin_jc_early_init(void); +#endif static struct console *early_console; @@ -142,6 +145,15 @@ int __init setup_early_printk(char *buf) early_console = earlyserial_init(buf); } #endif + +#ifdef CONFIG_BFIN_JTAG_COMM + /* Check for Blackfin JTAG */ + if (!strncmp(buf, "jtag", 4)) { + buf += 4; + early_console = bfin_jc_early_init(); + } +#endif + #ifdef CONFIG_FB /* TODO: add framebuffer console support */ #endif diff --git a/arch/blackfin/kernel/kgdb.c b/arch/blackfin/kernel/kgdb.c index a1f9641a642..b795a207742 100644 --- a/arch/blackfin/kernel/kgdb.c +++ b/arch/blackfin/kernel/kgdb.c @@ -1,32 +1,9 @@ /* - * File: arch/blackfin/kernel/kgdb.c - * Based on: - * Author: Sonic Zhang + * arch/blackfin/kernel/kgdb.c - Blackfin kgdb pieces * - * Created: - * Description: + * Copyright 2005-2008 Analog Devices Inc. * - * Rev: $Id: kgdb_bfin_linux-2.6.x.patch 4934 2007-02-13 09:32:11Z sonicz $ - * - * Modified: - * Copyright 2005-2006 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Licensed under the GPL-2 or later. */ #include <linux/string.h> @@ -39,24 +16,29 @@ #include <linux/kgdb.h> #include <linux/console.h> #include <linux/init.h> -#include <linux/debugger.h> #include <linux/errno.h> #include <linux/irq.h> +#include <linux/uaccess.h> #include <asm/system.h> #include <asm/traps.h> #include <asm/blackfin.h> +#include <asm/dma.h> /* Put the error code here just in case the user cares. */ -int gdb_bf533errcode; +int gdb_bfin_errcode; /* Likewise, the vector number here (since GDB only gets the signal number through the usual means, and that's not very specific). */ -int gdb_bf533vector = -1; +int gdb_bfin_vector = -1; #if KGDB_MAX_NO_CPUS != 8 #error change the definition of slavecpulocks #endif -void regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) +#ifdef CONFIG_BFIN_WDT +# error "Please unselect blackfin watchdog driver before build KGDB." +#endif + +void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) { gdb_regs[BFIN_R0] = regs->r0; gdb_regs[BFIN_R1] = regs->r1; @@ -133,7 +115,7 @@ void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) gdb_regs[BFIN_SEQSTAT] = p->thread.seqstat; } -void gdb_regs_to_regs(unsigned long *gdb_regs, struct pt_regs *regs) +void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) { regs->r0 = gdb_regs[BFIN_R0]; regs->r1 = gdb_regs[BFIN_R1]; @@ -199,171 +181,208 @@ struct hw_breakpoint { unsigned int dataacc:2; unsigned short count; unsigned int addr; -} breakinfo[HW_BREAKPOINT_NUM]; +} breakinfo[HW_WATCHPOINT_NUM]; -int kgdb_arch_init(void) -{ - debugger_step = 0; - - kgdb_remove_all_hw_break(); - return 0; -} - -int kgdb_set_hw_break(unsigned long addr) +int bfin_set_hw_break(unsigned long addr, int len, enum kgdb_bptype type) { int breakno; - for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++) - if (!breakinfo[breakno].occupied) { + int bfin_type; + int dataacc = 0; + + switch (type) { + case BP_HARDWARE_BREAKPOINT: + bfin_type = TYPE_INST_WATCHPOINT; + break; + case BP_WRITE_WATCHPOINT: + dataacc = 1; + bfin_type = TYPE_DATA_WATCHPOINT; + break; + case BP_READ_WATCHPOINT: + dataacc = 2; + bfin_type = TYPE_DATA_WATCHPOINT; + break; + case BP_ACCESS_WATCHPOINT: + dataacc = 3; + bfin_type = TYPE_DATA_WATCHPOINT; + break; + default: + return -ENOSPC; + } + + /* Becasue hardware data watchpoint impelemented in current + * Blackfin can not trigger an exception event as the hardware + * instrction watchpoint does, we ignaore all data watch point here. + * They can be turned on easily after future blackfin design + * supports this feature. + */ + for (breakno = 0; breakno < HW_INST_WATCHPOINT_NUM; breakno++) + if (bfin_type == breakinfo[breakno].type + && !breakinfo[breakno].occupied) { breakinfo[breakno].occupied = 1; breakinfo[breakno].enabled = 1; - breakinfo[breakno].type = 1; breakinfo[breakno].addr = addr; + breakinfo[breakno].dataacc = dataacc; + breakinfo[breakno].count = 0; return 0; } return -ENOSPC; } -int kgdb_remove_hw_break(unsigned long addr) +int bfin_remove_hw_break(unsigned long addr, int len, enum kgdb_bptype type) { int breakno; - for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++) - if (breakinfo[breakno].addr == addr) - memset(&(breakinfo[breakno]), 0, sizeof(struct hw_breakpoint)); + int bfin_type; + + switch (type) { + case BP_HARDWARE_BREAKPOINT: + bfin_type = TYPE_INST_WATCHPOINT; + break; + case BP_WRITE_WATCHPOINT: + case BP_READ_WATCHPOINT: + case BP_ACCESS_WATCHPOINT: + bfin_type = TYPE_DATA_WATCHPOINT; + break; + default: + return 0; + } + for (breakno = 0; breakno < HW_WATCHPOINT_NUM; breakno++) + if (bfin_type == breakinfo[breakno].type + && breakinfo[breakno].occupied + && breakinfo[breakno].addr == addr) { + breakinfo[breakno].occupied = 0; + breakinfo[breakno].enabled = 0; + } return 0; } -void kgdb_remove_all_hw_break(void) +void bfin_remove_all_hw_break(void) { - memset(breakinfo, 0, sizeof(struct hw_breakpoint)*8); -} + int breakno; -/* -void kgdb_show_info(void) -{ - printk(KERN_DEBUG "hwd: wpia0=0x%x, wpiacnt0=%d, wpiactl=0x%x, wpstat=0x%x\n", - bfin_read_WPIA0(), bfin_read_WPIACNT0(), - bfin_read_WPIACTL(), bfin_read_WPSTAT()); + memset(breakinfo, 0, sizeof(struct hw_breakpoint)*HW_WATCHPOINT_NUM); + + for (breakno = 0; breakno < HW_INST_WATCHPOINT_NUM; breakno++) + breakinfo[breakno].type = TYPE_INST_WATCHPOINT; + for (; breakno < HW_WATCHPOINT_NUM; breakno++) + breakinfo[breakno].type = TYPE_DATA_WATCHPOINT; } -*/ -void kgdb_correct_hw_break(void) +void bfin_correct_hw_break(void) { int breakno; - int correctit; - uint32_t wpdactl = bfin_read_WPDACTL(); + unsigned int wpiactl = 0; + unsigned int wpdactl = 0; + int enable_wp = 0; + + for (breakno = 0; breakno < HW_WATCHPOINT_NUM; breakno++) + if (breakinfo[breakno].enabled) { + enable_wp = 1; - correctit = 0; - for (breakno = 0; breakno < HW_BREAKPOINT_NUM; breakno++) { - if (breakinfo[breakno].type == 1) { switch (breakno) { case 0: - if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN0)) { - correctit = 1; - wpdactl &= ~(WPIREN01|EMUSW0); - wpdactl |= WPIAEN0|WPICNTEN0; - bfin_write_WPIA0(breakinfo[breakno].addr); - bfin_write_WPIACNT0(breakinfo[breakno].skip); - } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN0)) { - correctit = 1; - wpdactl &= ~WPIAEN0; - } + wpiactl |= WPIAEN0|WPICNTEN0; + bfin_write_WPIA0(breakinfo[breakno].addr); + bfin_write_WPIACNT0(breakinfo[breakno].count + + breakinfo->skip); break; - case 1: - if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN1)) { - correctit = 1; - wpdactl &= ~(WPIREN01|EMUSW1); - wpdactl |= WPIAEN1|WPICNTEN1; - bfin_write_WPIA1(breakinfo[breakno].addr); - bfin_write_WPIACNT1(breakinfo[breakno].skip); - } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN1)) { - correctit = 1; - wpdactl &= ~WPIAEN1; - } + wpiactl |= WPIAEN1|WPICNTEN1; + bfin_write_WPIA1(breakinfo[breakno].addr); + bfin_write_WPIACNT1(breakinfo[breakno].count + + breakinfo->skip); break; - case 2: - if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN2)) { - correctit = 1; - wpdactl &= ~(WPIREN23|EMUSW2); - wpdactl |= WPIAEN2|WPICNTEN2; - bfin_write_WPIA2(breakinfo[breakno].addr); - bfin_write_WPIACNT2(breakinfo[breakno].skip); - } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN2)) { - correctit = 1; - wpdactl &= ~WPIAEN2; - } + wpiactl |= WPIAEN2|WPICNTEN2; + bfin_write_WPIA2(breakinfo[breakno].addr); + bfin_write_WPIACNT2(breakinfo[breakno].count + + breakinfo->skip); break; - case 3: - if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN3)) { - correctit = 1; - wpdactl &= ~(WPIREN23|EMUSW3); - wpdactl |= WPIAEN3|WPICNTEN3; - bfin_write_WPIA3(breakinfo[breakno].addr); - bfin_write_WPIACNT3(breakinfo[breakno].skip); - } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN3)) { - correctit = 1; - wpdactl &= ~WPIAEN3; - } + wpiactl |= WPIAEN3|WPICNTEN3; + bfin_write_WPIA3(breakinfo[breakno].addr); + bfin_write_WPIACNT3(breakinfo[breakno].count + + breakinfo->skip); break; case 4: - if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN4)) { - correctit = 1; - wpdactl &= ~(WPIREN45|EMUSW4); - wpdactl |= WPIAEN4|WPICNTEN4; - bfin_write_WPIA4(breakinfo[breakno].addr); - bfin_write_WPIACNT4(breakinfo[breakno].skip); - } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN4)) { - correctit = 1; - wpdactl &= ~WPIAEN4; - } + wpiactl |= WPIAEN4|WPICNTEN4; + bfin_write_WPIA4(breakinfo[breakno].addr); + bfin_write_WPIACNT4(breakinfo[breakno].count + + breakinfo->skip); break; case 5: - if (breakinfo[breakno].enabled && !(wpdactl & WPIAEN5)) { - correctit = 1; - wpdactl &= ~(WPIREN45|EMUSW5); - wpdactl |= WPIAEN5|WPICNTEN5; - bfin_write_WPIA5(breakinfo[breakno].addr); - bfin_write_WPIACNT5(breakinfo[breakno].skip); - } else if (!breakinfo[breakno].enabled && (wpdactl & WPIAEN5)) { - correctit = 1; - wpdactl &= ~WPIAEN5; - } + wpiactl |= WPIAEN5|WPICNTEN5; + bfin_write_WPIA5(breakinfo[breakno].addr); + bfin_write_WPIACNT5(breakinfo[breakno].count + + breakinfo->skip); + break; + case 6: + wpdactl |= WPDAEN0|WPDCNTEN0|WPDSRC0; + wpdactl |= breakinfo[breakno].dataacc + << WPDACC0_OFFSET; + bfin_write_WPDA0(breakinfo[breakno].addr); + bfin_write_WPDACNT0(breakinfo[breakno].count + + breakinfo->skip); + break; + case 7: + wpdactl |= WPDAEN1|WPDCNTEN1|WPDSRC1; + wpdactl |= breakinfo[breakno].dataacc + << WPDACC1_OFFSET; + bfin_write_WPDA1(breakinfo[breakno].addr); + bfin_write_WPDACNT1(breakinfo[breakno].count + + breakinfo->skip); break; } } - } - if (correctit) { - wpdactl &= ~WPAND; - wpdactl |= WPPWR; - /*printk("correct_hw_break: wpdactl=0x%x\n", wpdactl);*/ + + /* Should enable WPPWR bit first before set any other + * WPIACTL and WPDACTL bits */ + if (enable_wp) { + bfin_write_WPIACTL(WPPWR); + CSYNC(); + bfin_write_WPIACTL(wpiactl|WPPWR); bfin_write_WPDACTL(wpdactl); CSYNC(); - /*kgdb_show_info();*/ } } void kgdb_disable_hw_debug(struct pt_regs *regs) { /* Disable hardware debugging while we are in kgdb */ - bfin_write_WPIACTL(bfin_read_WPIACTL() & ~0x1); + bfin_write_WPIACTL(0); + bfin_write_WPDACTL(0); CSYNC(); } -void kgdb_post_master_code(struct pt_regs *regs, int eVector, int err_code) +#ifdef CONFIG_SMP +void kgdb_passive_cpu_callback(void *info) +{ + kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + smp_call_function(kgdb_passive_cpu_callback, NULL, 0, 0); +} + +void kgdb_roundup_cpu(int cpu, unsigned long flags) +{ + smp_call_function_single(cpu, kgdb_passive_cpu_callback, NULL, 0, 0); +} +#endif + +void kgdb_post_primary_code(struct pt_regs *regs, int eVector, int err_code) { /* Master processor is completely in the debugger */ - gdb_bf533vector = eVector; - gdb_bf533errcode = err_code; + gdb_bfin_vector = eVector; + gdb_bfin_errcode = err_code; } -int kgdb_arch_handle_exception(int exceptionVector, int signo, +int kgdb_arch_handle_exception(int vector, int signo, int err_code, char *remcom_in_buffer, char *remcom_out_buffer, - struct pt_regs *linux_regs) + struct pt_regs *regs) { long addr; long breakno; @@ -385,44 +404,40 @@ int kgdb_arch_handle_exception(int exceptionVector, int signo, /* try to read optional parameter, pc unchanged if no parm */ ptr = &remcom_in_buffer[1]; if (kgdb_hex2long(&ptr, &addr)) { - linux_regs->retx = addr; + regs->retx = addr; } - newPC = linux_regs->retx; + newPC = regs->retx; /* clear the trace bit */ - linux_regs->syscfg &= 0xfffffffe; + regs->syscfg &= 0xfffffffe; /* set the trace bit if we're stepping */ if (remcom_in_buffer[0] == 's') { - linux_regs->syscfg |= 0x1; - debugger_step = linux_regs->ipend; - debugger_step >>= 6; - for (i = 10; i > 0; i--, debugger_step >>= 1) - if (debugger_step & 1) + regs->syscfg |= 0x1; + kgdb_single_step = regs->ipend; + kgdb_single_step >>= 6; + for (i = 10; i > 0; i--, kgdb_single_step >>= 1) + if (kgdb_single_step & 1) break; /* i indicate event priority of current stopped instruction * user space instruction is 0, IVG15 is 1, IVTMR is 10. - * debugger_step > 0 means in single step mode + * kgdb_single_step > 0 means in single step mode */ - debugger_step = i + 1; - } else { - debugger_step = 0; + kgdb_single_step = i + 1; } - wp_status = bfin_read_WPSTAT(); - CSYNC(); - - if (exceptionVector == VEC_WATCH) { - for (breakno = 0; breakno < 6; ++breakno) { + if (vector == VEC_WATCH) { + wp_status = bfin_read_WPSTAT(); + for (breakno = 0; breakno < HW_WATCHPOINT_NUM; breakno++) { if (wp_status & (1 << breakno)) { breakinfo->skip = 1; break; } } + bfin_write_WPSTAT(0); } - kgdb_correct_hw_break(); - bfin_write_WPSTAT(0); + bfin_correct_hw_break(); return 0; } /* switch */ @@ -431,5 +446,385 @@ int kgdb_arch_handle_exception(int exceptionVector, int signo, struct kgdb_arch arch_kgdb_ops = { .gdb_bpt_instr = {0xa1}, +#ifdef CONFIG_SMP + .flags = KGDB_HW_BREAKPOINT|KGDB_THR_PROC_SWAP, +#else .flags = KGDB_HW_BREAKPOINT, +#endif + .set_hw_breakpoint = bfin_set_hw_break, + .remove_hw_breakpoint = bfin_remove_hw_break, + .remove_all_hw_break = bfin_remove_all_hw_break, + .correct_hw_break = bfin_correct_hw_break, }; + +static int hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return ch - 'a' + 10; + if ((ch >= '0') && (ch <= '9')) + return ch - '0'; + if ((ch >= 'A') && (ch <= 'F')) + return ch - 'A' + 10; + return -1; +} + +static int validate_memory_access_address(unsigned long addr, int size) +{ + int cpu = raw_smp_processor_id(); + + if (size < 0) + return EFAULT; + if (addr >= 0x1000 && (addr + size) <= physical_mem_end) + return 0; + if (addr >= SYSMMR_BASE) + return 0; + if (addr >= ASYNC_BANK0_BASE + && addr + size <= ASYNC_BANK3_BASE + ASYNC_BANK3_SIZE) + return 0; + if (cpu == 0) { + if (addr >= L1_SCRATCH_START + && (addr + size <= L1_SCRATCH_START + L1_SCRATCH_LENGTH)) + return 0; +#if L1_CODE_LENGTH != 0 + if (addr >= L1_CODE_START + && (addr + size <= L1_CODE_START + L1_CODE_LENGTH)) + return 0; +#endif +#if L1_DATA_A_LENGTH != 0 + if (addr >= L1_DATA_A_START + && (addr + size <= L1_DATA_A_START + L1_DATA_A_LENGTH)) + return 0; +#endif +#if L1_DATA_B_LENGTH != 0 + if (addr >= L1_DATA_B_START + && (addr + size <= L1_DATA_B_START + L1_DATA_B_LENGTH)) + return 0; +#endif +#ifdef CONFIG_SMP + } else if (cpu == 1) { + if (addr >= COREB_L1_SCRATCH_START + && (addr + size <= COREB_L1_SCRATCH_START + + L1_SCRATCH_LENGTH)) + return 0; +# if L1_CODE_LENGTH != 0 + if (addr >= COREB_L1_CODE_START + && (addr + size <= COREB_L1_CODE_START + L1_CODE_LENGTH)) + return 0; +# endif +# if L1_DATA_A_LENGTH != 0 + if (addr >= COREB_L1_DATA_A_START + && (addr + size <= COREB_L1_DATA_A_START + L1_DATA_A_LENGTH)) + return 0; +# endif +# if L1_DATA_B_LENGTH != 0 + if (addr >= COREB_L1_DATA_B_START + && (addr + size <= COREB_L1_DATA_B_START + L1_DATA_B_LENGTH)) + return 0; +# endif +#endif + } + +#if L2_LENGTH != 0 + if (addr >= L2_START + && addr + size <= L2_START + L2_LENGTH) + return 0; +#endif + + return EFAULT; +} + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null). May return an error. + */ +int kgdb_mem2hex(char *mem, char *buf, int count) +{ + char *tmp; + int err = 0; + unsigned char *pch; + unsigned short mmr16; + unsigned long mmr32; + int cpu = raw_smp_processor_id(); + + if (validate_memory_access_address((unsigned long)mem, count)) + return EFAULT; + + /* + * We use the upper half of buf as an intermediate buffer for the + * raw memory copy. Hex conversion will work against this one. + */ + tmp = buf + count; + + if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/ + switch (count) { + case 2: + if ((unsigned int)mem % 2 == 0) { + mmr16 = *(unsigned short *)mem; + pch = (unsigned char *)&mmr16; + *tmp++ = *pch++; + *tmp++ = *pch++; + tmp -= 2; + } else + err = EFAULT; + break; + case 4: + if ((unsigned int)mem % 4 == 0) { + mmr32 = *(unsigned long *)mem; + pch = (unsigned char *)&mmr32; + *tmp++ = *pch++; + *tmp++ = *pch++; + *tmp++ = *pch++; + *tmp++ = *pch++; + tmp -= 4; + } else + err = EFAULT; + break; + default: + err = EFAULT; + } + } else if (cpu == 0 && (unsigned int)mem >= L1_CODE_START && + (unsigned int)(mem + count) <= L1_CODE_START + L1_CODE_LENGTH +#ifdef CONFIG_SMP + || cpu == 1 && (unsigned int)mem >= COREB_L1_CODE_START && + (unsigned int)(mem + count) <= + COREB_L1_CODE_START + L1_CODE_LENGTH +#endif + ) { + /* access L1 instruction SRAM*/ + if (dma_memcpy(tmp, mem, count) == NULL) + err = EFAULT; + } else + err = probe_kernel_read(tmp, mem, count); + + if (!err) { + while (count > 0) { + buf = pack_hex_byte(buf, *tmp); + tmp++; + count--; + } + + *buf = 0; + } + + return err; +} + +/* + * Copy the binary array pointed to by buf into mem. Fix $, #, and + * 0x7d escaped with 0x7d. Return a pointer to the character after + * the last byte written. + */ +int kgdb_ebin2mem(char *buf, char *mem, int count) +{ + char *tmp_old; + char *tmp_new; + unsigned short *mmr16; + unsigned long *mmr32; + int err = 0; + int size = 0; + int cpu = raw_smp_processor_id(); + + tmp_old = tmp_new = buf; + + while (count-- > 0) { + if (*tmp_old == 0x7d) + *tmp_new = *(++tmp_old) ^ 0x20; + else + *tmp_new = *tmp_old; + tmp_new++; + tmp_old++; + size++; + } + + if (validate_memory_access_address((unsigned long)mem, size)) + return EFAULT; + + if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/ + switch (size) { + case 2: + if ((unsigned int)mem % 2 == 0) { + mmr16 = (unsigned short *)buf; + *(unsigned short *)mem = *mmr16; + } else + return EFAULT; + break; + case 4: + if ((unsigned int)mem % 4 == 0) { + mmr32 = (unsigned long *)buf; + *(unsigned long *)mem = *mmr32; + } else + return EFAULT; + break; + default: + return EFAULT; + } + } else if (cpu == 0 && (unsigned int)mem >= L1_CODE_START && + (unsigned int)(mem + count) < L1_CODE_START + L1_CODE_LENGTH +#ifdef CONFIG_SMP + || cpu == 1 && (unsigned int)mem >= COREB_L1_CODE_START && + (unsigned int)(mem + count) <= + COREB_L1_CODE_START + L1_CODE_LENGTH +#endif + ) { + /* access L1 instruction SRAM */ + if (dma_memcpy(mem, buf, size) == NULL) + err = EFAULT; + } else + err = probe_kernel_write(mem, buf, size); + + return err; +} + +/* + * Convert the hex array pointed to by buf into binary to be placed in mem. + * Return a pointer to the character AFTER the last byte written. + * May return an error. + */ +int kgdb_hex2mem(char *buf, char *mem, int count) +{ + char *tmp_raw; + char *tmp_hex; + unsigned short *mmr16; + unsigned long *mmr32; + int cpu = raw_smp_processor_id(); + + if (validate_memory_access_address((unsigned long)mem, count)) + return EFAULT; + + /* + * We use the upper half of buf as an intermediate buffer for the + * raw memory that is converted from hex. + */ + tmp_raw = buf + count * 2; + + tmp_hex = tmp_raw - 1; + while (tmp_hex >= buf) { + tmp_raw--; + *tmp_raw = hex(*tmp_hex--); + *tmp_raw |= hex(*tmp_hex--) << 4; + } + + if ((unsigned int)mem >= SYSMMR_BASE) { /*access MMR registers*/ + switch (count) { + case 2: + if ((unsigned int)mem % 2 == 0) { + mmr16 = (unsigned short *)tmp_raw; + *(unsigned short *)mem = *mmr16; + } else + return EFAULT; + break; + case 4: + if ((unsigned int)mem % 4 == 0) { + mmr32 = (unsigned long *)tmp_raw; + *(unsigned long *)mem = *mmr32; + } else + return EFAULT; + break; + default: + return EFAULT; + } + } else if (cpu == 0 && (unsigned int)mem >= L1_CODE_START && + (unsigned int)(mem + count) <= L1_CODE_START + L1_CODE_LENGTH +#ifdef CONFIG_SMP + || cpu == 1 && (unsigned int)mem >= COREB_L1_CODE_START && + (unsigned int)(mem + count) <= + COREB_L1_CODE_START + L1_CODE_LENGTH +#endif + ) { + /* access L1 instruction SRAM */ + if (dma_memcpy(mem, tmp_raw, count) == NULL) + return EFAULT; + } else + return probe_kernel_write(mem, tmp_raw, count); + return 0; +} + +int kgdb_validate_break_address(unsigned long addr) +{ + int cpu = raw_smp_processor_id(); + + if (addr >= 0x1000 && (addr + BREAK_INSTR_SIZE) <= physical_mem_end) + return 0; + if (addr >= ASYNC_BANK0_BASE + && addr + BREAK_INSTR_SIZE <= ASYNC_BANK3_BASE + ASYNC_BANK3_BASE) + return 0; +#if L1_CODE_LENGTH != 0 + if (cpu == 0 && addr >= L1_CODE_START + && addr + BREAK_INSTR_SIZE <= L1_CODE_START + L1_CODE_LENGTH) + return 0; +# ifdef CONFIG_SMP + else if (cpu == 1 && addr >= COREB_L1_CODE_START + && addr + BREAK_INSTR_SIZE <= COREB_L1_CODE_START + L1_CODE_LENGTH) + return 0; +# endif +#endif +#if L2_LENGTH != 0 + if (addr >= L2_START + && addr + BREAK_INSTR_SIZE <= L2_START + L2_LENGTH) + return 0; +#endif + + return EFAULT; +} + +int kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) +{ + int err; + int cpu = raw_smp_processor_id(); + + if ((cpu == 0 && (unsigned int)addr >= L1_CODE_START + && (unsigned int)(addr + BREAK_INSTR_SIZE) + < L1_CODE_START + L1_CODE_LENGTH) +#ifdef CONFIG_SMP + || (cpu == 1 && (unsigned int)addr >= COREB_L1_CODE_START + && (unsigned int)(addr + BREAK_INSTR_SIZE) + < COREB_L1_CODE_START + L1_CODE_LENGTH) +#endif + ) { + /* access L1 instruction SRAM */ + if (dma_memcpy(saved_instr, (void *)addr, BREAK_INSTR_SIZE) + == NULL) + return -EFAULT; + + if (dma_memcpy((void *)addr, arch_kgdb_ops.gdb_bpt_instr, + BREAK_INSTR_SIZE) == NULL) + return -EFAULT; + + return 0; + } else { + err = probe_kernel_read(saved_instr, (char *)addr, + BREAK_INSTR_SIZE); + if (err) + return err; + + return probe_kernel_write((char *)addr, + arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE); + } +} + +int kgdb_arch_remove_breakpoint(unsigned long addr, char *bundle) +{ + if ((unsigned int)addr >= L1_CODE_START && + (unsigned int)(addr + BREAK_INSTR_SIZE) < + L1_CODE_START + L1_CODE_LENGTH) { + /* access L1 instruction SRAM */ + if (dma_memcpy((void *)addr, bundle, BREAK_INSTR_SIZE) == NULL) + return -EFAULT; + + return 0; + } else + return probe_kernel_write((char *)addr, + (char *)bundle, BREAK_INSTR_SIZE); +} + +int kgdb_arch_init(void) +{ + kgdb_single_step = 0; + + bfin_remove_all_hw_break(); + return 0; +} + +void kgdb_arch_exit(void) +{ +} diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c index bf1a51d8e60..140bf00e997 100644 --- a/arch/blackfin/kernel/ptrace.c +++ b/arch/blackfin/kernel/ptrace.c @@ -46,7 +46,6 @@ #include <asm/dma.h> #include <asm/fixed_code.h> -#define MAX_SHARED_LIBS 3 #define TEXT_OFFSET 0 /* * does not yet catch signals sent when the child dies. @@ -161,21 +160,32 @@ static inline int is_user_addr_valid(struct task_struct *child, struct vm_list_struct *vml; struct sram_list_struct *sraml; + /* overflow */ + if (start + len < start) + return -EIO; + for (vml = child->mm->context.vmlist; vml; vml = vml->next) - if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end) + if (start >= vml->vma->vm_start && start + len < vml->vma->vm_end) return 0; for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next) if (start >= (unsigned long)sraml->addr - && start + len <= (unsigned long)sraml->addr + sraml->length) + && start + len < (unsigned long)sraml->addr + sraml->length) return 0; - if (start >= FIXED_CODE_START && start + len <= FIXED_CODE_END) + if (start >= FIXED_CODE_START && start + len < FIXED_CODE_END) return 0; return -EIO; } +void ptrace_enable(struct task_struct *child) +{ + unsigned long tmp; + tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS); + put_reg(child, PT_SYSCFG, tmp); +} + /* * Called by kernel/ptrace.c when detaching.. * @@ -192,14 +202,12 @@ void ptrace_disable(struct task_struct *child) long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int ret; - int add = 0; unsigned long __user *datap = (unsigned long __user *)data; switch (request) { /* when I and D space are separate, these will need to be fixed. */ case PTRACE_PEEKDATA: pr_debug("ptrace: PEEKDATA\n"); - add = MAX_SHARED_LIBS * 4; /* space between text and data */ /* fall through */ case PTRACE_PEEKTEXT: /* read word at location addr. */ { @@ -207,40 +215,35 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) int copied; ret = -EIO; - pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + add %d %ld\n", addr, add, - sizeof(data)); - if (is_user_addr_valid(child, addr + add, sizeof(tmp)) < 0) + pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + %ld\n", addr, sizeof(data)); + if (is_user_addr_valid(child, addr, sizeof(tmp)) < 0) break; pr_debug("ptrace: user address is valid\n"); -#if L1_CODE_LENGTH != 0 - if (addr + add >= L1_CODE_START - && addr + add + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) { - safe_dma_memcpy (&tmp, (const void *)(addr + add), sizeof(tmp)); + if (L1_CODE_LENGTH != 0 && addr >= L1_CODE_START + && addr + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) { + safe_dma_memcpy (&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); - } else -#endif -#if L1_DATA_A_LENGTH != 0 - if (addr + add >= L1_DATA_A_START - && addr + add + sizeof(tmp) <= L1_DATA_A_START + L1_DATA_A_LENGTH) { - memcpy(&tmp, (const void *)(addr + add), sizeof(tmp)); + + } else if (L1_DATA_A_LENGTH != 0 && addr >= L1_DATA_A_START + && addr + sizeof(tmp) <= L1_DATA_A_START + L1_DATA_A_LENGTH) { + memcpy(&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); - } else -#endif -#if L1_DATA_B_LENGTH != 0 - if (addr + add >= L1_DATA_B_START - && addr + add + sizeof(tmp) <= L1_DATA_B_START + L1_DATA_B_LENGTH) { - memcpy(&tmp, (const void *)(addr + add), sizeof(tmp)); + + } else if (L1_DATA_B_LENGTH != 0 && addr >= L1_DATA_B_START + && addr + sizeof(tmp) <= L1_DATA_B_START + L1_DATA_B_LENGTH) { + memcpy(&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); - } else -#endif - if (addr + add >= FIXED_CODE_START - && addr + add + sizeof(tmp) <= FIXED_CODE_END) { - memcpy(&tmp, (const void *)(addr + add), sizeof(tmp)); + + } else if (addr >= FIXED_CODE_START + && addr + sizeof(tmp) <= FIXED_CODE_END) { + memcpy(&tmp, (const void *)(addr), sizeof(tmp)); copied = sizeof(tmp); + } else - copied = access_process_vm(child, addr + add, &tmp, + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp); if (copied != sizeof(tmp)) break; @@ -284,47 +287,43 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) /* when I and D space are separate, this will have to be fixed. */ case PTRACE_POKEDATA: - printk(KERN_NOTICE "ptrace: PTRACE_PEEKDATA\n"); + pr_debug("ptrace: PTRACE_PEEKDATA\n"); /* fall through */ case PTRACE_POKETEXT: /* write the word at location addr. */ { int copied; ret = -EIO; - pr_debug("ptrace: POKETEXT at addr 0x%08lx + add %d %ld bytes %lx\n", - addr, add, sizeof(data), data); - if (is_user_addr_valid(child, addr + add, sizeof(data)) < 0) + pr_debug("ptrace: POKETEXT at addr 0x%08lx + %ld bytes %lx\n", + addr, sizeof(data), data); + if (is_user_addr_valid(child, addr, sizeof(data)) < 0) break; pr_debug("ptrace: user address is valid\n"); -#if L1_CODE_LENGTH != 0 - if (addr + add >= L1_CODE_START - && addr + add + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) { - safe_dma_memcpy ((void *)(addr + add), &data, sizeof(data)); + if (L1_CODE_LENGTH != 0 && addr >= L1_CODE_START + && addr + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) { + safe_dma_memcpy ((void *)(addr), &data, sizeof(data)); copied = sizeof(data); - } else -#endif -#if L1_DATA_A_LENGTH != 0 - if (addr + add >= L1_DATA_A_START - && addr + add + sizeof(data) <= L1_DATA_A_START + L1_DATA_A_LENGTH) { - memcpy((void *)(addr + add), &data, sizeof(data)); + + } else if (L1_DATA_A_LENGTH != 0 && addr >= L1_DATA_A_START + && addr + sizeof(data) <= L1_DATA_A_START + L1_DATA_A_LENGTH) { + memcpy((void *)(addr), &data, sizeof(data)); copied = sizeof(data); - } else -#endif -#if L1_DATA_B_LENGTH != 0 - if (addr + add >= L1_DATA_B_START - && addr + add + sizeof(data) <= L1_DATA_B_START + L1_DATA_B_LENGTH) { - memcpy((void *)(addr + add), &data, sizeof(data)); + + } else if (L1_DATA_B_LENGTH != 0 && addr >= L1_DATA_B_START + && addr + sizeof(data) <= L1_DATA_B_START + L1_DATA_B_LENGTH) { + memcpy((void *)(addr), &data, sizeof(data)); copied = sizeof(data); - } else -#endif - if (addr + add >= FIXED_CODE_START - && addr + add + sizeof(data) <= FIXED_CODE_END) { - memcpy((void *)(addr + add), &data, sizeof(data)); + + } else if (addr >= FIXED_CODE_START + && addr + sizeof(data) <= FIXED_CODE_END) { + memcpy((void *)(addr), &data, sizeof(data)); copied = sizeof(data); + } else - copied = access_process_vm(child, addr + add, &data, + copied = access_process_vm(child, addr, &data, sizeof(data), 1); + pr_debug("ptrace: copied size %d\n", copied); if (copied != sizeof(data)) break; @@ -351,29 +350,22 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) break; case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ - case PTRACE_CONT: - { /* restart after signal. */ - long tmp; + case PTRACE_CONT: /* restart after signal. */ + pr_debug("ptrace: syscall/cont\n"); - pr_debug("ptrace_cont\n"); - - ret = -EIO; - if (!valid_signal(data)) - break; - if (request == PTRACE_SYSCALL) - set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - else - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - child->exit_code = data; - /* make sure the single step bit is not set. */ - tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS); - put_reg(child, PT_SYSCFG, tmp); - pr_debug("before wake_up_process\n"); - wake_up_process(child); - ret = 0; + ret = -EIO; + if (!valid_signal(data)) break; - } + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + ptrace_disable(child); + pr_debug("ptrace: before wake_up_process\n"); + wake_up_process(child); + ret = 0; + break; /* * make the child exit. Best I can do is send it a sigkill. @@ -381,55 +373,37 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) * exit. */ case PTRACE_KILL: - { - long tmp; - ret = 0; - if (child->exit_state == EXIT_ZOMBIE) /* already dead */ - break; - child->exit_code = SIGKILL; - /* make sure the single step bit is not set. */ - tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS); - put_reg(child, PT_SYSCFG, tmp); - wake_up_process(child); + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ break; - } - - case PTRACE_SINGLESTEP: - { /* set the trap flag. */ - long tmp; - - pr_debug("single step\n"); - ret = -EIO; - if (!valid_signal(data)) - break; - clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); - - tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS); - put_reg(child, PT_SYSCFG, tmp); + child->exit_code = SIGKILL; + ptrace_disable(child); + wake_up_process(child); + break; - child->exit_code = data; - /* give it a chance to run. */ - wake_up_process(child); - ret = 0; + case PTRACE_SINGLESTEP: /* set the trap flag. */ + pr_debug("ptrace: single step\n"); + ret = -EIO; + if (!valid_signal(data)) break; - } + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + ptrace_enable(child); + child->exit_code = data; + wake_up_process(child); + ret = 0; + break; case PTRACE_GETREGS: - { - - /* Get all gp regs from the child. */ - ret = ptrace_getregs(child, datap); - break; - } + /* Get all gp regs from the child. */ + ret = ptrace_getregs(child, datap); + break; case PTRACE_SETREGS: - { - printk(KERN_NOTICE - "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n"); - /* Set all gp regs in the child. */ - ret = 0; - break; - } + printk(KERN_WARNING "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n"); + /* Set all gp regs in the child. */ + ret = 0; + break; + default: ret = ptrace_request(child, request, addr, data); break; @@ -440,7 +414,6 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) asmlinkage void syscall_trace(void) { - if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; diff --git a/arch/blackfin/kernel/reboot.c b/arch/blackfin/kernel/reboot.c index 367e2dc0988..ae97ca407b0 100644 --- a/arch/blackfin/kernel/reboot.c +++ b/arch/blackfin/kernel/reboot.c @@ -10,6 +10,7 @@ #include <asm/bfin-global.h> #include <asm/reboot.h> #include <asm/system.h> +#include <asm/bfrom.h> /* A system soft reset makes external memory unusable so force * this function into L1. We use the compiler ssync here rather @@ -20,7 +21,7 @@ * the core reset. */ __attribute__((l1_text)) -void bfin_reset(void) +static void bfin_reset(void) { /* Wait for completion of "system" events such as cache line * line fills so that we avoid infinite stalls later on as @@ -34,15 +35,15 @@ void bfin_reset(void) bfin_write_SWRST(0x7); /* Due to the way reset is handled in the hardware, we need - * to delay for 7 SCLKS. The only reliable way to do this is - * to calculate the CCLK/SCLK ratio and multiply 7. For now, + * to delay for 10 SCLKS. The only reliable way to do this is + * to calculate the CCLK/SCLK ratio and multiply 10. For now, * we'll assume worse case which is a 1:15 ratio. */ asm( "LSETUP (1f, 1f) LC0 = %0\n" "1: nop;" : - : "a" (15 * 7) + : "a" (15 * 10) : "LC0", "LB0", "LT0" ); @@ -74,7 +75,14 @@ void machine_restart(char *cmd) { native_machine_restart(cmd); local_irq_disable(); - bfin_reset(); + if (ANOMALY_05000353 || ANOMALY_05000386) + bfin_reset(); + else + /* the bootrom checks to see how it was reset and will + * automatically perform a software reset for us when + * it starts executing boot + */ + asm("raise 1;"); } __attribute__((weak)) diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c index 7a82d10b4eb..7f35d1046cd 100644 --- a/arch/blackfin/kernel/setup.c +++ b/arch/blackfin/kernel/setup.c @@ -42,6 +42,7 @@ EXPORT_SYMBOL(memory_start); EXPORT_SYMBOL(memory_end); EXPORT_SYMBOL(physical_mem_end); EXPORT_SYMBOL(_ramend); +EXPORT_SYMBOL(reserved_mem_dcache_on); #ifdef CONFIG_MTD_UCLINUX unsigned long memory_mtd_end, memory_mtd_start, mtd_size; @@ -52,7 +53,8 @@ EXPORT_SYMBOL(mtd_size); #endif char __initdata command_line[COMMAND_LINE_SIZE]; -unsigned int __initdata *__retx; +void __initdata *init_retx, *init_saved_retx, *init_saved_seqstat, + *init_saved_icplb_fault_addr, *init_saved_dcplb_fault_addr; /* boot memmap, for parsing "memmap=" */ #define BFIN_MEMMAP_MAX 128 /* number of entries in bfin_memmap */ @@ -77,10 +79,10 @@ static struct change_member *change_point[2*BFIN_MEMMAP_MAX] __initdata; static struct bfin_memmap_entry *overlap_list[BFIN_MEMMAP_MAX] __initdata; static struct bfin_memmap_entry new_map[BFIN_MEMMAP_MAX] __initdata; -void __init bf53x_cache_init(void) +void __init bfin_cache_init(void) { #if defined(CONFIG_BFIN_DCACHE) || defined(CONFIG_BFIN_ICACHE) - generate_cpl_tables(); + generate_cplb_tables(); #endif #ifdef CONFIG_BFIN_ICACHE @@ -100,7 +102,7 @@ void __init bf53x_cache_init(void) #endif } -void __init bf53x_relocate_l1_mem(void) +void __init bfin_relocate_l1_mem(void) { unsigned long l1_code_length; unsigned long l1_data_a_length; @@ -410,7 +412,7 @@ static __init void parse_cmdline_early(char *cmdline_p) * [_rambase, _ramstart]: kernel image * [memory_start, memory_end]: dynamic memory managed by kernel * [memory_end, _ramend]: reserved memory - * [meory_mtd_start(memory_end), + * [memory_mtd_start(memory_end), * memory_mtd_start + mtd_size]: rootfs (if any) * [_ramend - DMA_UNCACHED_REGION, * _ramend]: uncached DMA region @@ -782,16 +784,25 @@ void __init setup_arch(char **cmdline_p) _bfin_swrst = bfin_read_SWRST(); - /* If we double fault, reset the system - otherwise we hang forever */ - bfin_write_SWRST(DOUBLE_FAULT); +#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT + bfin_write_SWRST(_bfin_swrst & ~DOUBLE_FAULT); +#endif +#ifdef CONFIG_DEBUG_DOUBLEFAULT_RESET + bfin_write_SWRST(_bfin_swrst | DOUBLE_FAULT); +#endif - if (_bfin_swrst & RESET_DOUBLE) - /* - * don't decode the address, since you don't know if this - * kernel's symbol map is the same as the crashing kernel - */ - printk(KERN_INFO "Recovering from Double Fault event at %pF\n", __retx); - else if (_bfin_swrst & RESET_WDOG) + if (_bfin_swrst & RESET_DOUBLE) { + printk(KERN_EMERG "Recovering from DOUBLE FAULT event\n"); +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* We assume the crashing kernel, and the current symbol table match */ + printk(KERN_EMERG " While handling exception (EXCAUSE = 0x%x) at %pF\n", + (int)init_saved_seqstat & SEQSTAT_EXCAUSE, init_saved_retx); + printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %pF\n", init_saved_dcplb_fault_addr); + printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %pF\n", init_saved_icplb_fault_addr); +#endif + printk(KERN_NOTICE " The instruction at %pF caused a double exception\n", + init_retx); + } else if (_bfin_swrst & RESET_WDOG) printk(KERN_INFO "Recovering from Watchdog event\n"); else if (_bfin_swrst & RESET_SOFTWARE) printk(KERN_NOTICE "Reset caused by Software reset\n"); @@ -803,17 +814,24 @@ void __init setup_arch(char **cmdline_p) printk(KERN_INFO "Compiled for ADSP-%s Rev none\n", CPU); else printk(KERN_INFO "Compiled for ADSP-%s Rev 0.%d\n", CPU, bfin_compiled_revid()); - if (bfin_revid() != bfin_compiled_revid()) { - if (bfin_compiled_revid() == -1) - printk(KERN_ERR "Warning: Compiled for Rev none, but running on Rev %d\n", - bfin_revid()); - else if (bfin_compiled_revid() != 0xffff) - printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n", - bfin_compiled_revid(), bfin_revid()); + + if (unlikely(CPUID != bfin_cpuid())) + printk(KERN_ERR "ERROR: Not running on ADSP-%s: unknown CPUID 0x%04x Rev 0.%d\n", + CPU, bfin_cpuid(), bfin_revid()); + else { + if (bfin_revid() != bfin_compiled_revid()) { + if (bfin_compiled_revid() == -1) + printk(KERN_ERR "Warning: Compiled for Rev none, but running on Rev %d\n", + bfin_revid()); + else if (bfin_compiled_revid() != 0xffff) + printk(KERN_ERR "Warning: Compiled for Rev %d, but running on Rev %d\n", + bfin_compiled_revid(), bfin_revid()); + } + if (bfin_revid() <= CONFIG_BF_REV_MIN || bfin_revid() > CONFIG_BF_REV_MAX) + printk(KERN_ERR "Warning: Unsupported Chip Revision ADSP-%s Rev 0.%d detected\n", + CPU, bfin_revid()); } - if (bfin_revid() < SUPPORTED_REVID) - printk(KERN_ERR "Warning: Unsupported Chip Revision ADSP-%s Rev 0.%d detected\n", - CPU, bfin_revid()); + printk(KERN_INFO "Blackfin Linux support by http://blackfin.uclinux.org/\n"); printk(KERN_INFO "Processor Speed: %lu MHz core clock and %lu MHz System Clock\n", @@ -850,7 +868,7 @@ void __init setup_arch(char **cmdline_p) != SAFE_USER_INSTRUCTION - FIXED_CODE_START); init_exception_vectors(); - bf53x_cache_init(); + bfin_cache_init(); } static int __init topology_init(void) @@ -986,13 +1004,18 @@ static int show_cpuinfo(struct seq_file *m, void *v) } seq_printf(m, "processor\t: %d\n" - "vendor_id\t: %s\n" - "cpu family\t: 0x%x\n" - "model name\t: ADSP-%s %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n" + "vendor_id\t: %s\n", + *(unsigned int *)v, + vendor); + + if (CPUID == bfin_cpuid()) + seq_printf(m, "cpu family\t: 0x%04x\n", CPUID); + else + seq_printf(m, "cpu family\t: Compiled for:0x%04x, running on:0x%04x\n", + CPUID, bfin_cpuid()); + + seq_printf(m, "model name\t: ADSP-%s %lu(MHz CCLK) %lu(MHz SCLK) (%s)\n" "stepping\t: %d\n", - 0, - vendor, - (bfin_read_CHIPID() & CHIPID_FAMILY), cpu, cclk/1000000, sclk/1000000, #ifdef CONFIG_MPU "mpu on", @@ -1038,7 +1061,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) if ((bfin_read_DMEM_CONTROL() & (ENDCPLB | DMC_ENABLE)) != (ENDCPLB | DMC_ENABLE)) dcache_size = 0; - if ((bfin_read_IMEM_CONTROL() & (IMC | ENICPLB)) == (IMC | ENICPLB)) + if ((bfin_read_IMEM_CONTROL() & (IMC | ENICPLB)) != (IMC | ENICPLB)) icache_size = 0; seq_printf(m, "cache size\t: %d KB(L1 icache) " @@ -1127,12 +1150,18 @@ static int show_cpuinfo(struct seq_file *m, void *v) static void *c_start(struct seq_file *m, loff_t *pos) { - return *pos < NR_CPUS ? ((void *)0x12345678) : NULL; + if (*pos == 0) + *pos = first_cpu(cpu_online_map); + if (*pos >= num_online_cpus()) + return NULL; + + return pos; } static void *c_next(struct seq_file *m, void *v, loff_t *pos) { - ++*pos; + *pos = next_cpu(*pos, cpu_online_map); + return c_start(m, pos); } diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index 9a9d5083acf..1aa2c788e22 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -34,20 +34,19 @@ #include <linux/fs.h> #include <asm/traps.h> #include <asm/cacheflush.h> +#include <asm/cplb.h> #include <asm/blackfin.h> #include <asm/irq_handler.h> #include <linux/irq.h> #include <asm/trace.h> #include <asm/fixed_code.h> -#include <asm/dma.h> #ifdef CONFIG_KGDB -# include <linux/debugger.h> # include <linux/kgdb.h> # define CHK_DEBUGGER_TRAP() \ do { \ - CHK_DEBUGGER(trapnr, sig, info.si_code, fp, ); \ + kgdb_handle_exception(trapnr, sig, info.si_code, fp); \ } while (0) # define CHK_DEBUGGER_TRAP_MAYBE() \ do { \ @@ -59,6 +58,15 @@ # define CHK_DEBUGGER_TRAP_MAYBE() do { } while (0) #endif + +#ifdef CONFIG_VERBOSE_DEBUG +#define verbose_printk(fmt, arg...) \ + printk(fmt, ##arg) +#else +#define verbose_printk(fmt, arg...) \ + ({ if (0) printk(fmt, ##arg); 0; }) +#endif + /* Initiate the event table handler */ void __init trap_init(void) { @@ -67,10 +75,19 @@ void __init trap_init(void) CSYNC(); } -unsigned long saved_icplb_fault_addr, saved_dcplb_fault_addr; +/* + * Used to save the RETX, SEQSTAT, I/D CPLB FAULT ADDR + * values across the transition from exception to IRQ5. + * We put these in L1, so they are going to be in a valid + * location during exception context + */ +__attribute__((l1_data)) +unsigned long saved_retx, saved_seqstat, + saved_icplb_fault_addr, saved_dcplb_fault_addr; static void decode_address(char *buf, unsigned long address) { +#ifdef CONFIG_DEBUG_VERBOSE struct vm_list_struct *vml; struct task_struct *p; struct mm_struct *mm; @@ -178,16 +195,39 @@ static void decode_address(char *buf, unsigned long address) done: write_unlock_irqrestore(&tasklist_lock, flags); +#else + sprintf(buf, " "); +#endif } asmlinkage void double_fault_c(struct pt_regs *fp) { console_verbose(); oops_in_progress = 1; +#ifdef CONFIG_DEBUG_VERBOSE printk(KERN_EMERG "\n" KERN_EMERG "Double Fault\n"); - dump_bfin_process(fp); - dump_bfin_mem(fp); - show_regs(fp); +#ifdef CONFIG_DEBUG_DOUBLEFAULT_PRINT + if (((long)fp->seqstat & SEQSTAT_EXCAUSE) == VEC_UNCOV) { + char buf[150]; + decode_address(buf, saved_retx); + printk(KERN_EMERG "While handling exception (EXCAUSE = 0x%x) at %s:\n", + (int)saved_seqstat & SEQSTAT_EXCAUSE, buf); + decode_address(buf, saved_dcplb_fault_addr); + printk(KERN_NOTICE " DCPLB_FAULT_ADDR: %s\n", buf); + decode_address(buf, saved_icplb_fault_addr); + printk(KERN_NOTICE " ICPLB_FAULT_ADDR: %s\n", buf); + + decode_address(buf, fp->retx); + printk(KERN_NOTICE "The instruction at %s caused a double exception\n", + buf); + } else +#endif + { + dump_bfin_process(fp); + dump_bfin_mem(fp); + show_regs(fp); + } +#endif panic("Double Fault - unrecoverable event\n"); } @@ -259,34 +299,42 @@ asmlinkage void trap_c(struct pt_regs *fp) return; else break; + /* 0x03 - User Defined, userspace stack overflow */ + case VEC_EXCPT03: + info.si_code = SEGV_STACKFLOW; + sig = SIGSEGV; + verbose_printk(KERN_NOTICE EXC_0x03(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); + break; + /* 0x02 - KGDB initial connection and break signal trap */ + case VEC_EXCPT02: #ifdef CONFIG_KGDB - case VEC_EXCPT02 : /* gdb connection */ info.si_code = TRAP_ILLTRAP; sig = SIGTRAP; CHK_DEBUGGER_TRAP(); return; -#else - /* 0x02 - User Defined, Caught by default */ #endif - /* 0x03 - User Defined, userspace stack overflow */ - case VEC_EXCPT03: - info.si_code = SEGV_STACKFLOW; - sig = SIGSEGV; - printk(KERN_NOTICE EXC_0x03(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + /* 0x04 - User Defined */ + /* 0x05 - User Defined */ + /* 0x06 - User Defined */ + /* 0x07 - User Defined */ + /* 0x08 - User Defined */ + /* 0x09 - User Defined */ + /* 0x0A - User Defined */ + /* 0x0B - User Defined */ + /* 0x0C - User Defined */ + /* 0x0D - User Defined */ + /* 0x0E - User Defined */ + /* 0x0F - User Defined */ + /* If we got here, it is most likely that someone was trying to use a + * custom exception handler, and it is not actually installed properly + */ + case VEC_EXCPT04 ... VEC_EXCPT15: + info.si_code = ILL_ILLPARAOP; + sig = SIGILL; + verbose_printk(KERN_NOTICE EXC_0x04(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; - /* 0x04 - User Defined, Caught by default */ - /* 0x05 - User Defined, Caught by default */ - /* 0x06 - User Defined, Caught by default */ - /* 0x07 - User Defined, Caught by default */ - /* 0x08 - User Defined, Caught by default */ - /* 0x09 - User Defined, Caught by default */ - /* 0x0A - User Defined, Caught by default */ - /* 0x0B - User Defined, Caught by default */ - /* 0x0C - User Defined, Caught by default */ - /* 0x0D - User Defined, Caught by default */ - /* 0x0E - User Defined, Caught by default */ - /* 0x0F - User Defined, Caught by default */ /* 0x10 HW Single step, handled here */ case VEC_STEP: info.si_code = TRAP_STEP; @@ -301,8 +349,8 @@ asmlinkage void trap_c(struct pt_regs *fp) case VEC_OVFLOW: info.si_code = TRAP_TRACEFLOW; sig = SIGTRAP; - printk(KERN_NOTICE EXC_0x11(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x11(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x12 - Reserved, Caught by default */ /* 0x13 - Reserved, Caught by default */ @@ -323,44 +371,43 @@ asmlinkage void trap_c(struct pt_regs *fp) case VEC_UNDEF_I: info.si_code = ILL_ILLOPC; sig = SIGILL; - printk(KERN_NOTICE EXC_0x21(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x21(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x22 - Illegal Instruction Combination, handled here */ case VEC_ILGAL_I: info.si_code = ILL_ILLPARAOP; sig = SIGILL; - printk(KERN_NOTICE EXC_0x22(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x22(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x23 - Data CPLB protection violation, handled here */ case VEC_CPLB_VL: info.si_code = ILL_CPLB_VI; sig = SIGBUS; - printk(KERN_NOTICE EXC_0x23(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x23(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x24 - Data access misaligned, handled here */ case VEC_MISALI_D: info.si_code = BUS_ADRALN; sig = SIGBUS; - printk(KERN_NOTICE EXC_0x24(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x24(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x25 - Unrecoverable Event, handled here */ case VEC_UNCOV: info.si_code = ILL_ILLEXCPT; sig = SIGILL; - printk(KERN_NOTICE EXC_0x25(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x25(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x26 - Data CPLB Miss, normal case is handled in _cplb_hdr, error case is handled here */ case VEC_CPLB_M: info.si_code = BUS_ADRALN; sig = SIGBUS; - printk(KERN_NOTICE EXC_0x26(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x26(KERN_NOTICE)); break; /* 0x27 - Data CPLB Multiple Hits - Linux Trap Zero, handled here */ case VEC_CPLB_MHIT: @@ -368,11 +415,11 @@ asmlinkage void trap_c(struct pt_regs *fp) sig = SIGSEGV; #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO if (saved_dcplb_fault_addr < FIXED_CODE_START) - printk(KERN_NOTICE "NULL pointer access\n"); + verbose_printk(KERN_NOTICE "NULL pointer access\n"); else #endif - printk(KERN_NOTICE EXC_0x27(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x27(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x28 - Emulation Watchpoint, handled here */ case VEC_WATCH: @@ -390,8 +437,8 @@ asmlinkage void trap_c(struct pt_regs *fp) case VEC_ISTRU_VL: /* ADSP-BF535 only (MH) */ info.si_code = BUS_OPFETCH; sig = SIGBUS; - printk(KERN_NOTICE "BF535: VEC_ISTRU_VL\n"); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE "BF535: VEC_ISTRU_VL\n"); + CHK_DEBUGGER_TRAP_MAYBE(); break; #else /* 0x29 - Reserved, Caught by default */ @@ -400,22 +447,21 @@ asmlinkage void trap_c(struct pt_regs *fp) case VEC_MISALI_I: info.si_code = BUS_ADRALN; sig = SIGBUS; - printk(KERN_NOTICE EXC_0x2A(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x2A(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x2B - Instruction CPLB protection violation, handled here */ case VEC_CPLB_I_VL: info.si_code = ILL_CPLB_VI; sig = SIGBUS; - printk(KERN_NOTICE EXC_0x2B(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x2B(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x2C - Instruction CPLB miss, handled in _cplb_hdr */ case VEC_CPLB_I_M: info.si_code = ILL_CPLB_MISS; sig = SIGBUS; - printk(KERN_NOTICE EXC_0x2C(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x2C(KERN_NOTICE)); break; /* 0x2D - Instruction CPLB Multiple Hits, handled here */ case VEC_CPLB_I_MHIT: @@ -423,18 +469,18 @@ asmlinkage void trap_c(struct pt_regs *fp) sig = SIGSEGV; #ifdef CONFIG_DEBUG_HUNT_FOR_ZERO if (saved_icplb_fault_addr < FIXED_CODE_START) - printk(KERN_NOTICE "Jump to NULL address\n"); + verbose_printk(KERN_NOTICE "Jump to NULL address\n"); else #endif - printk(KERN_NOTICE EXC_0x2D(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x2D(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x2E - Illegal use of Supervisor Resource, handled here */ case VEC_ILL_RES: info.si_code = ILL_PRVOPC; sig = SIGILL; - printk(KERN_NOTICE EXC_0x2E(KERN_NOTICE)); - CHK_DEBUGGER_TRAP(); + verbose_printk(KERN_NOTICE EXC_0x2E(KERN_NOTICE)); + CHK_DEBUGGER_TRAP_MAYBE(); break; /* 0x2F - Reserved, Caught by default */ /* 0x30 - Reserved, Caught by default */ @@ -461,17 +507,17 @@ asmlinkage void trap_c(struct pt_regs *fp) case (SEQSTAT_HWERRCAUSE_SYSTEM_MMR): info.si_code = BUS_ADRALN; sig = SIGBUS; - printk(KERN_NOTICE HWC_x2(KERN_NOTICE)); + verbose_printk(KERN_NOTICE HWC_x2(KERN_NOTICE)); break; /* External Memory Addressing Error */ case (SEQSTAT_HWERRCAUSE_EXTERN_ADDR): info.si_code = BUS_ADRERR; sig = SIGBUS; - printk(KERN_NOTICE HWC_x3(KERN_NOTICE)); + verbose_printk(KERN_NOTICE HWC_x3(KERN_NOTICE)); break; /* Performance Monitor Overflow */ case (SEQSTAT_HWERRCAUSE_PERF_FLOW): - printk(KERN_NOTICE HWC_x12(KERN_NOTICE)); + verbose_printk(KERN_NOTICE HWC_x12(KERN_NOTICE)); break; /* RAISE 5 instruction */ case (SEQSTAT_HWERRCAUSE_RAISE_5): @@ -481,21 +527,25 @@ asmlinkage void trap_c(struct pt_regs *fp) printk(KERN_NOTICE HWC_default(KERN_NOTICE)); break; } - CHK_DEBUGGER_TRAP(); + CHK_DEBUGGER_TRAP_MAYBE(); break; + /* + * We should be handling all known exception types above, + * if we get here we hit a reserved one, so panic + */ default: - info.si_code = TRAP_ILLTRAP; - sig = SIGTRAP; - printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n", + oops_in_progress = 1; + info.si_code = ILL_ILLPARAOP; + sig = SIGILL; + verbose_printk(KERN_EMERG "Caught Unhandled Exception, code = %08lx\n", (fp->seqstat & SEQSTAT_EXCAUSE)); - CHK_DEBUGGER_TRAP(); + CHK_DEBUGGER_TRAP_MAYBE(); break; } BUG_ON(sig == 0); if (sig != SIGTRAP) { - unsigned long *stack; dump_bfin_process(fp); dump_bfin_mem(fp); show_regs(fp); @@ -503,7 +553,7 @@ asmlinkage void trap_c(struct pt_regs *fp) /* Print out the trace buffer if it makes sense */ #ifndef CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE if (trapnr == VEC_CPLB_I_M || trapnr == VEC_CPLB_M) - printk(KERN_NOTICE "No trace since you do not have " + verbose_printk(KERN_NOTICE "No trace since you do not have " "CONFIG_DEBUG_BFIN_NO_KERN_HWTRACE enabled\n" KERN_NOTICE "\n"); else @@ -512,20 +562,22 @@ asmlinkage void trap_c(struct pt_regs *fp) if (oops_in_progress) { /* Dump the current kernel stack */ - printk(KERN_NOTICE "\n" KERN_NOTICE "Kernel Stack\n"); + verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "Kernel Stack\n"); show_stack(current, NULL); - print_modules(); #ifndef CONFIG_ACCESS_CHECK - printk(KERN_EMERG "Please turn on " + verbose_printk(KERN_EMERG "Please turn on " "CONFIG_ACCESS_CHECK\n"); #endif panic("Kernel exception"); } else { +#ifdef CONFIG_VERBOSE_DEBUG + unsigned long *stack; /* Dump the user space stack */ stack = (unsigned long *)rdusp(); - printk(KERN_NOTICE "Userspace Stack\n"); + verbose_printk(KERN_NOTICE "Userspace Stack\n"); show_stack(NULL, stack); +#endif } } @@ -546,7 +598,7 @@ asmlinkage void trap_c(struct pt_regs *fp) * Similar to get_user, do some address checking, then dereference * Return true on sucess, false on bad address */ -bool get_instruction(unsigned short *val, unsigned short *address) +static bool get_instruction(unsigned short *val, unsigned short *address) { unsigned long addr; @@ -592,7 +644,7 @@ bool get_instruction(unsigned short *val, unsigned short *address) #if L1_CODE_LENGTH != 0 if (addr >= L1_CODE_START && (addr + 2) <= (L1_CODE_START + L1_CODE_LENGTH)) { - dma_memcpy(val, address, 2); + isram_memcpy(val, address, 2); return true; } #endif @@ -607,45 +659,48 @@ bool get_instruction(unsigned short *val, unsigned short *address) * These are the normal instructions which cause change of flow, which * would be at the source of the trace buffer */ -void decode_instruction(unsigned short *address) +#ifdef CONFIG_DEBUG_VERBOSE +static void decode_instruction(unsigned short *address) { unsigned short opcode; if (get_instruction(&opcode, address)) { if (opcode == 0x0010) - printk("RTS"); + verbose_printk("RTS"); else if (opcode == 0x0011) - printk("RTI"); + verbose_printk("RTI"); else if (opcode == 0x0012) - printk("RTX"); + verbose_printk("RTX"); else if (opcode >= 0x0050 && opcode <= 0x0057) - printk("JUMP (P%i)", opcode & 7); + verbose_printk("JUMP (P%i)", opcode & 7); else if (opcode >= 0x0060 && opcode <= 0x0067) - printk("CALL (P%i)", opcode & 7); + verbose_printk("CALL (P%i)", opcode & 7); else if (opcode >= 0x0070 && opcode <= 0x0077) - printk("CALL (PC+P%i)", opcode & 7); + verbose_printk("CALL (PC+P%i)", opcode & 7); else if (opcode >= 0x0080 && opcode <= 0x0087) - printk("JUMP (PC+P%i)", opcode & 7); + verbose_printk("JUMP (PC+P%i)", opcode & 7); else if ((opcode >= 0x1000 && opcode <= 0x13FF) || (opcode >= 0x1800 && opcode <= 0x1BFF)) - printk("IF !CC JUMP"); + verbose_printk("IF !CC JUMP"); else if ((opcode >= 0x1400 && opcode <= 0x17ff) || (opcode >= 0x1c00 && opcode <= 0x1fff)) - printk("IF CC JUMP"); + verbose_printk("IF CC JUMP"); else if (opcode >= 0x2000 && opcode <= 0x2fff) - printk("JUMP.S"); + verbose_printk("JUMP.S"); else if (opcode >= 0xe080 && opcode <= 0xe0ff) - printk("LSETUP"); + verbose_printk("LSETUP"); else if (opcode >= 0xe200 && opcode <= 0xe2ff) - printk("JUMP.L"); + verbose_printk("JUMP.L"); else if (opcode >= 0xe300 && opcode <= 0xe3ff) - printk("CALL pcrel"); + verbose_printk("CALL pcrel"); else - printk("0x%04x", opcode); + verbose_printk("0x%04x", opcode); } } +#endif void dump_bfin_trace_buffer(void) { +#ifdef CONFIG_DEBUG_VERBOSE #ifdef CONFIG_DEBUG_BFIN_HWTRACE_ON int tflags, i = 0; char buf[150]; @@ -701,6 +756,7 @@ void dump_bfin_trace_buffer(void) trace_buffer_restore(tflags); #endif +#endif } EXPORT_SYMBOL(dump_bfin_trace_buffer); @@ -708,7 +764,7 @@ EXPORT_SYMBOL(dump_bfin_trace_buffer); * Checks to see if the address pointed to is either a * 16-bit CALL instruction, or a 32-bit CALL instruction */ -bool is_bfin_call(unsigned short *addr) +static bool is_bfin_call(unsigned short *addr) { unsigned short opcode = 0, *ins_addr; ins_addr = (unsigned short *)addr; @@ -730,8 +786,10 @@ bool is_bfin_call(unsigned short *addr) return false; } + void show_stack(struct task_struct *task, unsigned long *stack) { +#ifdef CONFIG_PRINTK unsigned int *addr, *endstack, *fp = 0, *frame; unsigned short *ins_addr; char buf[150]; @@ -756,8 +814,10 @@ void show_stack(struct task_struct *task, unsigned long *stack) } else endstack = (unsigned int *)PAGE_ALIGN((unsigned int)stack); + printk(KERN_NOTICE "Stack info:\n"); decode_address(buf, (unsigned int)stack); - printk(KERN_NOTICE "Stack info:\n" KERN_NOTICE " SP: [0x%p] %s\n", stack, buf); + printk(KERN_NOTICE " SP: [0x%p] %s\n", stack, buf); + addr = (unsigned int *)((unsigned int)stack & ~0x3F); /* First thing is to look for a frame pointer */ @@ -848,7 +908,7 @@ void show_stack(struct task_struct *task, unsigned long *stack) if (!j) printk("\n"); } - +#endif } void dump_stack(void) @@ -866,38 +926,39 @@ EXPORT_SYMBOL(dump_stack); void dump_bfin_process(struct pt_regs *fp) { +#ifdef CONFIG_DEBUG_VERBOSE /* We should be able to look at fp->ipend, but we don't push it on the * stack all the time, so do this until we fix that */ unsigned int context = bfin_read_IPEND(); if (oops_in_progress) - printk(KERN_EMERG "Kernel OOPS in progress\n"); + verbose_printk(KERN_EMERG "Kernel OOPS in progress\n"); if (context & 0x0020 && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) - printk(KERN_NOTICE "HW Error context\n"); + verbose_printk(KERN_NOTICE "HW Error context\n"); else if (context & 0x0020) - printk(KERN_NOTICE "Deferred Exception context\n"); + verbose_printk(KERN_NOTICE "Deferred Exception context\n"); else if (context & 0x3FC0) - printk(KERN_NOTICE "Interrupt context\n"); + verbose_printk(KERN_NOTICE "Interrupt context\n"); else if (context & 0x4000) - printk(KERN_NOTICE "Deferred Interrupt context\n"); + verbose_printk(KERN_NOTICE "Deferred Interrupt context\n"); else if (context & 0x8000) - printk(KERN_NOTICE "Kernel process context\n"); + verbose_printk(KERN_NOTICE "Kernel process context\n"); /* Because we are crashing, and pointers could be bad, we check things * pretty closely before we use them */ if ((unsigned long)current >= FIXED_CODE_START && !((unsigned long)current & 0x3) && current->pid) { - printk(KERN_NOTICE "CURRENT PROCESS:\n"); + verbose_printk(KERN_NOTICE "CURRENT PROCESS:\n"); if (current->comm >= (char *)FIXED_CODE_START) - printk(KERN_NOTICE "COMM=%s PID=%d\n", + verbose_printk(KERN_NOTICE "COMM=%s PID=%d\n", current->comm, current->pid); else - printk(KERN_NOTICE "COMM= invalid\n"); + verbose_printk(KERN_NOTICE "COMM= invalid\n"); if (!((unsigned long)current->mm & 0x3) && (unsigned long)current->mm >= FIXED_CODE_START) - printk(KERN_NOTICE "TEXT = 0x%p-0x%p DATA = 0x%p-0x%p\n" + verbose_printk(KERN_NOTICE "TEXT = 0x%p-0x%p DATA = 0x%p-0x%p\n" KERN_NOTICE " BSS = 0x%p-0x%p USER-STACK = 0x%p\n" KERN_NOTICE "\n", (void *)current->mm->start_code, @@ -908,38 +969,40 @@ void dump_bfin_process(struct pt_regs *fp) (void *)current->mm->brk, (void *)current->mm->start_stack); else - printk(KERN_NOTICE "invalid mm\n"); + verbose_printk(KERN_NOTICE "invalid mm\n"); } else - printk(KERN_NOTICE "\n" KERN_NOTICE + verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "No Valid process in current context\n"); +#endif } void dump_bfin_mem(struct pt_regs *fp) { +#ifdef CONFIG_DEBUG_VERBOSE unsigned short *addr, *erraddr, val = 0, err = 0; char sti = 0, buf[6]; erraddr = (void *)fp->pc; - printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr); + verbose_printk(KERN_NOTICE "return address: [0x%p]; contents of:", erraddr); for (addr = (unsigned short *)((unsigned long)erraddr & ~0xF) - 0x10; addr < (unsigned short *)((unsigned long)erraddr & ~0xF) + 0x10; addr++) { if (!((unsigned long)addr & 0xF)) - printk("\n" KERN_NOTICE "0x%p: ", addr); + verbose_printk("\n" KERN_NOTICE "0x%p: ", addr); - if (get_instruction(&val, addr)) { + if (!get_instruction(&val, addr)) { val = 0; sprintf(buf, "????"); } else sprintf(buf, "%04x", val); if (addr == erraddr) { - printk("[%s]", buf); + verbose_printk("[%s]", buf); err = val; } else - printk(" %s ", buf); + verbose_printk(" %s ", buf); /* Do any previous instructions turn on interrupts? */ if (addr <= erraddr && /* in the past */ @@ -948,14 +1011,14 @@ void dump_bfin_mem(struct pt_regs *fp) sti = 1; } - printk("\n"); + verbose_printk("\n"); /* Hardware error interrupts can be deferred */ if (unlikely(sti && (fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR && oops_in_progress)){ - printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n"); + verbose_printk(KERN_NOTICE "Looks like this was a deferred error - sorry\n"); #ifndef CONFIG_DEBUG_HWERR - printk(KERN_NOTICE "The remaining message may be meaningless\n" + verbose_printk(KERN_NOTICE "The remaining message may be meaningless\n" KERN_NOTICE "You should enable CONFIG_DEBUG_HWERR to get a" " better idea where it came from\n"); #else @@ -969,34 +1032,47 @@ void dump_bfin_mem(struct pt_regs *fp) /* And the last RETI points to the current userspace context */ if ((fp + 1)->pc >= current->mm->start_code && (fp + 1)->pc <= current->mm->end_code) { - printk(KERN_NOTICE "It might be better to look around here : \n"); - printk(KERN_NOTICE "-------------------------------------------\n"); + verbose_printk(KERN_NOTICE "It might be better to look around here : \n"); + verbose_printk(KERN_NOTICE "-------------------------------------------\n"); show_regs(fp + 1); - printk(KERN_NOTICE "-------------------------------------------\n"); + verbose_printk(KERN_NOTICE "-------------------------------------------\n"); } } #endif } +#endif } void show_regs(struct pt_regs *fp) { +#ifdef CONFIG_DEBUG_VERBOSE char buf [150]; struct irqaction *action; unsigned int i; unsigned long flags; - printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted()); - printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", + verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "SEQUENCER STATUS:\t\t%s\n", print_tainted()); + verbose_printk(KERN_NOTICE " SEQSTAT: %08lx IPEND: %04lx SYSCFG: %04lx\n", (long)fp->seqstat, fp->ipend, fp->syscfg); - printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n", - (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14); - printk(KERN_NOTICE " EXCAUSE : 0x%lx\n", + if ((fp->seqstat & SEQSTAT_EXCAUSE) == VEC_HWERR) { + verbose_printk(KERN_NOTICE " HWERRCAUSE: 0x%lx\n", + (fp->seqstat & SEQSTAT_HWERRCAUSE) >> 14); +#ifdef EBIU_ERRMST + /* If the error was from the EBIU, print it out */ + if (bfin_read_EBIU_ERRMST() & CORE_ERROR) { + verbose_printk(KERN_NOTICE " EBIU Error Reason : 0x%04x\n", + bfin_read_EBIU_ERRMST()); + verbose_printk(KERN_NOTICE " EBIU Error Address : 0x%08x\n", + bfin_read_EBIU_ERRADD()); + } +#endif + } + verbose_printk(KERN_NOTICE " EXCAUSE : 0x%lx\n", fp->seqstat & SEQSTAT_EXCAUSE); for (i = 6; i <= 15 ; i++) { if (fp->ipend & (1 << i)) { decode_address(buf, bfin_read32(EVT0 + 4*i)); - printk(KERN_NOTICE " physical IVG%i asserted : %s\n", i, buf); + verbose_printk(KERN_NOTICE " physical IVG%i asserted : %s\n", i, buf); } } @@ -1009,64 +1085,65 @@ void show_regs(struct pt_regs *fp) goto unlock; decode_address(buf, (unsigned int)action->handler); - printk(KERN_NOTICE " logical irq %3d mapped : %s", i, buf); + verbose_printk(KERN_NOTICE " logical irq %3d mapped : %s", i, buf); for (action = action->next; action; action = action->next) { decode_address(buf, (unsigned int)action->handler); - printk(", %s", buf); + verbose_printk(", %s", buf); } - printk("\n"); + verbose_printk("\n"); unlock: spin_unlock_irqrestore(&irq_desc[i].lock, flags); } } decode_address(buf, fp->rete); - printk(KERN_NOTICE " RETE: %s\n", buf); + verbose_printk(KERN_NOTICE " RETE: %s\n", buf); decode_address(buf, fp->retn); - printk(KERN_NOTICE " RETN: %s\n", buf); + verbose_printk(KERN_NOTICE " RETN: %s\n", buf); decode_address(buf, fp->retx); - printk(KERN_NOTICE " RETX: %s\n", buf); + verbose_printk(KERN_NOTICE " RETX: %s\n", buf); decode_address(buf, fp->rets); - printk(KERN_NOTICE " RETS: %s\n", buf); + verbose_printk(KERN_NOTICE " RETS: %s\n", buf); decode_address(buf, fp->pc); - printk(KERN_NOTICE " PC : %s\n", buf); + verbose_printk(KERN_NOTICE " PC : %s\n", buf); if (((long)fp->seqstat & SEQSTAT_EXCAUSE) && (((long)fp->seqstat & SEQSTAT_EXCAUSE) != VEC_HWERR)) { decode_address(buf, saved_dcplb_fault_addr); - printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf); + verbose_printk(KERN_NOTICE "DCPLB_FAULT_ADDR: %s\n", buf); decode_address(buf, saved_icplb_fault_addr); - printk(KERN_NOTICE "ICPLB_FAULT_ADDR: %s\n", buf); + verbose_printk(KERN_NOTICE "ICPLB_FAULT_ADDR: %s\n", buf); } - printk(KERN_NOTICE "\n" KERN_NOTICE "PROCESSOR STATE:\n"); - printk(KERN_NOTICE " R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n", + verbose_printk(KERN_NOTICE "\n" KERN_NOTICE "PROCESSOR STATE:\n"); + verbose_printk(KERN_NOTICE " R0 : %08lx R1 : %08lx R2 : %08lx R3 : %08lx\n", fp->r0, fp->r1, fp->r2, fp->r3); - printk(KERN_NOTICE " R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n", + verbose_printk(KERN_NOTICE " R4 : %08lx R5 : %08lx R6 : %08lx R7 : %08lx\n", fp->r4, fp->r5, fp->r6, fp->r7); - printk(KERN_NOTICE " P0 : %08lx P1 : %08lx P2 : %08lx P3 : %08lx\n", + verbose_printk(KERN_NOTICE " P0 : %08lx P1 : %08lx P2 : %08lx P3 : %08lx\n", fp->p0, fp->p1, fp->p2, fp->p3); - printk(KERN_NOTICE " P4 : %08lx P5 : %08lx FP : %08lx SP : %08lx\n", + verbose_printk(KERN_NOTICE " P4 : %08lx P5 : %08lx FP : %08lx SP : %08lx\n", fp->p4, fp->p5, fp->fp, (long)fp); - printk(KERN_NOTICE " LB0: %08lx LT0: %08lx LC0: %08lx\n", + verbose_printk(KERN_NOTICE " LB0: %08lx LT0: %08lx LC0: %08lx\n", fp->lb0, fp->lt0, fp->lc0); - printk(KERN_NOTICE " LB1: %08lx LT1: %08lx LC1: %08lx\n", + verbose_printk(KERN_NOTICE " LB1: %08lx LT1: %08lx LC1: %08lx\n", fp->lb1, fp->lt1, fp->lc1); - printk(KERN_NOTICE " B0 : %08lx L0 : %08lx M0 : %08lx I0 : %08lx\n", + verbose_printk(KERN_NOTICE " B0 : %08lx L0 : %08lx M0 : %08lx I0 : %08lx\n", fp->b0, fp->l0, fp->m0, fp->i0); - printk(KERN_NOTICE " B1 : %08lx L1 : %08lx M1 : %08lx I1 : %08lx\n", + verbose_printk(KERN_NOTICE " B1 : %08lx L1 : %08lx M1 : %08lx I1 : %08lx\n", fp->b1, fp->l1, fp->m1, fp->i1); - printk(KERN_NOTICE " B2 : %08lx L2 : %08lx M2 : %08lx I2 : %08lx\n", + verbose_printk(KERN_NOTICE " B2 : %08lx L2 : %08lx M2 : %08lx I2 : %08lx\n", fp->b2, fp->l2, fp->m2, fp->i2); - printk(KERN_NOTICE " B3 : %08lx L3 : %08lx M3 : %08lx I3 : %08lx\n", + verbose_printk(KERN_NOTICE " B3 : %08lx L3 : %08lx M3 : %08lx I3 : %08lx\n", fp->b3, fp->l3, fp->m3, fp->i3); - printk(KERN_NOTICE "A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n", + verbose_printk(KERN_NOTICE "A0.w: %08lx A0.x: %08lx A1.w: %08lx A1.x: %08lx\n", fp->a0w, fp->a0x, fp->a1w, fp->a1x); - printk(KERN_NOTICE "USP : %08lx ASTAT: %08lx\n", + verbose_printk(KERN_NOTICE "USP : %08lx ASTAT: %08lx\n", rdusp(), fp->astat); - printk(KERN_NOTICE "\n"); + verbose_printk(KERN_NOTICE "\n"); +#endif } #ifdef CONFIG_SYS_BFIN_SPINLOCK_L1 diff --git a/arch/blackfin/mach-bf527/boards/Kconfig b/arch/blackfin/mach-bf527/boards/Kconfig index 8bf9e58f014..df224d04e16 100644 --- a/arch/blackfin/mach-bf527/boards/Kconfig +++ b/arch/blackfin/mach-bf527/boards/Kconfig @@ -14,4 +14,9 @@ config BFIN527_BLUETECHNIX_CM help CM-BF527 support for EVAL- and DEV-Board. +config BFIN526_EZBRD + bool "BF526-EZBRD" + help + BF526-EZBRD/EZKIT Lite board support. + endchoice diff --git a/arch/blackfin/mach-bf527/boards/Makefile b/arch/blackfin/mach-bf527/boards/Makefile index 7ba7d256bbb..eb6ed3362f9 100644 --- a/arch/blackfin/mach-bf527/boards/Makefile +++ b/arch/blackfin/mach-bf527/boards/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_BFIN527_EZKIT) += ezkit.o obj-$(CONFIG_BFIN527_BLUETECHNIX_CM) += cm_bf527.o +obj-$(CONFIG_BFIN526_EZBRD) += ezbrd.o diff --git a/arch/blackfin/mach-bf527/boards/cm_bf527.c b/arch/blackfin/mach-bf527/boards/cm_bf527.c index d22bc777371..9ea440bbb13 100644 --- a/arch/blackfin/mach-bf527/boards/cm_bf527.c +++ b/arch/blackfin/mach-bf527/boards/cm_bf527.c @@ -43,10 +43,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/usb/sl811.h> -#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> -#endif -#include <asm/cplb.h> #include <asm/dma.h> #include <asm/bfin5xx_spi.h> #include <asm/reboot.h> @@ -130,6 +127,16 @@ static struct resource musb_resources[] = { }, }; +static struct musb_hdrc_config musb_config = { + .multipoint = 0, + .dyn_fifo = 0, + .soft_con = 1, + .dma = 1, + .num_eps = 7, + .dma_channels = 7, + .gpio_vrsel = GPIO_PF11, +}; + static struct musb_hdrc_platform_data musb_plat = { #if defined(CONFIG_USB_MUSB_OTG) .mode = MUSB_OTG, @@ -138,7 +145,7 @@ static struct musb_hdrc_platform_data musb_plat = { #elif defined(CONFIG_USB_GADGET_MUSB_HDRC) .mode = MUSB_PERIPHERAL, #endif - .multipoint = 0, + .config = &musb_config, }; static u64 musb_dmamask = ~(u32)0; @@ -201,7 +208,7 @@ static struct mtd_partition partition_info[] = { { .name = "linux kernel(nand)", .offset = 0, - .size = 4 * SIZE_1M, + .size = 4 * 1024 * 1024, }, { .name = "file system(nand)", diff --git a/arch/blackfin/mach-bf527/boards/ezbrd.c b/arch/blackfin/mach-bf527/boards/ezbrd.c new file mode 100644 index 00000000000..36c87b6fbde --- /dev/null +++ b/arch/blackfin/mach-bf527/boards/ezbrd.c @@ -0,0 +1,734 @@ +/* + * File: arch/blackfin/mach-bf527/boards/ezbrd.c + * Based on: arch/blackfin/mach-bf537/boards/stamp.c + * Author: Aidan Williams <aidan@nicta.com.au> + * + * Created: + * Description: + * + * Modified: + * Copyright 2005 National ICT Australia (NICTA) + * Copyright 2004-2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/physmap.h> +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> + +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/usb/musb.h> +#include <asm/dma.h> +#include <asm/bfin5xx_spi.h> +#include <asm/reboot.h> +#include <asm/nand.h> +#include <asm/portmux.h> +#include <asm/dpmc.h> +#include <linux/spi/ad7877.h> + +/* + * Name the Board for the /proc/cpuinfo + */ +const char bfin_board_name[] = "BF526-EZBRD"; + +/* + * Driver needs to know address, irq and flag pin. + */ + +#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) +static struct resource musb_resources[] = { + [0] = { + .start = 0xffc03800, + .end = 0xffc03cff, + .flags = IORESOURCE_MEM, + }, + [1] = { /* general IRQ */ + .start = IRQ_USB_INT0, + .end = IRQ_USB_INT0, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, + [2] = { /* DMA IRQ */ + .start = IRQ_USB_DMA, + .end = IRQ_USB_DMA, + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL, + }, +}; + +static struct musb_hdrc_config musb_config = { + .multipoint = 0, + .dyn_fifo = 0, + .soft_con = 1, + .dma = 1, + .num_eps = 7, + .dma_channels = 7, + .gpio_vrsel = GPIO_PG13, +}; + +static struct musb_hdrc_platform_data musb_plat = { +#if defined(CONFIG_USB_MUSB_OTG) + .mode = MUSB_OTG, +#elif defined(CONFIG_USB_MUSB_HDRC_HCD) + .mode = MUSB_HOST, +#elif defined(CONFIG_USB_GADGET_MUSB_HDRC) + .mode = MUSB_PERIPHERAL, +#endif + .config = &musb_config, +}; + +static u64 musb_dmamask = ~(u32)0; + +static struct platform_device musb_device = { + .name = "musb_hdrc", + .id = 0, + .dev = { + .dma_mask = &musb_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &musb_plat, + }, + .num_resources = ARRAY_SIZE(musb_resources), + .resource = musb_resources, +}; +#endif + +#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE) +static struct mtd_partition ezbrd_partitions[] = { + { + .name = "bootloader(nor)", + .size = 0x40000, + .offset = 0, + }, { + .name = "linux kernel(nor)", + .size = 0x1C0000, + .offset = MTDPART_OFS_APPEND, + }, { + .name = "file system(nor)", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct physmap_flash_data ezbrd_flash_data = { + .width = 2, + .parts = ezbrd_partitions, + .nr_parts = ARRAY_SIZE(ezbrd_partitions), +}; + +static struct resource ezbrd_flash_resource = { + .start = 0x20000000, + .end = 0x203fffff, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device ezbrd_flash_device = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &ezbrd_flash_data, + }, + .num_resources = 1, + .resource = &ezbrd_flash_resource, +}; +#endif + +#if defined(CONFIG_MTD_NAND_BF5XX) || defined(CONFIG_MTD_NAND_BF5XX_MODULE) +static struct mtd_partition partition_info[] = { + { + .name = "linux kernel(nand)", + .offset = 0, + .size = 4 * 1024 * 1024, + }, + { + .name = "file system(nand)", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct bf5xx_nand_platform bf5xx_nand_platform = { + .page_size = NFC_PG_SIZE_256, + .data_width = NFC_NWIDTH_8, + .partitions = partition_info, + .nr_partitions = ARRAY_SIZE(partition_info), + .rd_dly = 3, + .wr_dly = 3, +}; + +static struct resource bf5xx_nand_resources[] = { + { + .start = NFC_CTL, + .end = NFC_DATA_RD + 2, + .flags = IORESOURCE_MEM, + }, + { + .start = CH_NFC, + .end = CH_NFC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bf5xx_nand_device = { + .name = "bf5xx-nand", + .id = 0, + .num_resources = ARRAY_SIZE(bf5xx_nand_resources), + .resource = bf5xx_nand_resources, + .dev = { + .platform_data = &bf5xx_nand_platform, + }, +}; +#endif + +#if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE) +static struct platform_device rtc_device = { + .name = "rtc-bfin", + .id = -1, +}; +#endif + + +#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE) +static struct platform_device bfin_mac_device = { + .name = "bfin_mac", +}; +#endif + +#if defined(CONFIG_MTD_M25P80) \ + || defined(CONFIG_MTD_M25P80_MODULE) +static struct mtd_partition bfin_spi_flash_partitions[] = { + { + .name = "bootloader(spi)", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_CAP_ROM + }, { + .name = "linux kernel(spi)", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static struct flash_platform_data bfin_spi_flash_data = { + .name = "m25p80", + .parts = bfin_spi_flash_partitions, + .nr_parts = ARRAY_SIZE(bfin_spi_flash_partitions), + .type = "m25p16", +}; + +/* SPI flash chip (m25p64) */ +static struct bfin5xx_spi_chip spi_flash_chip_info = { + .enable_dma = 0, /* use dma transfer with this chip*/ + .bits_per_word = 8, +}; +#endif + +#if defined(CONFIG_SPI_ADC_BF533) \ + || defined(CONFIG_SPI_ADC_BF533_MODULE) +/* SPI ADC chip */ +static struct bfin5xx_spi_chip spi_adc_chip_info = { + .enable_dma = 1, /* use dma transfer with this chip*/ + .bits_per_word = 16, +}; +#endif + +#if defined(CONFIG_SPI_MMC) || defined(CONFIG_SPI_MMC_MODULE) +static struct bfin5xx_spi_chip spi_mmc_chip_info = { + .enable_dma = 1, + .bits_per_word = 8, +}; +#endif + +#if defined(CONFIG_PBX) +static struct bfin5xx_spi_chip spi_si3xxx_chip_info = { + .ctl_reg = 0x4, /* send zero */ + .enable_dma = 0, + .bits_per_word = 8, + .cs_change_per_word = 1, +}; +#endif + +#if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE) +static struct bfin5xx_spi_chip spi_ad7877_chip_info = { + .enable_dma = 0, + .bits_per_word = 16, +}; + +static const struct ad7877_platform_data bfin_ad7877_ts_info = { + .model = 7877, + .vref_delay_usecs = 50, /* internal, no capacitor */ + .x_plate_ohms = 419, + .y_plate_ohms = 486, + .pressure_max = 1000, + .pressure_min = 0, + .stopacq_polarity = 1, + .first_conversion_delay = 3, + .acquisition_time = 1, + .averaging = 1, + .pen_down_acc_interval = 1, +}; +#endif + +#if defined(CONFIG_SND_SOC_WM8731) || defined(CONFIG_SND_SOC_WM8731_MODULE) \ + && defined(CONFIG_SND_SOC_WM8731_SPI) +static struct bfin5xx_spi_chip spi_wm8731_chip_info = { + .enable_dma = 0, + .bits_per_word = 16, +}; +#endif + +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) +static struct bfin5xx_spi_chip spidev_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) +static struct bfin5xx_spi_chip lq035q1_spi_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + +static struct spi_board_info bfin_spi_board_info[] __initdata = { +#if defined(CONFIG_MTD_M25P80) \ + || defined(CONFIG_MTD_M25P80_MODULE) + { + /* the modalias must be the same as spi device driver name */ + .modalias = "m25p80", /* Name of spi_driver for this device */ + .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, /* Framework bus number */ + .chip_select = 1, /* Framework chip select. On STAMP537 it is SPISSEL1*/ + .platform_data = &bfin_spi_flash_data, + .controller_data = &spi_flash_chip_info, + .mode = SPI_MODE_3, + }, +#endif + +#if defined(CONFIG_SPI_ADC_BF533) \ + || defined(CONFIG_SPI_ADC_BF533_MODULE) + { + .modalias = "bfin_spi_adc", /* Name of spi_driver for this device */ + .max_speed_hz = 6250000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, /* Framework bus number */ + .chip_select = 1, /* Framework chip select. */ + .platform_data = NULL, /* No spi_driver specific config */ + .controller_data = &spi_adc_chip_info, + }, +#endif + +#if defined(CONFIG_SPI_MMC) || defined(CONFIG_SPI_MMC_MODULE) + { + .modalias = "spi_mmc_dummy", + .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 0, + .platform_data = NULL, + .controller_data = &spi_mmc_chip_info, + .mode = SPI_MODE_3, + }, + { + .modalias = "spi_mmc", + .max_speed_hz = 25000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = CONFIG_SPI_MMC_CS_CHAN, + .platform_data = NULL, + .controller_data = &spi_mmc_chip_info, + .mode = SPI_MODE_3, + }, +#endif +#if defined(CONFIG_PBX) + { + .modalias = "fxs-spi", + .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 8 - CONFIG_J11_JUMPER, + .controller_data = &spi_si3xxx_chip_info, + .mode = SPI_MODE_3, + }, + { + .modalias = "fxo-spi", + .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 8 - CONFIG_J19_JUMPER, + .controller_data = &spi_si3xxx_chip_info, + .mode = SPI_MODE_3, + }, +#endif +#if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE) + { + .modalias = "ad7877", + .platform_data = &bfin_ad7877_ts_info, + .irq = IRQ_PF8, + .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 2, + .controller_data = &spi_ad7877_chip_info, + }, +#endif +#if defined(CONFIG_SND_SOC_WM8731) || defined(CONFIG_SND_SOC_WM8731_MODULE) \ + && defined(CONFIG_SND_SOC_WM8731_SPI) + { + .modalias = "wm8731", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 5, + .controller_data = &spi_wm8731_chip_info, + .mode = SPI_MODE_0, + }, +#endif +#if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) + { + .modalias = "spidev", + .max_speed_hz = 3125000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spidev_chip_info, + }, +#endif +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) + { + .modalias = "bfin-lq035q1-spi", + .max_speed_hz = 20000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &lq035q1_spi_chip_info, + .mode = SPI_CPHA | SPI_CPOL, + }, +#endif +}; + +#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE) +/* SPI controller data */ +static struct bfin5xx_spi_master bfin_spi0_info = { + .num_chipselect = 8, + .enable_dma = 1, /* master has the ability to do dma transfer */ + .pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0}, +}; + +/* SPI (0) */ +static struct resource bfin_spi0_resource[] = { + [0] = { + .start = SPI0_REGBASE, + .end = SPI0_REGBASE + 0xFF, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = CH_SPI, + .end = CH_SPI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bfin_spi0_device = { + .name = "bfin-spi", + .id = 0, /* Bus number */ + .num_resources = ARRAY_SIZE(bfin_spi0_resource), + .resource = bfin_spi0_resource, + .dev = { + .platform_data = &bfin_spi0_info, /* Passed to driver */ + }, +}; +#endif /* spi master and devices */ + +#if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE) +static struct resource bfin_uart_resources[] = { +#ifdef CONFIG_SERIAL_BFIN_UART0 + { + .start = 0xFFC00400, + .end = 0xFFC004FF, + .flags = IORESOURCE_MEM, + }, +#endif +#ifdef CONFIG_SERIAL_BFIN_UART1 + { + .start = 0xFFC02000, + .end = 0xFFC020FF, + .flags = IORESOURCE_MEM, + }, +#endif +}; + +static struct platform_device bfin_uart_device = { + .name = "bfin-uart", + .id = 1, + .num_resources = ARRAY_SIZE(bfin_uart_resources), + .resource = bfin_uart_resources, +}; +#endif + +#if defined(CONFIG_BFIN_SIR) || defined(CONFIG_BFIN_SIR_MODULE) +static struct resource bfin_sir_resources[] = { +#ifdef CONFIG_BFIN_SIR0 + { + .start = 0xFFC00400, + .end = 0xFFC004FF, + .flags = IORESOURCE_MEM, + }, +#endif +#ifdef CONFIG_BFIN_SIR1 + { + .start = 0xFFC02000, + .end = 0xFFC020FF, + .flags = IORESOURCE_MEM, + }, +#endif +}; + +static struct platform_device bfin_sir_device = { + .name = "bfin_sir", + .id = 0, + .num_resources = ARRAY_SIZE(bfin_sir_resources), + .resource = bfin_sir_resources, +}; +#endif + +#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) +static struct resource bfin_twi0_resource[] = { + [0] = { + .start = TWI0_REGBASE, + .end = TWI0_REGBASE, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_TWI, + .end = IRQ_TWI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device i2c_bfin_twi_device = { + .name = "i2c-bfin-twi", + .id = 0, + .num_resources = ARRAY_SIZE(bfin_twi0_resource), + .resource = bfin_twi0_resource, +}; +#endif + +#ifdef CONFIG_I2C_BOARDINFO +static struct i2c_board_info __initdata bfin_i2c_board_info[] = { +#if defined(CONFIG_TWI_LCD) || defined(CONFIG_TWI_LCD_MODULE) + { + I2C_BOARD_INFO("pcf8574_lcd", 0x22), + }, +#endif +#if defined(CONFIG_TWI_KEYPAD) || defined(CONFIG_TWI_KEYPAD_MODULE) + { + I2C_BOARD_INFO("pcf8574_keypad", 0x27), + .irq = IRQ_PF8, + }, +#endif +}; +#endif + +#if defined(CONFIG_SERIAL_BFIN_SPORT) || defined(CONFIG_SERIAL_BFIN_SPORT_MODULE) +static struct platform_device bfin_sport0_uart_device = { + .name = "bfin-sport-uart", + .id = 0, +}; + +static struct platform_device bfin_sport1_uart_device = { + .name = "bfin-sport-uart", + .id = 1, +}; +#endif + +#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) +#include <linux/input.h> +#include <linux/gpio_keys.h> + +static struct gpio_keys_button bfin_gpio_keys_table[] = { + {BTN_0, GPIO_PG0, 1, "gpio-keys: BTN0"}, + {BTN_1, GPIO_PG13, 1, "gpio-keys: BTN1"}, +}; + +static struct gpio_keys_platform_data bfin_gpio_keys_data = { + .buttons = bfin_gpio_keys_table, + .nbuttons = ARRAY_SIZE(bfin_gpio_keys_table), +}; + +static struct platform_device bfin_device_gpiokeys = { + .name = "gpio-keys", + .dev = { + .platform_data = &bfin_gpio_keys_data, + }, +}; +#endif + +static struct resource bfin_gpios_resources = { + .start = 0, + .end = MAX_BLACKFIN_GPIOS - 1, + .flags = IORESOURCE_IRQ, +}; + +static struct platform_device bfin_gpios_device = { + .name = "simple-gpio", + .id = -1, + .num_resources = 1, + .resource = &bfin_gpios_resources, +}; + +static const unsigned int cclk_vlev_datasheet[] = +{ + VRPAIR(VLEV_100, 400000000), + VRPAIR(VLEV_105, 426000000), + VRPAIR(VLEV_110, 500000000), + VRPAIR(VLEV_115, 533000000), + VRPAIR(VLEV_120, 600000000), +}; + +static struct bfin_dpmc_platform_data bfin_dmpc_vreg_data = { + .tuple_tab = cclk_vlev_datasheet, + .tabsize = ARRAY_SIZE(cclk_vlev_datasheet), + .vr_settling_time = 25 /* us */, +}; + +static struct platform_device bfin_dpmc = { + .name = "bfin dpmc", + .dev = { + .platform_data = &bfin_dmpc_vreg_data, + }, +}; + +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) +#include <asm/bfin-lq035q1.h> + +static struct bfin_lq035q1fb_disp_info bfin_lq035q1_data = { + .mode = LQ035_NORM | LQ035_RGB | LQ035_RL | LQ035_TB, + .use_bl = 1, + .gpio_bl = GPIO_PG12, +}; + +static struct resource bfin_lq035q1_resources[] = { + { + .start = IRQ_PPI_ERROR, + .end = IRQ_PPI_ERROR, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bfin_lq035q1_device = { + .name = "bfin-lq035q1", + .id = -1, + .num_resources = ARRAY_SIZE(bfin_lq035q1_resources), + .resource = bfin_lq035q1_resources, + .dev = { + .platform_data = &bfin_lq035q1_data, + }, +}; +#endif + +static struct platform_device *stamp_devices[] __initdata = { + + &bfin_dpmc, + +#if defined(CONFIG_MTD_NAND_BF5XX) || defined(CONFIG_MTD_NAND_BF5XX_MODULE) + &bf5xx_nand_device, +#endif + +#if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE) + &rtc_device, +#endif + +#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) + &musb_device, +#endif + +#if defined(CONFIG_BFIN_MAC) || defined(CONFIG_BFIN_MAC_MODULE) + &bfin_mac_device, +#endif + +#if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE) + &bfin_spi0_device, +#endif + +#if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE) + &bfin_uart_device, +#endif + +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) + &bfin_lq035q1_device, +#endif + +#if defined(CONFIG_BFIN_SIR) || defined(CONFIG_BFIN_SIR_MODULE) + &bfin_sir_device, +#endif + +#if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) + &i2c_bfin_twi_device, +#endif + +#if defined(CONFIG_SERIAL_BFIN_SPORT) || defined(CONFIG_SERIAL_BFIN_SPORT_MODULE) + &bfin_sport0_uart_device, + &bfin_sport1_uart_device, +#endif + +#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE) + &bfin_device_gpiokeys, +#endif + +#if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE) + &ezbrd_flash_device, +#endif + + &bfin_gpios_device, +}; + +static int __init stamp_init(void) +{ + printk(KERN_INFO "%s(): registering device resources\n", __func__); + +#ifdef CONFIG_I2C_BOARDINFO + i2c_register_board_info(0, bfin_i2c_board_info, + ARRAY_SIZE(bfin_i2c_board_info)); +#endif + + platform_add_devices(stamp_devices, ARRAY_SIZE(stamp_devices)); + spi_register_board_info(bfin_spi_board_info, ARRAY_SIZE(bfin_spi_board_info)); + return 0; +} + +arch_initcall(stamp_init); + +void native_machine_restart(char *cmd) +{ + /* workaround reboot hang when booting from SPI */ + if ((bfin_read_SYSCR() & 0x7) == 0x3) + bfin_gpio_reset_spi0_ssel1(); +} + +void bfin_get_ether_addr(char *addr) +{ + /* the MAC is stored in OTP memory page 0xDF */ + u32 ret; + u64 otp_mac; + u32 (*otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)0xEF00001A; + + ret = otp_read(0xDF, 0x00, &otp_mac); + if (!(ret & 0x1)) { + char *otp_mac_p = (char *)&otp_mac; + for (ret = 0; ret < 6; ++ret) + addr[ret] = otp_mac_p[5 - ret]; + } +} +EXPORT_SYMBOL(bfin_get_ether_addr); diff --git a/arch/blackfin/mach-bf527/boards/ezkit.c b/arch/blackfin/mach-bf527/boards/ezkit.c index 762f754c06c..8ee2b744e23 100644 --- a/arch/blackfin/mach-bf527/boards/ezkit.c +++ b/arch/blackfin/mach-bf527/boards/ezkit.c @@ -42,10 +42,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/usb/sl811.h> -#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> -#endif -#include <asm/cplb.h> #include <asm/dma.h> #include <asm/bfin5xx_spi.h> #include <asm/reboot.h> @@ -129,6 +126,16 @@ static struct resource musb_resources[] = { }, }; +static struct musb_hdrc_config musb_config = { + .multipoint = 0, + .dyn_fifo = 0, + .soft_con = 1, + .dma = 1, + .num_eps = 7, + .dma_channels = 7, + .gpio_vrsel = GPIO_PG13, +}; + static struct musb_hdrc_platform_data musb_plat = { #if defined(CONFIG_USB_MUSB_OTG) .mode = MUSB_OTG, @@ -137,7 +144,7 @@ static struct musb_hdrc_platform_data musb_plat = { #elif defined(CONFIG_USB_GADGET_MUSB_HDRC) .mode = MUSB_PERIPHERAL, #endif - .multipoint = 0, + .config = &musb_config, }; static u64 musb_dmamask = ~(u32)0; @@ -218,7 +225,7 @@ static struct mtd_partition partition_info[] = { { .name = "linux kernel(nand)", .offset = 0, - .size = 4 * SIZE_1M, + .size = 4 * 1024 * 1024, }, { .name = "file system(nand)", @@ -846,6 +853,38 @@ static struct platform_device bfin_device_gpiokeys = { }; #endif +#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE) +#include <linux/input.h> +#include <asm/bfin_rotary.h> + +static struct bfin_rotary_platform_data bfin_rotary_data = { + /*.rotary_up_key = KEY_UP,*/ + /*.rotary_down_key = KEY_DOWN,*/ + .rotary_rel_code = REL_WHEEL, + .rotary_button_key = KEY_ENTER, + .debounce = 10, /* 0..17 */ + .mode = ROT_QUAD_ENC | ROT_DEBE, +}; + +static struct resource bfin_rotary_resources[] = { + { + .start = IRQ_CNT, + .end = IRQ_CNT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bfin_rotary_device = { + .name = "bfin-rotary", + .id = -1, + .num_resources = ARRAY_SIZE(bfin_rotary_resources), + .resource = bfin_rotary_resources, + .dev = { + .platform_data = &bfin_rotary_data, + }, +}; +#endif + static struct resource bfin_gpios_resources = { .start = 0, .end = MAX_BLACKFIN_GPIOS - 1, @@ -962,6 +1001,10 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_device_gpiokeys, #endif +#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE) + &bfin_rotary_device, +#endif + #if defined(CONFIG_MTD_PHYSMAP) || defined(CONFIG_MTD_PHYSMAP_MODULE) &ezkit_flash_device, #endif diff --git a/arch/blackfin/mach-bf527/head.S b/arch/blackfin/mach-bf527/head.S index 28c48619120..6588170e384 100644 --- a/arch/blackfin/mach-bf527/head.S +++ b/arch/blackfin/mach-bf527/head.S @@ -87,6 +87,9 @@ ENTRY(_start_dma_code) r1 = PLL_BYPASS; /* Bypass the PLL? */ r1 = r1 << 8; /* Shift it over */ r0 = r1 | r0; /* add them all together */ +#ifdef ANOMALY_05000265 + r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */ +#endif p0.h = hi(PLL_CTL); p0.l = lo(PLL_CTL); /* Load the address */ diff --git a/arch/blackfin/mach-bf527/include/mach/anomaly.h b/arch/blackfin/mach-bf527/include/mach/anomaly.h index b7b166f4f06..62373e61c58 100644 --- a/arch/blackfin/mach-bf527/include/mach/anomaly.h +++ b/arch/blackfin/mach-bf527/include/mach/anomaly.h @@ -7,12 +7,24 @@ */ /* This file shoule be up to date with: - * - Revision C, 01/25/2008; ADSP-BF527 Blackfin Processor Anomaly List + * - Revision B, 08/12/2008; ADSP-BF526 Blackfin Processor Anomaly List + * - Revision E, 08/18/2008; ADSP-BF527 Blackfin Processor Anomaly List */ #ifndef _MACH_ANOMALY_H_ #define _MACH_ANOMALY_H_ +#if defined(__ADSPBF522__) || defined(__ADSPBF524__) || defined(__ADSPBF526__) +# define ANOMALY_BF526 1 +#else +# define ANOMALY_BF526 0 +#endif +#if defined(__ADSPBF523__) || defined(__ADSPBF525__) || defined(__ADSPBF527__) +# define ANOMALY_BF527 1 +#else +# define ANOMALY_BF527 0 +#endif + /* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */ #define ANOMALY_05000074 (1) /* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */ @@ -23,68 +35,124 @@ #define ANOMALY_05000245 (1) /* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */ #define ANOMALY_05000265 (1) -/* New Feature: EMAC TX DMA Word Alignment */ -#define ANOMALY_05000285 (1) +/* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */ +#define ANOMALY_05000310 (1) /* Errors when SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */ -#define ANOMALY_05000312 (1) +#define ANOMALY_05000312 (ANOMALY_BF527) +/* PPI Is Level-Sensitive on First Transfer In Single Frame Sync Modes */ +#define ANOMALY_05000313 (__SILICON_REVISION__ < 2) /* Incorrect Access of OTP_STATUS During otp_write() Function */ -#define ANOMALY_05000328 (1) +#define ANOMALY_05000328 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Disallowed Configuration Prevents Subsequent Allowed Configuration on Host DMA Port */ -#define ANOMALY_05000337 (1) +#define ANOMALY_05000337 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Ethernet MAC MDIO Reads Do Not Meet IEEE Specification */ -#define ANOMALY_05000341 (1) +#define ANOMALY_05000341 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* TWI May Not Operate Correctly Under Certain Signal Termination Conditions */ -#define ANOMALY_05000342 (1) +#define ANOMALY_05000342 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* USB Calibration Value Is Not Initialized */ -#define ANOMALY_05000346 (1) +#define ANOMALY_05000346 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* USB Calibration Value to use */ +#define ANOMALY_05000346_value 0xE510 /* Preboot Routine Incorrectly Alters Reset Value of USB Register */ -#define ANOMALY_05000347 (1) +#define ANOMALY_05000347 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Security Features Are Not Functional */ -#define ANOMALY_05000348 (__SILICON_REVISION__ < 1) +#define ANOMALY_05000348 (ANOMALY_BF527 && __SILICON_REVISION__ < 1) +/* bfrom_SysControl() Firmware Function Performs Improper System Reset */ +#define ANOMALY_05000353 (ANOMALY_BF526) /* Regulator Programming Blocked when Hibernate Wakeup Source Remains Active */ -#define ANOMALY_05000355 (1) +#define ANOMALY_05000355 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */ -#define ANOMALY_05000357 (1) +#define ANOMALY_05000357 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Incorrect Revision Number in DSPID Register */ -#define ANOMALY_05000364 (__SILICON_REVISION__ > 0) +#define ANOMALY_05000364 (ANOMALY_BF527 && __SILICON_REVISION__ == 1) /* PPI Underflow Error Goes Undetected in ITU-R 656 Mode */ #define ANOMALY_05000366 (1) -/* New Feature: Higher Default CCLK Rate */ -#define ANOMALY_05000368 (1) +/* Incorrect Default CSEL Value in PLL_DIV */ +#define ANOMALY_05000368 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */ -#define ANOMALY_05000371 (1) +#define ANOMALY_05000371 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Authentication Fails To Initiate */ -#define ANOMALY_05000376 (__SILICON_REVISION__ > 0) +#define ANOMALY_05000376 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* Data Read From L3 Memory by USB DMA May be Corrupted */ -#define ANOMALY_05000380 (1) -/* USB Full-speed Mode not Fully Tested */ -#define ANOMALY_05000381 (1) -/* New Feature: Boot from OTP Memory */ -#define ANOMALY_05000385 (1) -/* New Feature: bfrom_SysControl() Routine */ -#define ANOMALY_05000386 (1) -/* New Feature: Programmable Preboot Settings */ -#define ANOMALY_05000387 (1) +#define ANOMALY_05000380 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* 8-Bit NAND Flash Boot Mode Not Functional */ +#define ANOMALY_05000382 (__SILICON_REVISION__ < 2) +/* Host Must Not Read Back During Host DMA Boot */ +#define ANOMALY_05000384 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Boot from OTP Memory Not Functional */ +#define ANOMALY_05000385 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* bfrom_SysControl() Firmware Routine Not Functional */ +#define ANOMALY_05000386 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Programmable Preboot Settings Not Functional */ +#define ANOMALY_05000387 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* CRC32 Checksum Support Not Functional */ +#define ANOMALY_05000388 (__SILICON_REVISION__ < 2) /* Reset Vector Must Not Be in SDRAM Memory Space */ -#define ANOMALY_05000389 (1) -/* New Feature: pTempCurrent Added to ADI_BOOT_DATA Structure */ -#define ANOMALY_05000392 (1) -/* New Feature: dTempByteCount Value Increased in ADI_BOOT_DATA Structure */ -#define ANOMALY_05000393 (1) -/* New Feature: Log Buffer Functionality */ -#define ANOMALY_05000394 (1) -/* New Feature: Hook Routine Functionality */ -#define ANOMALY_05000395 (1) -/* New Feature: Header Indirect Bit */ -#define ANOMALY_05000396 (1) -/* New Feature: BK_ONES, BK_ZEROS, and BK_DATECODE Constants */ -#define ANOMALY_05000397 (1) -/* New Feature: SWRESET, DFRESET and WDRESET Bits Added to SYSCR Register */ -#define ANOMALY_05000398 (1) -/* New Feature: BCODE_NOBOOT Added to BCODE Field of SYSCR Register */ -#define ANOMALY_05000399 (1) +#define ANOMALY_05000389 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* pTempCurrent Not Present in ADI_BOOT_DATA Structure */ +#define ANOMALY_05000392 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Deprecated Value of dTempByteCount in ADI_BOOT_DATA Structure */ +#define ANOMALY_05000393 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Log Buffer Not Functional */ +#define ANOMALY_05000394 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Hook Routine Not Functional */ +#define ANOMALY_05000395 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Header Indirect Bit Not Functional */ +#define ANOMALY_05000396 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* BK_ONES, BK_ZEROS, and BK_DATECODE Constants Not Functional */ +#define ANOMALY_05000397 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* SWRESET, DFRESET and WDRESET Bits in the SYSCR Register Not Functional */ +#define ANOMALY_05000398 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* BCODE_NOBOOT in BCODE Field of SYSCR Register Not Functional */ +#define ANOMALY_05000399 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) /* PPI Data Signals D0 and D8 do not Tristate After Disabling PPI */ -#define ANOMALY_05000401 (1) +#define ANOMALY_05000401 (__SILICON_REVISION__ < 2) +/* Level-Sensitive External GPIO Wakeups May Cause Indefinite Stall */ +#define ANOMALY_05000403 (__SILICON_REVISION__ < 2) +/* Lockbox SESR Disallows Certain User Interrupts */ +#define ANOMALY_05000404 (__SILICON_REVISION__ < 2) +/* Lockbox SESR Firmware Does Not Save/Restore Full Context */ +#define ANOMALY_05000405 (1) +/* Lockbox SESR Firmware Arguments Are Not Retained After First Initialization */ +#define ANOMALY_05000407 (__SILICON_REVISION__ < 2) +/* Lockbox Firmware Memory Cleanup Routine Does not Clear Registers */ +#define ANOMALY_05000408 (1) +/* Lockbox firmware leaves MDMA0 channel enabled */ +#define ANOMALY_05000409 (__SILICON_REVISION__ < 2) +/* Incorrect Default Internal Voltage Regulator Setting */ +#define ANOMALY_05000410 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* bfrom_SysControl() Firmware Function Cannot be Used to Enter Power Saving Modes */ +#define ANOMALY_05000411 (__SILICON_REVISION__ < 2) +/* OTP_CHECK_FOR_PREV_WRITE Bit is Not Functional in bfrom_OtpWrite() API */ +#define ANOMALY_05000414 (__SILICON_REVISION__ < 2) +/* DEB2_URGENT Bit Not Functional */ +#define ANOMALY_05000415 (__SILICON_REVISION__ < 2) +/* Speculative Fetches Can Cause Undesired External FIFO Operations */ +#define ANOMALY_05000416 (1) +/* SPORT0 Ignores External TSCLK0 on PG14 When TMR6 is an Output */ +#define ANOMALY_05000417 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* tSFSPE and tHFSPE Do Not Meet Data Sheet Specifications */ +#define ANOMALY_05000418 (__SILICON_REVISION__ < 2) +/* USB PLL_STABLE Bit May Not Accurately Reflect the USB PLL's Status */ +#define ANOMALY_05000420 (__SILICON_REVISION__ < 2) +/* TWI Fall Time (Tof) May Violate the Minimum I2C Specification */ +#define ANOMALY_05000421 (1) +/* TWI Input Capacitance (Ci) May Violate the Maximum I2C Specification */ +#define ANOMALY_05000422 (ANOMALY_BF527 && __SILICON_REVISION__ > 1) +/* Certain Ethernet Frames With Errors are Misclassified in RMII Mode */ +#define ANOMALY_05000423 (__SILICON_REVISION__ < 2) +/* Internal Voltage Regulator Not Trimmed */ +#define ANOMALY_05000424 (ANOMALY_BF527 && __SILICON_REVISION__ < 2) +/* Multichannel SPORT Channel Misalignment Under Specific Configuration */ +#define ANOMALY_05000425 (__SILICON_REVISION__ < 2) +/* Speculative Fetches of Indirect-Pointer Instructions Can Cause Spurious Hardware Errors */ +#define ANOMALY_05000426 (1) +/* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Reflects Buffer Status Instead of IRQ Status */ +#define ANOMALY_05000429 (__SILICON_REVISION__ < 2) +/* Software System Reset Corrupts PLL_LOCKCNT Register */ +#define ANOMALY_05000430 (ANOMALY_BF527 && __SILICON_REVISION__ > 1) +/* bfrom_SysControl() Does Not Clear SIC_IWR1 Before Executing PLL Programming Sequence */ +#define ANOMALY_05000432 (ANOMALY_BF526) /* Anomalies that don't exist on this proc */ #define ANOMALY_05000125 (0) @@ -97,6 +165,8 @@ #define ANOMALY_05000263 (0) #define ANOMALY_05000266 (0) #define ANOMALY_05000273 (0) +#define ANOMALY_05000285 (0) +#define ANOMALY_05000307 (0) #define ANOMALY_05000311 (0) #define ANOMALY_05000323 (0) #define ANOMALY_05000363 (0) diff --git a/arch/blackfin/mach-bf527/include/mach/bf527.h b/arch/blackfin/mach-bf527/include/mach/bf527.h index 056eb4b9cd2..144f08d3f8e 100644 --- a/arch/blackfin/mach-bf527/include/mach/bf527.h +++ b/arch/blackfin/mach-bf527/include/mach/bf527.h @@ -30,8 +30,6 @@ #ifndef __MACH_BF527_H__ #define __MACH_BF527_H__ -#define SUPPORTED_REVID 2 - #define OFFSET_(x) ((x) & 0x0000FFFF) /*some misc defines*/ @@ -112,16 +110,31 @@ #ifdef CONFIG_BF527 #define CPU "BF527" +#define CPUID 0x27e4 +#endif +#ifdef CONFIG_BF526 +#define CPU "BF526" +#define CPUID 0x27e4 #endif #ifdef CONFIG_BF525 #define CPU "BF525" +#define CPUID 0x27e4 +#endif +#ifdef CONFIG_BF524 +#define CPU "BF524" +#define CPUID 0x27e4 +#endif +#ifdef CONFIG_BF523 +#define CPU "BF523" +#define CPUID 0x27e4 #endif #ifdef CONFIG_BF522 #define CPU "BF522" +#define CPUID 0x27e4 #endif + #ifndef CPU -#define CPU "UNKNOWN" -#define CPUID 0x0 +#error Unknown CPU type - This kernel doesn't seem to be configured properly #endif #endif /* __MACH_BF527_H__ */ diff --git a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h index 2526b6ed6fa..75722d6008b 100644 --- a/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf527/include/mach/bfin_serial_5xx.h @@ -78,6 +78,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -119,7 +122,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -164,8 +166,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h b/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h index 6ac2ed7026e..68b55d03fed 100644 --- a/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h +++ b/arch/blackfin/mach-bf527/include/mach/defBF52x_base.h @@ -1840,6 +1840,33 @@ #define DPRESCALE 0xf /* Load Counter Register */ +/* CNT_COMMAND bit field options */ + +#define W1LCNT_ZERO 0x0001 /* write 1 to load CNT_COUNTER with zero */ +#define W1LCNT_MIN 0x0004 /* write 1 to load CNT_COUNTER from CNT_MIN */ +#define W1LCNT_MAX 0x0008 /* write 1 to load CNT_COUNTER from CNT_MAX */ + +#define W1LMIN_ZERO 0x0010 /* write 1 to load CNT_MIN with zero */ +#define W1LMIN_CNT 0x0020 /* write 1 to load CNT_MIN from CNT_COUNTER */ +#define W1LMIN_MAX 0x0080 /* write 1 to load CNT_MIN from CNT_MAX */ + +#define W1LMAX_ZERO 0x0100 /* write 1 to load CNT_MAX with zero */ +#define W1LMAX_CNT 0x0200 /* write 1 to load CNT_MAX from CNT_COUNTER */ +#define W1LMAX_MIN 0x0400 /* write 1 to load CNT_MAX from CNT_MIN */ + +/* CNT_CONFIG bit field options */ + +#define CNTMODE_QUADENC 0x0000 /* quadrature encoder mode */ +#define CNTMODE_BINENC 0x0100 /* binary encoder mode */ +#define CNTMODE_UDCNT 0x0200 /* up/down counter mode */ +#define CNTMODE_DIRCNT 0x0400 /* direction counter mode */ +#define CNTMODE_DIRTMR 0x0500 /* direction timer mode */ + +#define BNDMODE_COMP 0x0000 /* boundary compare mode */ +#define BNDMODE_ZERO 0x1000 /* boundary compare and zero mode */ +#define BNDMODE_CAPT 0x2000 /* boundary capture mode */ +#define BNDMODE_AEXT 0x3000 /* boundary auto-extend mode */ + /* Bit masks for OTP_CONTROL */ #define FUSE_FADDR 0x1ff /* OTP/Fuse Address */ diff --git a/arch/blackfin/mach-bf527/include/mach/portmux.h b/arch/blackfin/mach-bf527/include/mach/portmux.h index ae4d205bfcf..7f6da2c386b 100644 --- a/arch/blackfin/mach-bf527/include/mach/portmux.h +++ b/arch/blackfin/mach-bf527/include/mach/portmux.h @@ -67,6 +67,10 @@ #define P_UART1_RX (P_DEFINED | P_IDENT(GPIO_PG13) | P_FUNCT(1)) #endif +#define P_CNT_CZM (P_DEFINED | P_IDENT(GPIO_PF11) | P_FUNCT(3)) +#define P_CNT_CDG (P_DEFINED | P_IDENT(GPIO_PF12) | P_FUNCT(3)) +#define P_CNT_CUD (P_DEFINED | P_IDENT(GPIO_PF13) | P_FUNCT(3)) + #define P_HWAIT (P_DONTCARE) #define P_SPI0_SS (P_DEFINED | P_IDENT(GPIO_PG1) | P_FUNCT(0)) diff --git a/arch/blackfin/mach-bf533/boards/H8606.c b/arch/blackfin/mach-bf533/boards/H8606.c index c66a68f3023..72ac3ac8ef7 100644 --- a/arch/blackfin/mach-bf533/boards/H8606.c +++ b/arch/blackfin/mach-bf533/boards/H8606.c @@ -9,7 +9,7 @@ * Modified: * Copyright 2005 National ICT Australia (NICTA) * Copyright 2004-2006 Analog Devices Inc - * Copyright 2007 HV Sistemas S.L. + * Copyright 2007,2008 HV Sistemas S.L. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -64,18 +64,18 @@ static struct platform_device rtc_device = { static struct resource dm9000_resources[] = { [0] = { .start = 0x20300000, - .end = 0x20300000 + 1, + .end = 0x20300002, .flags = IORESOURCE_MEM, }, [1] = { - .start = 0x20300000 + 4, - .end = 0x20300000 + 5, + .start = 0x20300004, + .end = 0x20300006, .flags = IORESOURCE_MEM, }, [2] = { .start = IRQ_PF10, .end = IRQ_PF10, - .flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE), + .flags = (IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IRQF_SHARED | IRQF_TRIGGER_HIGH), }, }; @@ -140,18 +140,22 @@ static struct platform_device net2272_bfin_device = { #if defined(CONFIG_MTD_M25P80) || defined(CONFIG_MTD_M25P80_MODULE) static struct mtd_partition bfin_spi_flash_partitions[] = { { - .name = "bootloader(spi)", - .size = 0x00060000, + .name = "bootloader (spi)", + .size = 0x40000, .offset = 0, .mask_flags = MTD_CAP_ROM }, { - .name = "linux kernel(spi)", - .size = 0x100000, - .offset = 0x60000 + .name = "fpga (spi)", + .size = 0x30000, + .offset = 0x40000 }, { - .name = "file system(spi)", - .size = 0x6a0000, - .offset = 0x00160000, + .name = "linux kernel (spi)", + .size = 0x150000, + .offset = 0x70000 + }, { + .name = "jffs2 root file system (spi)", + .size = 0x640000, + .offset = 0x1c0000, } }; @@ -340,7 +344,7 @@ static struct platform_device bfin_sir_device = { static struct plat_serial8250_port serial8250_platform_data [] = { { - .membase = 0x20200000, + .membase = (void *)0x20200000, .mapbase = 0x20200000, .irq = IRQ_PF8, .flags = UPF_BOOT_AUTOCONF | UART_CONFIG_TYPE, @@ -348,7 +352,7 @@ static struct plat_serial8250_port serial8250_platform_data [] = { .regshift = 1, .uartclk = 66666667, }, { - .membase = 0x20200010, + .membase = (void *)0x20200010, .mapbase = 0x20200010, .irq = IRQ_PF8, .flags = UPF_BOOT_AUTOCONF | UART_CONFIG_TYPE, diff --git a/arch/blackfin/mach-bf533/head.S b/arch/blackfin/mach-bf533/head.S index 01b2b7ead5a..619685b94d9 100644 --- a/arch/blackfin/mach-bf533/head.S +++ b/arch/blackfin/mach-bf533/head.S @@ -78,6 +78,9 @@ ENTRY(_start_dma_code) r1 = PLL_BYPASS; /* Bypass the PLL? */ r1 = r1 << 8; /* Shift it over */ r0 = r1 | r0; /* add them all together */ +#ifdef ANOMALY_05000265 + r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */ +#endif p0.h = hi(PLL_CTL); p0.l = lo(PLL_CTL); /* Load the address */ diff --git a/arch/blackfin/mach-bf533/include/mach/anomaly.h b/arch/blackfin/mach-bf533/include/mach/anomaly.h index 8f7ea112fd3..f544fc56959 100644 --- a/arch/blackfin/mach-bf533/include/mach/anomaly.h +++ b/arch/blackfin/mach-bf533/include/mach/anomaly.h @@ -7,7 +7,7 @@ */ /* This file shoule be up to date with: - * - Revision C, 02/08/2008; ADSP-BF531/BF532/BF533 Blackfin Processor Anomaly List + * - Revision D, 06/18/2008; ADSP-BF531/BF532/BF533 Blackfin Processor Anomaly List */ #ifndef _MACH_ANOMALY_H_ @@ -97,11 +97,11 @@ /* UART STB Bit Incorrectly Affects Receiver Setting */ #define ANOMALY_05000231 (__SILICON_REVISION__ < 5) /* PPI_FS3 Is Not Driven in 2 or 3 Internal Frame Sync Transmit Modes */ -#define ANOMALY_05000233 (__SILICON_REVISION__ < 4) +#define ANOMALY_05000233 (__SILICON_REVISION__ < 6) /* Incorrect Revision Number in DSPID Register */ #define ANOMALY_05000234 (__SILICON_REVISION__ == 4) /* DF Bit in PLL_CTL Register Does Not Respond to Hardware Reset */ -#define ANOMALY_05000242 (__SILICON_REVISION__ < 4) +#define ANOMALY_05000242 (__SILICON_REVISION__ < 5) /* If I-Cache Is On, CSYNC/SSYNC/IDLE Around Change of Control Causes Failures */ #define ANOMALY_05000244 (__SILICON_REVISION__ < 5) /* Spurious Hardware Error from an Access in the Shadow of a Conditional Branch */ @@ -131,7 +131,7 @@ /* CSYNC/SSYNC/IDLE Causes Infinite Stall in Penultimate Instruction in Hardware Loop */ #define ANOMALY_05000264 (__SILICON_REVISION__ < 5) /* Sensitivity To Noise with Slow Input Edge Rates on External SPORT TX and RX Clocks */ -#define ANOMALY_05000265 (__SILICON_REVISION__ < 5) +#define ANOMALY_05000265 (1) /* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Increase */ #define ANOMALY_05000269 (__SILICON_REVISION__ < 5) /* High I/O Activity Causes Output Voltage of Internal Voltage Regulator (Vddint) to Decrease */ @@ -141,56 +141,59 @@ /* Certain Data Cache Writethrough Modes Fail for Vddint <= 0.9V */ #define ANOMALY_05000272 (1) /* Writes to Synchronous SDRAM Memory May Be Lost */ -#define ANOMALY_05000273 (1) +#define ANOMALY_05000273 (__SILICON_REVISION__ < 6) /* Timing Requirements Change for External Frame Sync PPI Modes with Non-Zero PPI_DELAY */ #define ANOMALY_05000276 (1) /* Writes to an I/O Data Register One SCLK Cycle after an Edge Is Detected May Clear Interrupt */ -#define ANOMALY_05000277 (1) +#define ANOMALY_05000277 (__SILICON_REVISION__ < 6) /* Disabling Peripherals with DMA Running May Cause DMA System Instability */ -#define ANOMALY_05000278 (1) +#define ANOMALY_05000278 (__SILICON_REVISION__ < 6) /* False Hardware Error Exception When ISR Context Is Not Restored */ -#define ANOMALY_05000281 (1) +#define ANOMALY_05000281 (__SILICON_REVISION__ < 6) /* Memory DMA Corruption with 32-Bit Data and Traffic Control */ -#define ANOMALY_05000282 (1) +#define ANOMALY_05000282 (__SILICON_REVISION__ < 6) /* System MMR Write Is Stalled Indefinitely When Killed in a Particular Stage */ -#define ANOMALY_05000283 (1) +#define ANOMALY_05000283 (__SILICON_REVISION__ < 6) /* SPORTs May Receive Bad Data If FIFOs Fill Up */ -#define ANOMALY_05000288 (1) +#define ANOMALY_05000288 (__SILICON_REVISION__ < 6) /* Memory-To-Memory DMA Source/Destination Descriptors Must Be in Same Memory Space */ -#define ANOMALY_05000301 (1) +#define ANOMALY_05000301 (__SILICON_REVISION__ < 6) /* SSYNCs After Writes To DMA MMR Registers May Not Be Handled Correctly */ #define ANOMALY_05000302 (__SILICON_REVISION__ < 5) /* New Feature: Additional Hysteresis on SPORT Input Pins (Not Available On Older Silicon) */ #define ANOMALY_05000305 (__SILICON_REVISION__ < 5) /* New Feature: Additional PPI Frame Sync Sampling Options (Not Available On Older Silicon) */ #define ANOMALY_05000306 (__SILICON_REVISION__ < 5) +/* SCKELOW Bit Does Not Maintain State Through Hibernate */ +#define ANOMALY_05000307 (1) /* False Hardware Errors Caused by Fetches at the Boundary of Reserved Memory */ #define ANOMALY_05000310 (1) /* Erroneous Flag (GPIO) Pin Operations under Specific Sequences */ -#define ANOMALY_05000311 (1) +#define ANOMALY_05000311 (__SILICON_REVISION__ < 6) /* Errors When SSYNC, CSYNC, or Loads to LT, LB and LC Registers Are Interrupted */ -#define ANOMALY_05000312 (1) +#define ANOMALY_05000312 (__SILICON_REVISION__ < 6) /* PPI Is Level-Sensitive on First Transfer */ -#define ANOMALY_05000313 (1) +#define ANOMALY_05000313 (__SILICON_REVISION__ < 6) /* Killed System MMR Write Completes Erroneously On Next System MMR Access */ -#define ANOMALY_05000315 (1) +#define ANOMALY_05000315 (__SILICON_REVISION__ < 6) /* Internal Voltage Regulator Values of 1.05V, 1.10V and 1.15V Not Allowed for LQFP Packages */ -#define ANOMALY_05000319 (ANOMALY_BF531 || ANOMALY_BF532) +#define ANOMALY_05000319 ((ANOMALY_BF531 || ANOMALY_BF532) && __SILICON_REVISION__ < 6) /* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */ -#define ANOMALY_05000357 (1) +#define ANOMALY_05000357 (__SILICON_REVISION__ < 6) /* UART Break Signal Issues */ #define ANOMALY_05000363 (__SILICON_REVISION__ < 5) /* PPI Underflow Error Goes Undetected in ITU-R 656 Mode */ #define ANOMALY_05000366 (1) /* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */ -#define ANOMALY_05000371 (1) +#define ANOMALY_05000371 (__SILICON_REVISION__ < 6) /* PPI Does Not Start Properly In Specific Mode */ -#define ANOMALY_05000400 (__SILICON_REVISION__ >= 5) +#define ANOMALY_05000400 (__SILICON_REVISION__ == 5) /* SSYNC Stalls Processor when Executed from Non-Cacheable Memory */ -#define ANOMALY_05000402 (__SILICON_REVISION__ >= 5) +#define ANOMALY_05000402 (__SILICON_REVISION__ == 5) /* Level-Sensitive External GPIO Wakeups May Cause Indefinite Stall */ #define ANOMALY_05000403 (1) - +/* Speculative Fetches Can Cause Undesired External FIFO Operations */ +#define ANOMALY_05000416 (1) /* These anomalies have been "phased" out of analog.com anomaly sheets and are * here to show running on older silicon just isn't feasible. @@ -268,5 +271,7 @@ /* Anomalies that don't exist on this proc */ #define ANOMALY_05000266 (0) #define ANOMALY_05000323 (0) +#define ANOMALY_05000353 (1) +#define ANOMALY_05000386 (1) #endif diff --git a/arch/blackfin/mach-bf533/include/mach/bf533.h b/arch/blackfin/mach-bf533/include/mach/bf533.h index 12a41693199..dfc8c1ad2d7 100644 --- a/arch/blackfin/mach-bf533/include/mach/bf533.h +++ b/arch/blackfin/mach-bf533/include/mach/bf533.h @@ -30,8 +30,6 @@ #ifndef __MACH_BF533_H__ #define __MACH_BF533_H__ -#define SUPPORTED_REVID 2 - #define OFFSET_(x) ((x) & 0x0000FFFF) /*some misc defines*/ @@ -143,19 +141,19 @@ #ifdef CONFIG_BF533 #define CPU "BF533" -#define CPUID 0x027a5000 +#define CPUID 0x27a5 #endif #ifdef CONFIG_BF532 #define CPU "BF532" -#define CPUID 0x0275A000 +#define CPUID 0x275A #endif #ifdef CONFIG_BF531 #define CPU "BF531" -#define CPUID 0x027a5000 +#define CPUID 0x27a5 #endif + #ifndef CPU -#define CPU "UNKNOWN" -#define CPUID 0x0 +#error Unknown CPU type - This kernel doesn't seem to be configured properly #endif #endif /* __MACH_BF533_H__ */ diff --git a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h index ebf592b59aa..34ab0e4e424 100644 --- a/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf533/include/mach/bfin_serial_5xx.h @@ -69,6 +69,8 @@ # endif #endif +#define BFIN_UART_TX_FIFO_SIZE 2 + struct bfin_serial_port { struct uart_port port; unsigned int old_status; @@ -83,7 +85,7 @@ struct bfin_serial_port { unsigned int rx_dma_channel; struct work_struct tx_dma_workqueue; #else -# if ANOMALY_05000230 +# if ANOMALY_05000363 unsigned int anomaly_threshold; # endif #endif @@ -111,7 +113,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -142,7 +143,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #define DRIVER_NAME "bfin-uart" -int nr_ports = BFIN_UART_NR_PORTS; static void bfin_serial_hw_init(struct bfin_serial_port *uart) { diff --git a/arch/blackfin/mach-bf537/boards/stamp.c b/arch/blackfin/mach-bf537/boards/stamp.c index 8482d22321f..dc5a30849c1 100644 --- a/arch/blackfin/mach-bf537/boards/stamp.c +++ b/arch/blackfin/mach-bf537/boards/stamp.c @@ -51,7 +51,6 @@ #include <asm/reboot.h> #include <asm/portmux.h> #include <asm/dpmc.h> -#include <linux/spi/ad7877.h> /* * Name the Board for the /proc/cpuinfo @@ -555,6 +554,7 @@ static struct bfin5xx_spi_chip spi_si3xxx_chip_info = { #endif #if defined(CONFIG_TOUCHSCREEN_AD7877) || defined(CONFIG_TOUCHSCREEN_AD7877_MODULE) +#include <linux/spi/ad7877.h> static struct bfin5xx_spi_chip spi_ad7877_chip_info = { .enable_dma = 0, .bits_per_word = 16, @@ -575,6 +575,28 @@ static const struct ad7877_platform_data bfin_ad7877_ts_info = { }; #endif +#if defined(CONFIG_TOUCHSCREEN_AD7879) || defined(CONFIG_TOUCHSCREEN_AD7879_MODULE) +#include <linux/spi/ad7879.h> +static struct bfin5xx_spi_chip spi_ad7879_chip_info = { + .enable_dma = 0, + .bits_per_word = 16, +}; + +static const struct ad7879_platform_data bfin_ad7879_ts_info = { + .model = 7879, /* Model = AD7879 */ + .x_plate_ohms = 620, /* 620 Ohm from the touch datasheet */ + .pressure_max = 10000, + .pressure_min = 0, + .first_conversion_delay = 3, /* wait 512us before do a first conversion */ + .acquisition_time = 1, /* 4us acquisition time per sample */ + .median = 2, /* do 8 measurements */ + .averaging = 1, /* take the average of 4 middle samples */ + .pen_down_acc_interval = 255, /* 9.4 ms */ + .gpio_output = 1, /* configure AUX/VBAT/GPIO as GPIO output */ + .gpio_default = 1, /* During initialization set GPIO = HIGH */ +}; +#endif + #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) static struct bfin5xx_spi_chip spidev_chip_info = { .enable_dma = 0, @@ -582,6 +604,13 @@ static struct bfin5xx_spi_chip spidev_chip_info = { }; #endif +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) +static struct bfin5xx_spi_chip lq035q1_spi_chip_info = { + .enable_dma = 0, + .bits_per_word = 8, +}; +#endif + #if defined(CONFIG_MTD_DATAFLASH) \ || defined(CONFIG_MTD_DATAFLASH_MODULE) @@ -721,6 +750,18 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &spi_ad7877_chip_info, }, #endif +#if defined(CONFIG_TOUCHSCREEN_AD7879) || defined(CONFIG_TOUCHSCREEN_AD7879_MODULE) + { + .modalias = "ad7879", + .platform_data = &bfin_ad7879_ts_info, + .irq = IRQ_PF7, + .max_speed_hz = 5000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 1, + .controller_data = &spi_ad7879_chip_info, + .mode = SPI_CPHA | SPI_CPOL, + }, +#endif #if defined(CONFIG_SPI_SPIDEV) || defined(CONFIG_SPI_SPIDEV_MODULE) { .modalias = "spidev", @@ -730,6 +771,16 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { .controller_data = &spidev_chip_info, }, #endif +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) + { + .modalias = "bfin-lq035q1-spi", + .max_speed_hz = 20000000, /* max spi clock (SCK) speed in HZ */ + .bus_num = 0, + .chip_select = 2, + .controller_data = &lq035q1_spi_chip_info, + .mode = SPI_CPHA | SPI_CPOL, + }, +#endif }; #if defined(CONFIG_SPI_BFIN) || defined(CONFIG_SPI_BFIN_MODULE) @@ -777,6 +828,34 @@ static struct platform_device bfin_fb_adv7393_device = { }; #endif +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) +#include <asm/bfin-lq035q1.h> + +static struct bfin_lq035q1fb_disp_info bfin_lq035q1_data = { + .mode = LQ035_NORM | LQ035_RGB | LQ035_RL | LQ035_TB, + .use_bl = 0, /* let something else control the LCD Blacklight */ + .gpio_bl = GPIO_PF7, +}; + +static struct resource bfin_lq035q1_resources[] = { + { + .start = IRQ_PPI_ERROR, + .end = IRQ_PPI_ERROR, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bfin_lq035q1_device = { + .name = "bfin-lq035q1", + .id = -1, + .num_resources = ARRAY_SIZE(bfin_lq035q1_resources), + .resource = bfin_lq035q1_resources, + .dev = { + .platform_data = &bfin_lq035q1_data, + }, +}; +#endif + #if defined(CONFIG_SERIAL_BFIN) || defined(CONFIG_SERIAL_BFIN_MODULE) static struct resource bfin_uart_resources[] = { #ifdef CONFIG_SERIAL_BFIN_UART0 @@ -997,6 +1076,10 @@ static struct platform_device *stamp_devices[] __initdata = { &bfin_fb_device, #endif +#if defined(CONFIG_FB_BFIN_LQ035Q1) || defined(CONFIG_FB_BFIN_LQ035Q1_MODULE) + &bfin_lq035q1_device, +#endif + #if defined(CONFIG_FB_BFIN_7393) || defined(CONFIG_FB_BFIN_7393_MODULE) &bfin_fb_adv7393_device, #endif diff --git a/arch/blackfin/mach-bf537/head.S b/arch/blackfin/mach-bf537/head.S index 12eb5cc571d..559a7eef7a3 100644 --- a/arch/blackfin/mach-bf537/head.S +++ b/arch/blackfin/mach-bf537/head.S @@ -87,6 +87,9 @@ ENTRY(_start_dma_code) r1 = PLL_BYPASS; /* Bypass the PLL? */ r1 = r1 << 8; /* Shift it over */ r0 = r1 | r0; /* add them all together */ +#ifdef ANOMALY_05000265 + r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */ +#endif p0.h = hi(PLL_CTL); p0.l = lo(PLL_CTL); /* Load the address */ diff --git a/arch/blackfin/mach-bf537/include/mach/anomaly.h b/arch/blackfin/mach-bf537/include/mach/anomaly.h index 8460ab9c324..c68992494f9 100644 --- a/arch/blackfin/mach-bf537/include/mach/anomaly.h +++ b/arch/blackfin/mach-bf537/include/mach/anomaly.h @@ -158,6 +158,8 @@ #define ANOMALY_05000266 (0) #define ANOMALY_05000311 (0) #define ANOMALY_05000323 (0) +#define ANOMALY_05000353 (1) #define ANOMALY_05000363 (0) +#define ANOMALY_05000386 (1) #endif diff --git a/arch/blackfin/mach-bf537/include/mach/bf537.h b/arch/blackfin/mach-bf537/include/mach/bf537.h index cfe2a221112..24d5c9d4232 100644 --- a/arch/blackfin/mach-bf537/include/mach/bf537.h +++ b/arch/blackfin/mach-bf537/include/mach/bf537.h @@ -30,8 +30,6 @@ #ifndef __MACH_BF537_H__ #define __MACH_BF537_H__ -#define SUPPORTED_REVID 2 - /* Masks for generic ERROR IRQ demultiplexing used in int-priority-sc.c */ #define SPI_ERR_MASK (TXCOL | RBSY | MODF | TXE) /* SPI_STAT */ @@ -123,19 +121,19 @@ #ifdef CONFIG_BF537 #define CPU "BF537" -#define CPUID 0x027c8000 +#define CPUID 0x27c8 #endif #ifdef CONFIG_BF536 #define CPU "BF536" -#define CPUID 0x027c8000 +#define CPUID 0x27c8 #endif #ifdef CONFIG_BF534 #define CPU "BF534" -#define CPUID 0x027c6000 +#define CPUID 0x27c6 #endif + #ifndef CPU -#define CPU "UNKNOWN" -#define CPUID 0x0 +#error Unknown CPU type - This kernel doesn't seem to be configured properly #endif #endif /* __MACH_BF537_H__ */ diff --git a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h index 1bf56ffa22f..b3f87e1d16a 100644 --- a/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf537/include/mach/bfin_serial_5xx.h @@ -78,6 +78,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -119,7 +122,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -164,8 +166,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf548/boards/cm_bf548.c b/arch/blackfin/mach-bf548/boards/cm_bf548.c index ce934ee174e..24192aaa927 100644 --- a/arch/blackfin/mach-bf548/boards/cm_bf548.c +++ b/arch/blackfin/mach-bf548/boards/cm_bf548.c @@ -36,11 +36,8 @@ #include <linux/spi/flash.h> #include <linux/irq.h> #include <linux/interrupt.h> -#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> -#endif #include <asm/bfin5xx_spi.h> -#include <asm/cplb.h> #include <asm/dma.h> #include <asm/gpio.h> #include <asm/nand.h> @@ -175,6 +172,7 @@ static struct resource bfin_uart_resources[] = { { .start = 0xFFC03100, .end = 0xFFC031FF, + .flags = IORESOURCE_MEM, }, #endif }; @@ -268,6 +266,16 @@ static struct resource musb_resources[] = { }, }; +static struct musb_hdrc_config musb_config = { + .multipoint = 0, + .dyn_fifo = 0, + .soft_con = 1, + .dma = 1, + .num_eps = 7, + .dma_channels = 7, + .gpio_vrsel = GPIO_PH6, +}; + static struct musb_hdrc_platform_data musb_plat = { #if defined(CONFIG_USB_MUSB_OTG) .mode = MUSB_OTG, @@ -276,7 +284,7 @@ static struct musb_hdrc_platform_data musb_plat = { #elif defined(CONFIG_USB_GADGET_MUSB_HDRC) .mode = MUSB_PERIPHERAL, #endif - .multipoint = 0, + .config = &musb_config, }; static u64 musb_dmamask = ~(u32)0; @@ -321,12 +329,12 @@ static struct mtd_partition partition_info[] = { { .name = "linux kernel(nand)", .offset = 0, - .size = 4 * SIZE_1M, + .size = 4 * 1024 * 1024, }, { .name = "file system(nand)", - .offset = 4 * SIZE_1M, - .size = (256 - 4) * SIZE_1M, + .offset = 4 * 1024 * 1024, + .size = (256 - 4) * 1024 * 1024, }, }; diff --git a/arch/blackfin/mach-bf548/boards/ezkit.c b/arch/blackfin/mach-bf548/boards/ezkit.c index 39357693046..5288187a3ac 100644 --- a/arch/blackfin/mach-bf548/boards/ezkit.c +++ b/arch/blackfin/mach-bf548/boards/ezkit.c @@ -38,11 +38,8 @@ #include <linux/irq.h> #include <linux/i2c.h> #include <linux/interrupt.h> -#if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) #include <linux/usb/musb.h> -#endif #include <asm/bfin5xx_spi.h> -#include <asm/cplb.h> #include <asm/dma.h> #include <asm/gpio.h> #include <asm/nand.h> @@ -186,6 +183,37 @@ static struct platform_device bf54x_kpad_device = { }; #endif +#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE) +#include <asm/bfin_rotary.h> + +static struct bfin_rotary_platform_data bfin_rotary_data = { + /*.rotary_up_key = KEY_UP,*/ + /*.rotary_down_key = KEY_DOWN,*/ + .rotary_rel_code = REL_WHEEL, + .rotary_button_key = KEY_ENTER, + .debounce = 10, /* 0..17 */ + .mode = ROT_QUAD_ENC | ROT_DEBE, +}; + +static struct resource bfin_rotary_resources[] = { + { + .start = IRQ_CNT, + .end = IRQ_CNT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device bfin_rotary_device = { + .name = "bfin-rotary", + .id = -1, + .num_resources = ARRAY_SIZE(bfin_rotary_resources), + .resource = bfin_rotary_resources, + .dev = { + .platform_data = &bfin_rotary_data, + }, +}; +#endif + #if defined(CONFIG_RTC_DRV_BFIN) || defined(CONFIG_RTC_DRV_BFIN_MODULE) static struct platform_device rtc_device = { .name = "rtc-bfin", @@ -314,6 +342,16 @@ static struct resource musb_resources[] = { }, }; +static struct musb_hdrc_config musb_config = { + .multipoint = 0, + .dyn_fifo = 0, + .soft_con = 1, + .dma = 1, + .num_eps = 7, + .dma_channels = 7, + .gpio_vrsel = GPIO_PE7, +}; + static struct musb_hdrc_platform_data musb_plat = { #if defined(CONFIG_USB_MUSB_OTG) .mode = MUSB_OTG, @@ -322,7 +360,7 @@ static struct musb_hdrc_platform_data musb_plat = { #elif defined(CONFIG_USB_GADGET_MUSB_HDRC) .mode = MUSB_PERIPHERAL, #endif - .multipoint = 0, + .config = &musb_config, }; static u64 musb_dmamask = ~(u32)0; @@ -367,7 +405,7 @@ static struct mtd_partition partition_info[] = { { .name = "linux kernel(nand)", .offset = 0, - .size = 4 * SIZE_1M, + .size = 4 * 1024 * 1024, }, { .name = "file system(nand)", @@ -424,7 +462,7 @@ static struct mtd_partition ezkit_partitions[] = { .offset = 0, }, { .name = "linux kernel(nor)", - .size = 0x1C0000, + .size = 0x400000, .offset = MTDPART_OFS_APPEND, }, { .name = "file system(nor)", @@ -441,7 +479,7 @@ static struct physmap_flash_data ezkit_flash_data = { static struct resource ezkit_flash_resource = { .start = 0x20000000, - .end = 0x20ffffff, + .end = 0x21ffffff, .flags = IORESOURCE_MEM, }; @@ -551,7 +589,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = { { .modalias = "ad7877", .platform_data = &bfin_ad7877_ts_info, - .irq = IRQ_PJ11, + .irq = IRQ_PJ11, /* newer boards (Rev 1.4+) use IRQ_PB4 */ .max_speed_hz = 12500000, /* max spi clock (SCK) speed in HZ */ .bus_num = 0, .chip_select = 2, @@ -810,6 +848,10 @@ static struct platform_device *ezkit_devices[] __initdata = { &bf54x_kpad_device, #endif +#if defined(CONFIG_JOYSTICK_BFIN_ROTARY) || defined(CONFIG_JOYSTICK_BFIN_ROTARY_MODULE) + &bfin_rotary_device, +#endif + #if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) &i2c_bfin_twi0_device, #if !defined(CONFIG_BF542) diff --git a/arch/blackfin/mach-bf548/head.S b/arch/blackfin/mach-bf548/head.S index 4d5cfeacb12..051b05c8802 100644 --- a/arch/blackfin/mach-bf548/head.S +++ b/arch/blackfin/mach-bf548/head.S @@ -73,25 +73,19 @@ ENTRY(_start_dma_code) w[p0] = r0.l; ssync; -#if defined(CONFIG_BF54x) + /* enable self refresh via SRREQ */ P2.H = hi(EBIU_RSTCTL); P2.L = lo(EBIU_RSTCTL); R0 = [P2]; BITSET (R0, 3); -#else - P2.H = hi(EBIU_SDGCTL); - P2.L = lo(EBIU_SDGCTL); - R0 = [P2]; - BITSET (R0, 24); -#endif [P2] = R0; SSYNC; -#if defined(CONFIG_BF54x) + + /* wait for SRACK bit to be set */ .LSRR_MODE: R0 = [P2]; CC = BITTST(R0, 4); if !CC JUMP .LSRR_MODE; -#endif r0 = CONFIG_VCO_MULT & 63; /* Load the VCO multiplier */ r0 = r0 << 9; /* Shift it over, */ @@ -100,6 +94,9 @@ ENTRY(_start_dma_code) r1 = PLL_BYPASS; /* Bypass the PLL? */ r1 = r1 << 8; /* Shift it over */ r0 = r1 | r0; /* add them all together */ +#ifdef ANOMALY_05000265 + r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */ +#endif p0.h = hi(PLL_CTL); p0.l = lo(PLL_CTL); /* Load the address */ @@ -123,7 +120,7 @@ ENTRY(_start_dma_code) w[p0] = r0.l; ssync; -#if defined(CONFIG_BF54x) + /* disable self refresh by clearing SRREQ */ P2.H = hi(EBIU_RSTCTL); P2.L = lo(EBIU_RSTCTL); R0 = [P2]; @@ -155,41 +152,6 @@ ENTRY(_start_dma_code) r0.h = hi(mem_DDRCTL2); [p0] = r0; ssync; -#else - p0.l = lo(EBIU_SDRRC); - p0.h = hi(EBIU_SDRRC); - r0 = mem_SDRRC; - w[p0] = r0.l; - ssync; - - p0.l = LO(EBIU_SDBCTL); - p0.h = HI(EBIU_SDBCTL); /* SDRAM Memory Bank Control Register */ - r0 = mem_SDBCTL; - w[p0] = r0.l; - ssync; - - P2.H = hi(EBIU_SDGCTL); - P2.L = lo(EBIU_SDGCTL); - R0 = [P2]; - BITCLR (R0, 24); - p0.h = hi(EBIU_SDSTAT); - p0.l = lo(EBIU_SDSTAT); - r2.l = w[p0]; - cc = bittst(r2,3); - if !cc jump .Lskip; - NOP; - BITSET (R0, 23); -.Lskip: - [P2] = R0; - SSYNC; - - R0.L = lo(mem_SDGCTL); - R0.H = hi(mem_SDGCTL); - R1 = [p2]; - R1 = R1 | R0; - [P2] = R1; - SSYNC; -#endif RTS; ENDPROC(_start_dma_code) diff --git a/arch/blackfin/mach-bf548/include/mach/anomaly.h b/arch/blackfin/mach-bf548/include/mach/anomaly.h index 3ad59655881..816b09278f6 100644 --- a/arch/blackfin/mach-bf548/include/mach/anomaly.h +++ b/arch/blackfin/mach-bf548/include/mach/anomaly.h @@ -2,18 +2,18 @@ * File: include/asm-blackfin/mach-bf548/anomaly.h * Bugs: Enter bugs at http://blackfin.uclinux.org/ * - * Copyright (C) 2004-2007 Analog Devices Inc. + * Copyright (C) 2004-2008 Analog Devices Inc. * Licensed under the GPL-2 or later. */ /* This file shoule be up to date with: - * - Revision E, 11/28/2007; ADSP-BF542/BF544/BF547/BF548/BF549 Blackfin Processor Anomaly List + * - Revision G, 08/07/2008; ADSP-BF542/BF544/BF547/BF548/BF549 Blackfin Processor Anomaly List */ #ifndef _MACH_ANOMALY_H_ #define _MACH_ANOMALY_H_ -/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot 2 Not Supported */ +/* Multi-Issue Instruction with dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported */ #define ANOMALY_05000074 (1) /* DMA_RUN Bit Is Not Valid after a Peripheral Receive Channel DMA Stops */ #define ANOMALY_05000119 (1) @@ -36,14 +36,14 @@ /* TWI Slave Boot Mode Is Not Functional */ #define ANOMALY_05000324 (__SILICON_REVISION__ < 1) /* External FIFO Boot Mode Is Not Functional */ -#define ANOMALY_05000325 (__SILICON_REVISION__ < 1) +#define ANOMALY_05000325 (__SILICON_REVISION__ < 2) /* Data Lost When Core and DMA Accesses Are Made to the USB FIFO Simultaneously */ #define ANOMALY_05000327 (__SILICON_REVISION__ < 1) /* Incorrect Access of OTP_STATUS During otp_write() Function */ #define ANOMALY_05000328 (__SILICON_REVISION__ < 1) /* Synchronous Burst Flash Boot Mode Is Not Functional */ #define ANOMALY_05000329 (__SILICON_REVISION__ < 1) -/* Host DMA Boot Mode Is Not Functional */ +/* Host DMA Boot Modes Are Not Functional */ #define ANOMALY_05000330 (__SILICON_REVISION__ < 1) /* Inadequate Timing Margins on DDR DQS to DQ and DQM Skew */ #define ANOMALY_05000334 (__SILICON_REVISION__ < 1) @@ -61,26 +61,102 @@ #define ANOMALY_05000344 (__SILICON_REVISION__ < 1) /* USB Calibration Value Is Not Intialized */ #define ANOMALY_05000346 (__SILICON_REVISION__ < 1) -/* Boot ROM Kernel Incorrectly Alters Reset Value of USB Register */ +/* USB Calibration Value to use */ +#define ANOMALY_05000346_value 0x5411 +/* Preboot Routine Incorrectly Alters Reset Value of USB Register */ #define ANOMALY_05000347 (__SILICON_REVISION__ < 1) /* Data Lost when Core Reads SDH Data FIFO */ #define ANOMALY_05000349 (__SILICON_REVISION__ < 1) /* PLL Status Register Is Inaccurate */ #define ANOMALY_05000351 (__SILICON_REVISION__ < 1) +/* bfrom_SysControl() Firmware Function Performs Improper System Reset */ +#define ANOMALY_05000353 (__SILICON_REVISION__ < 2) +/* Regulator Programming Blocked when Hibernate Wakeup Source Remains Active */ +#define ANOMALY_05000355 (__SILICON_REVISION__ < 1) +/* System Stalled During A Core Access To AMC While A Core Access To NFC FIFO Is Required */ +#define ANOMALY_05000356 (__SILICON_REVISION__ < 1) /* Serial Port (SPORT) Multichannel Transmit Failure when Channel 0 Is Disabled */ #define ANOMALY_05000357 (1) /* External Memory Read Access Hangs Core With PLL Bypass */ #define ANOMALY_05000360 (1) /* DMAs that Go Urgent during Tight Core Writes to External Memory Are Blocked */ #define ANOMALY_05000365 (1) +/* WURESET Bit In SYSCR Register Does Not Properly Indicate Hibernate Wake-Up */ +#define ANOMALY_05000367 (__SILICON_REVISION__ < 1) /* Addressing Conflict between Boot ROM and Asynchronous Memory */ #define ANOMALY_05000369 (1) +/* Default PLL MSEL and SSEL Settings Can Cause 400MHz Product To Violate Specifications */ +#define ANOMALY_05000370 (__SILICON_REVISION__ < 1) /* Possible RETS Register Corruption when Subroutine Is under 5 Cycles in Duration */ -#define ANOMALY_05000371 (1) +#define ANOMALY_05000371 (__SILICON_REVISION__ < 2) +/* USB DP/DM Data Pins May Lose State When Entering Hibernate */ +#define ANOMALY_05000372 (__SILICON_REVISION__ < 1) /* Mobile DDR Operation Not Functional */ #define ANOMALY_05000377 (1) /* Security/Authentication Speedpath Causes Authentication To Fail To Initiate */ -#define ANOMALY_05000378 (1) +#define ANOMALY_05000378 (__SILICON_REVISION__ < 2) +/* 16-Bit NAND FLASH Boot Mode Is Not Functional */ +#define ANOMALY_05000379 (1) +/* 8-Bit NAND Flash Boot Mode Not Functional */ +#define ANOMALY_05000382 (__SILICON_REVISION__ < 1) +/* Some ATAPI Modes Are Not Functional */ +#define ANOMALY_05000383 (1) +/* Boot from OTP Memory Not Functional */ +#define ANOMALY_05000385 (__SILICON_REVISION__ < 1) +/* bfrom_SysControl() Firmware Routine Not Functional */ +#define ANOMALY_05000386 (__SILICON_REVISION__ < 1) +/* Programmable Preboot Settings Not Functional */ +#define ANOMALY_05000387 (__SILICON_REVISION__ < 1) +/* CRC32 Checksum Support Not Functional */ +#define ANOMALY_05000388 (__SILICON_REVISION__ < 1) +/* Reset Vector Must Not Be in SDRAM Memory Space */ +#define ANOMALY_05000389 (__SILICON_REVISION__ < 1) +/* Changed Meaning of BCODE Field in SYSCR Register */ +#define ANOMALY_05000390 (__SILICON_REVISION__ < 1) +/* Repeated Boot from Page-Mode or Burst-Mode Flash Memory May Fail */ +#define ANOMALY_05000391 (__SILICON_REVISION__ < 1) +/* pTempCurrent Not Present in ADI_BOOT_DATA Structure */ +#define ANOMALY_05000392 (__SILICON_REVISION__ < 1) +/* Deprecated Value of dTempByteCount in ADI_BOOT_DATA Structure */ +#define ANOMALY_05000393 (__SILICON_REVISION__ < 1) +/* Log Buffer Not Functional */ +#define ANOMALY_05000394 (__SILICON_REVISION__ < 1) +/* Hook Routine Not Functional */ +#define ANOMALY_05000395 (__SILICON_REVISION__ < 1) +/* Header Indirect Bit Not Functional */ +#define ANOMALY_05000396 (__SILICON_REVISION__ < 1) +/* BK_ONES, BK_ZEROS, and BK_DATECODE Constants Not Functional */ +#define ANOMALY_05000397 (__SILICON_REVISION__ < 1) +/* Lockbox SESR Disallows Certain User Interrupts */ +#define ANOMALY_05000404 (__SILICON_REVISION__ < 2) +/* Lockbox SESR Firmware Does Not Save/Restore Full Context */ +#define ANOMALY_05000405 (1) +/* Lockbox SESR Argument Checking Does Not Check L2 Memory Protection Range */ +#define ANOMALY_05000406 (__SILICON_REVISION__ < 2) +/* Lockbox SESR Firmware Arguments Are Not Retained After First Initialization */ +#define ANOMALY_05000407 (__SILICON_REVISION__ < 2) +/* Lockbox Firmware Memory Cleanup Routine Does not Clear Registers */ +#define ANOMALY_05000408 (1) +/* Lockbox firmware leaves MDMA0 channel enabled */ +#define ANOMALY_05000409 (__SILICON_REVISION__ < 2) +/* bfrom_SysControl() Firmware Function Cannot be Used to Enter Power Saving Modes */ +#define ANOMALY_05000411 (__SILICON_REVISION__ < 2) +/* NAND Boot Mode Not Compatible With Some NAND Flash Devices */ +#define ANOMALY_05000413 (__SILICON_REVISION__ < 2) +/* OTP_CHECK_FOR_PREV_WRITE Bit is Not Functional in bfrom_OtpWrite() API */ +#define ANOMALY_05000414 (__SILICON_REVISION__ < 2) +/* Speculative Fetches Can Cause Undesired External FIFO Operations */ +#define ANOMALY_05000416 (1) +/* Multichannel SPORT Channel Misalignment Under Specific Configuration */ +#define ANOMALY_05000425 (1) +/* Speculative Fetches of Indirect-Pointer Instructions Can Cause Spurious Hardware Errors */ +#define ANOMALY_05000426 (1) +/* CORE_EPPI_PRIO bit and SYS_EPPI_PRIO bit in the HMDMA1_CONTROL register are not functional */ +#define ANOMALY_05000427 (__SILICON_REVISION__ < 2) +/* WB_EDGE Bit in NFC_IRQSTAT Incorrectly Behaves as a Buffer Status Bit Instead of an IRQ Status Bit */ +#define ANOMALY_05000429 (__SILICON_REVISION__ < 2) +/* Software System Reset Corrupts PLL_LOCKCNT Register */ +#define ANOMALY_05000430 (__SILICON_REVISION__ >= 2) /* Anomalies that don't exist on this proc */ #define ANOMALY_05000125 (0) @@ -93,6 +169,7 @@ #define ANOMALY_05000263 (0) #define ANOMALY_05000266 (0) #define ANOMALY_05000273 (0) +#define ANOMALY_05000307 (0) #define ANOMALY_05000311 (0) #define ANOMALY_05000323 (0) #define ANOMALY_05000363 (0) diff --git a/arch/blackfin/mach-bf548/include/mach/bf548.h b/arch/blackfin/mach-bf548/include/mach/bf548.h index e748588e893..49f9b403d45 100644 --- a/arch/blackfin/mach-bf548/include/mach/bf548.h +++ b/arch/blackfin/mach-bf548/include/mach/bf548.h @@ -30,8 +30,6 @@ #ifndef __MACH_BF548_H__ #define __MACH_BF548_H__ -#define SUPPORTED_REVID 0 - #define OFFSET_(x) ((x) & 0x0000FFFF) /*some misc defines*/ @@ -108,20 +106,23 @@ #if defined(CONFIG_BF542) # define CPU "BF542" -# define CPUID 0x027c8000 +# define CPUID 0x27de #elif defined(CONFIG_BF544) -# define CPU "BF544" -# define CPUID 0x027c8000 +# define CPU "BF544" +# define CPUID 0x27de #elif defined(CONFIG_BF547) -# define CPU "BF547" +# define CPU "BF547" +# define CPUID 0x27de #elif defined(CONFIG_BF548) -# define CPU "BF548" -# define CPUID 0x027c6000 +# define CPU "BF548" +# define CPUID 0x27de #elif defined(CONFIG_BF549) -# define CPU "BF549" -#else -# define CPU "UNKNOWN" -# define CPUID 0x0 +# define CPU "BF549" +# define CPUID 0x27de +#endif + +#ifndef CPU +#error Unknown CPU type - This kernel doesn't seem to be configured properly #endif #endif /* __MACH_BF48_H__ */ diff --git a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h index 5e29446a8e0..e4cf35e7ab9 100644 --- a/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf548/include/mach/bfin_serial_5xx.h @@ -82,6 +82,9 @@ # define CONFIG_UART1_RTS_PIN -1 # endif #endif + +#define BFIN_UART_TX_FIFO_SIZE 2 + /* * The pin configuration is different from schematic */ @@ -105,7 +108,6 @@ struct bfin_serial_port { #endif }; -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -170,8 +172,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #endif }; -int nr_ports = ARRAY_SIZE(bfin_serial_resource); - #define DRIVER_NAME "bfin-uart" static void bfin_serial_hw_init(struct bfin_serial_port *uart) diff --git a/arch/blackfin/mach-bf548/include/mach/mem_map.h b/arch/blackfin/mach-bf548/include/mach/mem_map.h index f99f47bc3a0..a2228428dc0 100644 --- a/arch/blackfin/mach-bf548/include/mach/mem_map.h +++ b/arch/blackfin/mach-bf548/include/mach/mem_map.h @@ -94,13 +94,13 @@ #endif /*CONFIG_BFIN_DCACHE*/ /* Level 2 Memory */ -#if !defined(CONFIG_BF542) -# define L2_START 0xFEB00000 -# if defined(CONFIG_BF544) -# define L2_LENGTH 0x10000 -# else -# define L2_LENGTH 0x20000 -# endif +#define L2_START 0xFEB00000 +#if defined(CONFIG_BF542) +# define L2_LENGTH 0 +#elif defined(CONFIG_BF544) +# define L2_LENGTH 0x10000 +#else +# define L2_LENGTH 0x20000 #endif /* Scratch Pad Memory */ diff --git a/arch/blackfin/mach-bf561/head.S b/arch/blackfin/mach-bf561/head.S index 75ea6a90582..0b28137b3de 100644 --- a/arch/blackfin/mach-bf561/head.S +++ b/arch/blackfin/mach-bf561/head.S @@ -77,6 +77,9 @@ ENTRY(_start_dma_code) r1 = PLL_BYPASS; /* Bypass the PLL? */ r1 = r1 << 8; /* Shift it over */ r0 = r1 | r0; /* add them all together */ +#ifdef ANOMALY_05000265 + r0 = BITSET(r0, 15); /* Add 250 mV of hysteresis to SPORT input pins */ +#endif p0.h = hi(PLL_CTL); p0.l = lo(PLL_CTL); /* Load the address */ diff --git a/arch/blackfin/mach-bf561/include/mach/anomaly.h b/arch/blackfin/mach-bf561/include/mach/anomaly.h index 5c5d7d7d695..22990df04ae 100644 --- a/arch/blackfin/mach-bf561/include/mach/anomaly.h +++ b/arch/blackfin/mach-bf561/include/mach/anomaly.h @@ -270,5 +270,7 @@ #define ANOMALY_05000183 (0) #define ANOMALY_05000273 (0) #define ANOMALY_05000311 (0) +#define ANOMALY_05000353 (1) +#define ANOMALY_05000386 (1) #endif diff --git a/arch/blackfin/mach-bf561/include/mach/bf561.h b/arch/blackfin/mach-bf561/include/mach/bf561.h index 3ef9e5f3613..18b1b3a223a 100644 --- a/arch/blackfin/mach-bf561/include/mach/bf561.h +++ b/arch/blackfin/mach-bf561/include/mach/bf561.h @@ -30,8 +30,6 @@ #ifndef __MACH_BF561_H__ #define __MACH_BF561_H__ -#define SUPPORTED_REVID 0x3 - #define OFFSET_(x) ((x) & 0x0000FFFF) /*some misc defines*/ @@ -213,11 +211,11 @@ #ifdef CONFIG_BF561 #define CPU "BF561" -#define CPUID 0x027bb000 +#define CPUID 0x27bb #endif + #ifndef CPU -#define CPU "UNKNOWN" -#define CPUID 0x0 +#error Unknown CPU type - This kernel doesn't seem to be configured properly #endif #endif /* __MACH_BF561_H__ */ diff --git a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h index 8aa02780e64..f5327264357 100644 --- a/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h +++ b/arch/blackfin/mach-bf561/include/mach/bfin_serial_5xx.h @@ -69,6 +69,8 @@ # endif #endif +#define BFIN_UART_TX_FIFO_SIZE 2 + struct bfin_serial_port { struct uart_port port; unsigned int old_status; @@ -83,7 +85,7 @@ struct bfin_serial_port { unsigned int rx_dma_channel; struct work_struct tx_dma_workqueue; #else -# if ANOMALY_05000230 +# if ANOMALY_05000363 unsigned int anomaly_threshold; # endif #endif @@ -111,7 +113,6 @@ static inline void UART_CLEAR_LSR(struct bfin_serial_port *uart) bfin_write16(uart->port.membase + OFFSET_LSR, -1); } -struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; struct bfin_serial_res { unsigned long uart_base_addr; int uart_irq; @@ -142,7 +143,6 @@ struct bfin_serial_res bfin_serial_resource[] = { #define DRIVER_NAME "bfin-uart" -int nr_ports = BFIN_UART_NR_PORTS; static void bfin_serial_hw_init(struct bfin_serial_port *uart) { diff --git a/arch/blackfin/mach-bf561/include/mach/mem_map.h b/arch/blackfin/mach-bf561/include/mach/mem_map.h index c26d8486cc4..f1d4c0637bd 100644 --- a/arch/blackfin/mach-bf561/include/mach/mem_map.h +++ b/arch/blackfin/mach-bf561/include/mach/mem_map.h @@ -35,9 +35,16 @@ /* Memory Map for ADSP-BF561 processors */ #ifdef CONFIG_BF561 -#define L1_CODE_START 0xFFA00000 -#define L1_DATA_A_START 0xFF800000 -#define L1_DATA_B_START 0xFF900000 +#define COREA_L1_CODE_START 0xFFA00000 +#define COREA_L1_DATA_A_START 0xFF800000 +#define COREA_L1_DATA_B_START 0xFF900000 +#define COREB_L1_CODE_START 0xFF600000 +#define COREB_L1_DATA_A_START 0xFF400000 +#define COREB_L1_DATA_B_START 0xFF500000 + +#define L1_CODE_START COREA_L1_CODE_START +#define L1_DATA_A_START COREA_L1_DATA_A_START +#define L1_DATA_B_START COREA_L1_DATA_B_START #define L1_CODE_LENGTH 0x4000 @@ -72,7 +79,10 @@ /* Scratch Pad Memory */ -#define L1_SCRATCH_START 0xFFB00000 +#define COREA_L1_SCRATCH_START 0xFFB00000 +#define COREB_L1_SCRATCH_START 0xFF700000 + +#define L1_SCRATCH_START COREA_L1_SCRATCH_START #define L1_SCRATCH_LENGTH 0x1000 #endif /* _MEM_MAP_533_H_ */ diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index 847c172a99e..c13fa8da28c 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -129,6 +129,18 @@ ENTRY(_ex_icplb_miss) #else call __cplb_hdr; #endif + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* While we were processing this, did we double fault? */ + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + r6.l = lo(SEQSTAT_EXCAUSE); + r6.h = hi(SEQSTAT_EXCAUSE); + r7 = r7 & r6; + r6 = 0x25; + CC = R7 == R6; + if CC JUMP _double_fault; +#endif + DEBUG_HWTRACE_RESTORE(p5, r7) RESTORE_ALL_SYS SP = EX_SCRATCH_REG; @@ -136,11 +148,8 @@ ENTRY(_ex_icplb_miss) ENDPROC(_ex_icplb_miss) ENTRY(_ex_syscall) - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; raise 15; /* invoked by TRAP #0, for sys call */ - sp = EX_SCRATCH_REG; - rtx + jump.s _bfin_return_from_exception; ENDPROC(_ex_syscall) ENTRY(_ex_soft_bp) @@ -181,8 +190,8 @@ ENTRY(_ex_single_step) if cc jump .Lfind_priority_done; jump.s .Lfind_priority_start; .Lfind_priority_done: - p4.l = _debugger_step; - p4.h = _debugger_step; + p4.l = _kgdb_single_step; + p4.h = _kgdb_single_step; r6 = [p4]; cc = r6 == 0; if cc jump .Ldo_single_step; @@ -250,6 +259,29 @@ ENTRY(_bfin_return_from_exception) R7=LC1; LC1=R7; #endif + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* While we were processing the current exception, + * did we cause another, and double fault? + */ + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + r6.l = lo(SEQSTAT_EXCAUSE); + r6.h = hi(SEQSTAT_EXCAUSE); + r7 = r7 & r6; + r6 = 0x25; + CC = R7 == R6; + if CC JUMP _double_fault; + + /* Did we cause a HW error? */ + p5.l = lo(ILAT); + p5.h = hi(ILAT); + r6 = [p5]; + r7 = 0x20; /* Did I just cause anther HW error? */ + r7 = r7 & r1; + CC = R7 == R6; + if CC JUMP _double_fault; +#endif + (R7:6,P5:4) = [sp++]; ASTAT = [sp++]; sp = EX_SCRATCH_REG; @@ -292,6 +324,14 @@ ENTRY(_ex_trap_c) [p4] = p5; csync; +#ifndef CONFIG_DEBUG_DOUBLEFAULT + /* + * Save these registers, as they are only valid in exception context + * (where we are now - as soon as we defer to IRQ5, they can change) + * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3, + * but they are not very interesting, so don't save them + */ + p4.l = lo(DCPLB_FAULT_ADDR); p4.h = hi(DCPLB_FAULT_ADDR); r7 = [p4]; @@ -304,12 +344,11 @@ ENTRY(_ex_trap_c) p5.l = _saved_icplb_fault_addr; [p5] = r7; - p4.l = _excpt_saved_stuff; - p4.h = _excpt_saved_stuff; - r6 = retx; + p4.l = _saved_retx; + p4.h = _saved_retx; [p4] = r6; - +#endif r6 = SYSCFG; [p4 + 4] = r6; BITCLR(r6, 0); @@ -327,59 +366,56 @@ ENTRY(_ex_trap_c) r6 = 0x3f; sti r6; - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SP = EX_SCRATCH_REG; raise 5; - rtx; + jump.s _bfin_return_from_exception; ENDPROC(_ex_trap_c) /* We just realized we got an exception, while we were processing a different * exception. This is a unrecoverable event, so crash */ ENTRY(_double_fault) - /* Turn caches & protection off, to ensure we don't get any more - * double exceptions - */ - - P4.L = LO(IMEM_CONTROL); - P4.H = HI(IMEM_CONTROL); - - R5 = [P4]; /* Control Register*/ - BITCLR(R5,ENICPLB_P); - SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - - P4.L = LO(DMEM_CONTROL); - P4.H = HI(DMEM_CONTROL); - R5 = [P4]; - BITCLR(R5,ENDCPLB_P); - SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ - .align 8; - [P4] = R5; - SSYNC; - - /* Fix up the stack */ - (R7:6,P5:4) = [sp++]; - ASTAT = [sp++]; - SP = EX_SCRATCH_REG; - - /* We should be out of the exception stack, and back down into - * kernel or user space stack - */ - SAVE_ALL_SYS + /* Turn caches & protection off, to ensure we don't get any more + * double exceptions + */ + + P4.L = LO(IMEM_CONTROL); + P4.H = HI(IMEM_CONTROL); + + R5 = [P4]; /* Control Register*/ + BITCLR(R5,ENICPLB_P); + SSYNC; /* SSYNC required before writing to IMEM_CONTROL. */ + .align 8; + [P4] = R5; + SSYNC; + + P4.L = LO(DMEM_CONTROL); + P4.H = HI(DMEM_CONTROL); + R5 = [P4]; + BITCLR(R5,ENDCPLB_P); + SSYNC; /* SSYNC required before writing to DMEM_CONTROL. */ + .align 8; + [P4] = R5; + SSYNC; + + /* Fix up the stack */ + (R7:6,P5:4) = [sp++]; + ASTAT = [sp++]; + SP = EX_SCRATCH_REG; + + /* We should be out of the exception stack, and back down into + * kernel or user space stack + */ + SAVE_ALL_SYS /* The dumping functions expect the return address in the RETI * slot. */ r6 = retx; [sp + PT_PC] = r6; - r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ - SP += -12; - call _double_fault_c; - SP += 12; + r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */ + SP += -12; + call _double_fault_c; + SP += 12; .L_double_fault_panic: JUMP .L_double_fault_panic @@ -388,8 +424,8 @@ ENDPROC(_double_fault) ENTRY(_exception_to_level5) SAVE_ALL_SYS - p4.l = _excpt_saved_stuff; - p4.h = _excpt_saved_stuff; + p4.l = _saved_retx; + p4.h = _saved_retx; r6 = [p4]; [sp + PT_PC] = r6; @@ -420,6 +456,17 @@ ENTRY(_exception_to_level5) call _trap_c; SP += 12; +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* Grab ILAT */ + p2.l = lo(ILAT); + p2.h = hi(ILAT); + r0 = [p2]; + r1 = 0x20; /* Did I just cause anther HW error? */ + r0 = r0 & r1; + CC = R0 == R1; + if CC JUMP _double_fault; +#endif + call _ret_from_exception; RESTORE_ALL_SYS rti; @@ -436,7 +483,48 @@ ENTRY(_trap) /* Exception: 4th entry into system event table(supervisor mode)*/ /* Try to deal with syscalls quickly. */ [--sp] = ASTAT; [--sp] = (R7:6,P5:4); + +#if ANOMALY_05000283 || ANOMALY_05000315 + cc = r7 == r7; + p5.h = HI(CHIPID); + p5.l = LO(CHIPID); + if cc jump 1f; + r7.l = W[p5]; +1: +#endif + +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* + * Save these registers, as they are only valid in exception context + * (where we are now - as soon as we defer to IRQ5, they can change) + * DCPLB_STATUS and ICPLB_STATUS are also only valid in EVT3, + * but they are not very interesting, so don't save them + */ + + p4.l = lo(DCPLB_FAULT_ADDR); + p4.h = hi(DCPLB_FAULT_ADDR); + r7 = [p4]; + p5.h = _saved_dcplb_fault_addr; + p5.l = _saved_dcplb_fault_addr; + [p5] = r7; + + r7 = [p4 + (ICPLB_FAULT_ADDR - DCPLB_FAULT_ADDR)]; + p5.h = _saved_icplb_fault_addr; + p5.l = _saved_icplb_fault_addr; + [p5] = r7; + + p4.l = _saved_retx; + p4.h = _saved_retx; + r6 = retx; + [p4] = r6; + r7 = SEQSTAT; /* reason code is in bit 5:0 */ + p4.l = _saved_seqstat; + p4.h = _saved_seqstat; + [p4] = r7; +#else + r7 = SEQSTAT; /* reason code is in bit 5:0 */ +#endif r6.l = lo(SEQSTAT_EXCAUSE); r6.h = hi(SEQSTAT_EXCAUSE); r7 = r7 & r6; @@ -616,6 +704,9 @@ ENTRY(_system_call) rts; ENDPROC(_system_call) +/* Do not mark as ENTRY() to avoid error in assembler ... + * this symbol need not be global anyways, so ... + */ _sys_trace: call _syscall_trace; @@ -941,6 +1032,15 @@ ENTRY(_early_trap) SAVE_ALL_SYS trace_buffer_stop(p0,r0); +#if ANOMALY_05000283 || ANOMALY_05000315 + cc = r5 == r5; + p4.h = HI(CHIPID); + p4.l = LO(CHIPID); + if cc jump 1f; + r5.l = W[p4]; +1: +#endif + /* Turn caches off, to ensure we don't get double exceptions */ P4.L = LO(IMEM_CONTROL); @@ -992,7 +1092,12 @@ ENTRY(_ex_table) */ .long _ex_syscall /* 0x00 - User Defined - Linux Syscall */ .long _ex_soft_bp /* 0x01 - User Defined - Software breakpoint */ +#ifdef CONFIG_KGDB + .long _ex_trap_c /* 0x02 - User Defined - KGDB initial connection + and break signal trap */ +#else .long _ex_replaceable /* 0x02 - User Defined */ +#endif .long _ex_trap_c /* 0x03 - User Defined - userspace stack overflow */ .long _ex_trap_c /* 0x04 - User Defined - dump trace buffer */ .long _ex_replaceable /* 0x05 - User Defined */ @@ -1432,15 +1537,7 @@ ENTRY(_sys_call_table) .rept NR_syscalls-(.-_sys_call_table)/4 .long _sys_ni_syscall .endr - - /* - * Used to save the real RETX, IMASK and SYSCFG when temporarily - * storing safe values across the transition from exception to IRQ5. - */ -_excpt_saved_stuff: - .long 0; - .long 0; - .long 0; +END(_sys_call_table) _exception_stack: .rept 1024 diff --git a/arch/blackfin/mach-common/head.S b/arch/blackfin/mach-common/head.S index 191b4e974c4..3069df58072 100644 --- a/arch/blackfin/mach-common/head.S +++ b/arch/blackfin/mach-common/head.S @@ -68,6 +68,16 @@ ENTRY(__start) M2 = r0; M3 = r0; + /* + * Clear ITEST_COMMAND and DTEST_COMMAND registers, + * Leaving these as non-zero can confuse the emulator + */ + p0.L = LO(DTEST_COMMAND); + p0.H = HI(DTEST_COMMAND); + [p0] = R0; + [p0 + (ITEST_COMMAND - DTEST_COMMAND)] = R0; + CSYNC; + trace_buffer_init(p0,r0); P0 = R1; R0 = R1; @@ -90,12 +100,46 @@ ENTRY(__start) [p0] = R0; SSYNC; - /* Save RETX, in case of doublefault */ - p0.l = ___retx; - p0.h = ___retx; + /* in case of double faults, save a few things */ + p0.l = _init_retx; + p0.h = _init_retx; R0 = RETX; [P0] = R0; +#ifdef CONFIG_DEBUG_DOUBLEFAULT + /* Only save these if we are storing them, + * This happens here, since L1 gets clobbered + * below + */ + p0.l = _saved_retx; + p0.h = _saved_retx; + p1.l = _init_saved_retx; + p1.h = _init_saved_retx; + r0 = [p0]; + [p1] = r0; + + p0.l = _saved_dcplb_fault_addr; + p0.h = _saved_dcplb_fault_addr; + p1.l = _init_saved_dcplb_fault_addr; + p1.h = _init_saved_dcplb_fault_addr; + r0 = [p0]; + [p1] = r0; + + p0.l = _saved_icplb_fault_addr; + p0.h = _saved_icplb_fault_addr; + p1.l = _init_saved_icplb_fault_addr; + p1.h = _init_saved_icplb_fault_addr; + r0 = [p0]; + [p1] = r0; + + p0.l = _saved_seqstat; + p0.h = _saved_seqstat; + p1.l = _init_saved_seqstat; + p1.h = _init_saved_seqstat; + r0 = [p0]; + [p1] = r0; +#endif + /* Initialize stack pointer */ sp.l = lo(INITIAL_STACK); sp.h = hi(INITIAL_STACK); @@ -107,7 +151,7 @@ ENTRY(__start) #endif /* Put The Code for PLL Programming and SDRAM Programming in L1 ISRAM */ - call _bf53x_relocate_l1_mem; + call _bfin_relocate_l1_mem; #ifdef CONFIG_BFIN_KERNEL_CLOCK call _start_dma_code; #endif diff --git a/arch/blackfin/mach-common/interrupt.S b/arch/blackfin/mach-common/interrupt.S index b27e59d3240..4a2ec7a9675 100644 --- a/arch/blackfin/mach-common/interrupt.S +++ b/arch/blackfin/mach-common/interrupt.S @@ -143,7 +143,7 @@ ENTRY(_evt_ivhw) fp = 0; #endif -#if ANOMALY_05000283 +#if ANOMALY_05000283 || ANOMALY_05000315 cc = r7 == r7; p5.h = HI(CHIPID); p5.l = LO(CHIPID); @@ -179,7 +179,16 @@ ENTRY(_evt_ivhw) call _trap_c; SP += 12; +#ifdef EBIU_ERRMST + /* make sure EBIU_ERRMST is clear */ + p0.l = LO(EBIU_ERRMST); + p0.h = HI(EBIU_ERRMST); + r0.l = (CORE_ERROR | CORE_MERROR); + w[p0] = r0.l; +#endif + call _ret_from_exception; + .Lcommon_restore_all_sys: RESTORE_ALL_SYS rti; diff --git a/arch/blackfin/mach-common/ints-priority.c b/arch/blackfin/mach-common/ints-priority.c index 5fa536727c6..34e8a726ffd 100644 --- a/arch/blackfin/mach-common/ints-priority.c +++ b/arch/blackfin/mach-common/ints-priority.c @@ -243,12 +243,14 @@ int bfin_internal_set_wake(unsigned int irq, unsigned int state) #endif static struct irq_chip bfin_core_irqchip = { + .name = "CORE", .ack = bfin_ack_noop, .mask = bfin_core_mask_irq, .unmask = bfin_core_unmask_irq, }; static struct irq_chip bfin_internal_irqchip = { + .name = "INTN", .ack = bfin_ack_noop, .mask = bfin_internal_mask_irq, .unmask = bfin_internal_unmask_irq, @@ -278,6 +280,7 @@ static void bfin_generic_error_unmask_irq(unsigned int irq) } static struct irq_chip bfin_generic_error_irqchip = { + .name = "ERROR", .ack = bfin_ack_noop, .mask_ack = bfin_generic_error_mask_irq, .mask = bfin_generic_error_mask_irq, @@ -361,6 +364,14 @@ static void bfin_demux_error_irq(unsigned int int_err_irq, } #endif /* BF537_GENERIC_ERROR_INT_DEMUX */ +static inline void bfin_set_irq_handler(unsigned irq, irq_flow_handler_t handle) +{ + struct irq_desc *desc = irq_desc + irq; + /* May not call generic set_irq_handler() due to spinlock + recursion. */ + desc->handle_irq = handle; +} + #if !defined(CONFIG_BF54x) static unsigned short gpio_enabled[gpio_bank(MAX_BLACKFIN_GPIOS)]; @@ -473,9 +484,9 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) SSYNC(); if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) - set_irq_handler(irq, handle_edge_irq); + bfin_set_irq_handler(irq, handle_edge_irq); else - set_irq_handler(irq, handle_level_irq); + bfin_set_irq_handler(irq, handle_level_irq); return 0; } @@ -495,6 +506,7 @@ int bfin_gpio_set_wake(unsigned int irq, unsigned int state) #endif static struct irq_chip bfin_gpio_irqchip = { + .name = "GPIO", .ack = bfin_gpio_ack_irq, .mask = bfin_gpio_mask_irq, .mask_ack = bfin_gpio_mask_ack_irq, @@ -804,10 +816,10 @@ static int bfin_gpio_irq_type(unsigned int irq, unsigned int type) if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)) { pint[bank]->edge_set = pintbit; - set_irq_handler(irq, handle_edge_irq); + bfin_set_irq_handler(irq, handle_edge_irq); } else { pint[bank]->edge_clear = pintbit; - set_irq_handler(irq, handle_level_irq); + bfin_set_irq_handler(irq, handle_level_irq); } SSYNC(); @@ -884,6 +896,7 @@ void bfin_pm_restore(void) #endif static struct irq_chip bfin_gpio_irqchip = { + .name = "GPIO", .ack = bfin_gpio_ack_irq, .mask = bfin_gpio_mask_irq, .mask_ack = bfin_gpio_mask_ack_irq, @@ -1136,8 +1149,4 @@ void do_irq(int vec, struct pt_regs *fp) vec = ivg->irqno; } asm_do_IRQ(vec, fp); - -#ifdef CONFIG_KGDB - kgdb_process_breakpoint(); -#endif } diff --git a/arch/blackfin/mm/Makefile b/arch/blackfin/mm/Makefile index 2a7202ce01f..d489f894f4b 100644 --- a/arch/blackfin/mm/Makefile +++ b/arch/blackfin/mm/Makefile @@ -2,4 +2,4 @@ # arch/blackfin/mm/Makefile # -obj-y := blackfin_sram.o init.o +obj-y := sram-alloc.o isram-driver.o init.o diff --git a/arch/blackfin/mm/isram-driver.c b/arch/blackfin/mm/isram-driver.c new file mode 100644 index 00000000000..22913e7a181 --- /dev/null +++ b/arch/blackfin/mm/isram-driver.c @@ -0,0 +1,201 @@ +/* + * Description: Instruction SRAM accessor functions for the Blackfin + * + * Copyright 2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/sched.h> + +#include <asm/blackfin.h> + +/* + * IMPORTANT WARNING ABOUT THESE FUNCTIONS + * + * The emulator will not function correctly if a write command is left in + * ITEST_COMMAND or DTEST_COMMAND AND access to cache memory is needed by + * the emulator. To avoid such problems, ensure that both ITEST_COMMAND + * and DTEST_COMMAND are zero when exiting these functions. + */ + + +/* + * On the Blackfin, L1 instruction sram (which operates at core speeds) can not + * be accessed by a normal core load, so we need to go through a few hoops to + * read/write it. + * To try to make it easier - we export a memcpy interface, where either src or + * dest can be in this special L1 memory area. + * The low level read/write functions should not be exposed to the rest of the + * kernel, since they operate on 64-bit data, and need specific address alignment + */ + +static DEFINE_SPINLOCK(dtest_lock); + +/* Takes a void pointer */ +#define IADDR2DTEST(x) \ + ({ unsigned long __addr = (unsigned long)(x); \ + (__addr & 0x47F8) | /* address bits 14 & 10:3 */ \ + (__addr & 0x0800) << 15 | /* address bit 11 */ \ + (__addr & 0x3000) << 4 | /* address bits 13:12 */ \ + (__addr & 0x8000) << 8 | /* address bit 15 */ \ + (0x1000004); /* isram access */ \ + }) + +/* Takes a pointer, and returns the offset (in bits) which things should be shifted */ +#define ADDR2OFFSET(x) ((((unsigned long)(x)) & 0x7) * 8) + +/* Takes a pointer, determines if it is the last byte in the isram 64-bit data type */ +#define ADDR2LAST(x) ((((unsigned long)x) & 0x7) == 0x7) + +static void isram_write(const void *addr, uint64_t data) +{ + uint32_t cmd; + unsigned long flags; + + if (addr >= (void *)(L1_CODE_START + L1_CODE_LENGTH)) + return; + + cmd = IADDR2DTEST(addr) | 1; /* write */ + + /* + * Writes to DTEST_DATA[0:1] need to be atomic with write to DTEST_COMMAND + * While in exception context - atomicity is guaranteed or double fault + */ + spin_lock_irqsave(&dtest_lock, flags); + + bfin_write_DTEST_DATA0(data & 0xFFFFFFFF); + bfin_write_DTEST_DATA1(data >> 32); + + /* use the builtin, since interrupts are already turned off */ + __builtin_bfin_csync(); + bfin_write_DTEST_COMMAND(cmd); + __builtin_bfin_csync(); + + bfin_write_DTEST_COMMAND(0); + __builtin_bfin_csync(); + + spin_unlock_irqrestore(&dtest_lock, flags); +} + +static uint64_t isram_read(const void *addr) +{ + uint32_t cmd; + unsigned long flags; + uint64_t ret; + + if (addr > (void *)(L1_CODE_START + L1_CODE_LENGTH)) + return 0; + + cmd = IADDR2DTEST(addr) | 0; /* read */ + + /* + * Reads of DTEST_DATA[0:1] need to be atomic with write to DTEST_COMMAND + * While in exception context - atomicity is guaranteed or double fault + */ + spin_lock_irqsave(&dtest_lock, flags); + /* use the builtin, since interrupts are already turned off */ + __builtin_bfin_csync(); + bfin_write_DTEST_COMMAND(cmd); + __builtin_bfin_csync(); + ret = bfin_read_DTEST_DATA0() | ((uint64_t)bfin_read_DTEST_DATA1() << 32); + + bfin_write_DTEST_COMMAND(0); + __builtin_bfin_csync(); + spin_unlock_irqrestore(&dtest_lock, flags); + + return ret; +} + +static bool isram_check_addr(const void *addr, size_t n) +{ + if ((addr >= (void *)L1_CODE_START) && + (addr < (void *)(L1_CODE_START + L1_CODE_LENGTH))) { + if ((addr + n) >= (void *)(L1_CODE_START + L1_CODE_LENGTH)) { + show_stack(NULL, NULL); + printk(KERN_ERR "isram_memcpy: copy involving %p length " + "(%zu) too long\n", addr, n); + } + return true; + } + return false; +} + +/* + * The isram_memcpy() function copies n bytes from memory area src to memory area dest. + * The isram_memcpy() function returns a pointer to dest. + * Either dest or src can be in L1 instruction sram. + */ +void *isram_memcpy(void *dest, const void *src, size_t n) +{ + uint64_t data_in = 0, data_out = 0; + size_t count; + bool dest_in_l1, src_in_l1, need_data, put_data; + unsigned char byte, *src_byte, *dest_byte; + + src_byte = (unsigned char *)src; + dest_byte = (unsigned char *)dest; + + dest_in_l1 = isram_check_addr(dest, n); + src_in_l1 = isram_check_addr(src, n); + + need_data = true; + put_data = true; + for (count = 0; count < n; count++) { + if (src_in_l1) { + if (need_data) { + data_in = isram_read(src + count); + need_data = false; + } + + if (ADDR2LAST(src + count)) + need_data = true; + + byte = (unsigned char)((data_in >> ADDR2OFFSET(src + count)) & 0xff); + + } else { + /* src is in L2 or L3 - so just dereference*/ + byte = src_byte[count]; + } + + if (dest_in_l1) { + if (put_data) { + data_out = isram_read(dest + count); + put_data = false; + } + + data_out &= ~((uint64_t)0xff << ADDR2OFFSET(dest + count)); + data_out |= ((uint64_t)byte << ADDR2OFFSET(dest + count)); + + if (ADDR2LAST(dest + count)) { + put_data = true; + isram_write(dest + count, data_out); + } + } else { + /* dest in L2 or L3 - so just dereference */ + dest_byte[count] = byte; + } + } + + /* make sure we dump the last byte if necessary */ + if (dest_in_l1 && !put_data) + isram_write(dest + count, data_out); + + return dest; +} +EXPORT_SYMBOL(isram_memcpy); + diff --git a/arch/blackfin/mm/blackfin_sram.c b/arch/blackfin/mm/sram-alloc.c index 4f5e887a0d9..0f1ca6930c1 100644 --- a/arch/blackfin/mm/blackfin_sram.c +++ b/arch/blackfin/mm/sram-alloc.c @@ -1,13 +1,13 @@ /* - * File: arch/blackfin/mm/blackfin_sram.c + * File: arch/blackfin/mm/sram-alloc.c * Based on: * Author: * * Created: - * Description: SRAM driver for Blackfin ADSP-BF5xx + * Description: SRAM allocator for Blackfin L1 and L2 memory * * Modified: - * Copyright 2004-2007 Analog Devices Inc. + * Copyright 2004-2008 Analog Devices Inc. * * Bugs: Enter bugs at http://blackfin.uclinux.org/ * @@ -78,7 +78,7 @@ static void __init l1sram_init(void) free_l1_ssram_head.next = kmem_cache_alloc(sram_piece_cache, GFP_KERNEL); if (!free_l1_ssram_head.next) { - printk(KERN_INFO"Fail to initialize Scratchpad data SRAM.\n"); + printk(KERN_INFO "Failed to initialize Scratchpad data SRAM\n"); return; } @@ -102,7 +102,7 @@ static void __init l1_data_sram_init(void) free_l1_data_A_sram_head.next = kmem_cache_alloc(sram_piece_cache, GFP_KERNEL); if (!free_l1_data_A_sram_head.next) { - printk(KERN_INFO"Fail to initialize L1 Data A SRAM.\n"); + printk(KERN_INFO "Failed to initialize L1 Data A SRAM\n"); return; } @@ -123,7 +123,7 @@ static void __init l1_data_sram_init(void) free_l1_data_B_sram_head.next = kmem_cache_alloc(sram_piece_cache, GFP_KERNEL); if (!free_l1_data_B_sram_head.next) { - printk(KERN_INFO"Fail to initialize L1 Data B SRAM.\n"); + printk(KERN_INFO "Failed to initialize L1 Data B SRAM\n"); return; } @@ -151,7 +151,7 @@ static void __init l1_inst_sram_init(void) free_l1_inst_sram_head.next = kmem_cache_alloc(sram_piece_cache, GFP_KERNEL); if (!free_l1_inst_sram_head.next) { - printk(KERN_INFO"Fail to initialize L1 Instruction SRAM.\n"); + printk(KERN_INFO "Failed to initialize L1 Instruction SRAM\n"); return; } @@ -179,7 +179,7 @@ static void __init l2_sram_init(void) free_l2_sram_head.next = kmem_cache_alloc(sram_piece_cache, GFP_KERNEL); if (!free_l2_sram_head.next) { - printk(KERN_INFO"Fail to initialize L2 SRAM.\n"); + printk(KERN_INFO "Failed to initialize L2 SRAM\n"); return; } @@ -351,28 +351,31 @@ static int _sram_free(const void *addr, int sram_free(const void *addr) { - if (0) {} + #if L1_CODE_LENGTH != 0 - else if (addr >= (void *)L1_CODE_START + if (addr >= (void *)L1_CODE_START && addr < (void *)(L1_CODE_START + L1_CODE_LENGTH)) return l1_inst_sram_free(addr); + else #endif #if L1_DATA_A_LENGTH != 0 - else if (addr >= (void *)L1_DATA_A_START + if (addr >= (void *)L1_DATA_A_START && addr < (void *)(L1_DATA_A_START + L1_DATA_A_LENGTH)) return l1_data_A_sram_free(addr); + else #endif #if L1_DATA_B_LENGTH != 0 - else if (addr >= (void *)L1_DATA_B_START + if (addr >= (void *)L1_DATA_B_START && addr < (void *)(L1_DATA_B_START + L1_DATA_B_LENGTH)) return l1_data_B_sram_free(addr); + else #endif #if L2_LENGTH != 0 - else if (addr >= (void *)L2_START + if (addr >= (void *)L2_START && addr < (void *)(L2_START + L2_LENGTH)) return l2_sram_free(addr); -#endif else +#endif return -1; } EXPORT_SYMBOL(sram_free); diff --git a/arch/cris/arch-v10/boot/tools/build.c b/arch/cris/arch-v10/boot/tools/build.c index 2f9bbb26d60..c8adef36416 100644 --- a/arch/cris/arch-v10/boot/tools/build.c +++ b/arch/cris/arch-v10/boot/tools/build.c @@ -30,7 +30,6 @@ #include <sys/sysmacros.h> #include <unistd.h> /* contains read/write */ #include <fcntl.h> -#include <linux/a.out.h> #include <errno.h> #define MINIX_HEADER 32 diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 396ab059efa..107cb5bb9f3 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -66,9 +66,6 @@ config TIME_LOW_RES bool default y -config ARCH_SUPPORTS_AOUT - def_bool y - config NO_IOPORT def_bool y diff --git a/arch/h8300/include/asm/a.out.h b/arch/h8300/include/asm/a.out.h deleted file mode 100644 index ded780f0a49..00000000000 --- a/arch/h8300/include/asm/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __H8300_A_OUT_H__ -#define __H8300_A_OUT_H__ - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __H8300_A_OUT_H__ */ diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c index dfbe7ab9ffe..a8ef654a5a0 100644 --- a/arch/h8300/kernel/process.c +++ b/arch/h8300/kernel/process.c @@ -34,7 +34,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/reboot.h> #include <linux/fs.h> diff --git a/arch/ia64/include/asm/a.out.h b/arch/ia64/include/asm/a.out.h deleted file mode 100644 index 193dcfb6759..00000000000 --- a/arch/ia64/include/asm/a.out.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _ASM_IA64_A_OUT_H -#define _ASM_IA64_A_OUT_H - -/* - * No a.out format has been (or should be) defined so this file is - * just a dummy that allows us to get binfmt_elf compiled. It - * probably would be better to clean up binfmt_elf.c so it does not - * necessarily depend on there being a.out support. - * - * Modified 1998-2002 - * David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co. - */ - -#include <linux/types.h> - -struct exec { - unsigned long a_info; - unsigned long a_text; - unsigned long a_data; - unsigned long a_bss; - unsigned long a_entry; -}; - -#define N_TXTADDR(x) 0 -#define N_DATADDR(x) 0 -#define N_BSSADDR(x) 0 -#define N_DRSIZE(x) 0 -#define N_TRSIZE(x) 0 -#define N_SYMSIZE(x) 0 -#define N_TXTOFF(x) 0 - -#endif /* _ASM_IA64_A_OUT_H */ diff --git a/arch/ia64/include/asm/siginfo.h b/arch/ia64/include/asm/siginfo.h index 9294e4b0c8b..118d4297900 100644 --- a/arch/ia64/include/asm/siginfo.h +++ b/arch/ia64/include/asm/siginfo.h @@ -113,11 +113,6 @@ typedef struct siginfo { #undef NSIGSEGV #define NSIGSEGV 3 -/* - * SIGTRAP si_codes - */ -#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */ -#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */ #undef NSIGTRAP #define NSIGTRAP 4 diff --git a/arch/ia64/include/asm/statfs.h b/arch/ia64/include/asm/statfs.h index 811097974f3..1e589669de5 100644 --- a/arch/ia64/include/asm/statfs.h +++ b/arch/ia64/include/asm/statfs.h @@ -8,55 +8,13 @@ * David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co */ -#ifndef __KERNEL_STRICT_NAMES -# include <linux/types.h> -typedef __kernel_fsid_t fsid_t; -#endif - /* - * This is ugly --- we're already 64-bit, so just duplicate the definitions + * We need compat_statfs64 to be packed, because the i386 ABI won't + * add padding at the end to bring it to a multiple of 8 bytes, but + * the IA64 ABI will. */ -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; +#define ARCH_PACK_COMPAT_STATFS64 __attribute__((packed,aligned(4))) -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -} __attribute__((packed)); +#include <asm-generic/statfs.h> #endif /* _ASM_IA64_STATFS_H */ diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 200100ea761..f482a9098e3 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -21,7 +21,6 @@ #include <linux/bitops.h> #include <linux/kexec.h> -#include <asm/a.out.h> #include <asm/dma.h> #include <asm/ia32.h> #include <asm/io.h> diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index f57113f1f89..00289c178f8 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -36,9 +36,6 @@ config NO_IOPORT config NO_DMA def_bool y -config ARCH_SUPPORTS_AOUT - def_bool y - config HZ int default 100 diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig index 8c5e1de68fc..41e5bf02e23 100644 --- a/arch/m68k/Kconfig +++ b/arch/m68k/Kconfig @@ -5,6 +5,7 @@ config M68K bool default y + select HAVE_AOUT select HAVE_IDE config MMU @@ -53,9 +54,6 @@ config NO_IOPORT config NO_DMA def_bool SUN3 -config ARCH_SUPPORTS_AOUT - def_bool y - config HZ int default 100 diff --git a/arch/m68knommu/Kconfig b/arch/m68knommu/Kconfig index 2e7515e8db9..0a8998315e5 100644 --- a/arch/m68knommu/Kconfig +++ b/arch/m68knommu/Kconfig @@ -73,9 +73,6 @@ config GENERIC_CLOCKEVENTS config NO_IOPORT def_bool y -config ARCH_SUPPORTS_AOUT - def_bool y - source "init/Kconfig" menu "Processor type and features" diff --git a/arch/m68knommu/include/asm/a.out.h b/arch/m68knommu/include/asm/a.out.h deleted file mode 100644 index ce18ef99de0..00000000000 --- a/arch/m68knommu/include/asm/a.out.h +++ /dev/null @@ -1 +0,0 @@ -#include <asm-m68k/a.out.h> diff --git a/arch/m68knommu/kernel/process.c b/arch/m68knommu/kernel/process.c index 47502d5ec19..3f2d7745f31 100644 --- a/arch/m68knommu/kernel/process.c +++ b/arch/m68knommu/kernel/process.c @@ -25,7 +25,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/reboot.h> #include <linux/fs.h> diff --git a/arch/m68knommu/kernel/traps.c b/arch/m68knommu/kernel/traps.c index 46f8f9d0c40..5d5d56bcd0e 100644 --- a/arch/m68knommu/kernel/traps.c +++ b/arch/m68knommu/kernel/traps.c @@ -22,7 +22,6 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/types.h> -#include <linux/a.out.h> #include <linux/user.h> #include <linux/string.h> #include <linux/linkage.h> diff --git a/arch/mips/include/asm/a.out.h b/arch/mips/include/asm/a.out.h deleted file mode 100644 index cad8371422a..00000000000 --- a/arch/mips/include/asm/a.out.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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. - * - * Copyright (C) 1994 - 1999, 2003 by Ralf Baechle - */ -#ifndef _ASM_A_OUT_H -#define _ASM_A_OUT_H - -#ifdef __KERNEL__ - - -#endif - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for - file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, - in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in - bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* _ASM_A_OUT_H */ diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 22fc19bbe87..ca2e4026ad2 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -22,7 +22,6 @@ #include <linux/personality.h> #include <linux/sys.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/init.h> #include <linux/completion.h> #include <linux/kallsyms.h> diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 343015a2f41..37970d9b218 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -7,7 +7,6 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 2001 MIPS Technologies, Inc. */ -#include <linux/a.out.h> #include <linux/capability.h> #include <linux/errno.h> #include <linux/linkage.h> diff --git a/arch/mn10300/Kconfig b/arch/mn10300/Kconfig index e856218da90..dd557c9cf00 100644 --- a/arch/mn10300/Kconfig +++ b/arch/mn10300/Kconfig @@ -53,9 +53,6 @@ config QUICKLIST config ARCH_HAS_ILOG2_U32 def_bool y -config ARCH_SUPPORTS_AOUT - def_bool n - # Use the generic interrupt handling code in kernel/irq/ config GENERIC_HARDIRQS def_bool y diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index a7d4fd353c2..8313fccced5 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -76,9 +76,6 @@ config IRQ_PER_CPU bool default y -config ARCH_SUPPORTS_AOUT - def_bool y - # unless you want to implement ACPI on PA-RISC ... ;-) config PM bool diff --git a/arch/powerpc/include/asm/a.out.h b/arch/powerpc/include/asm/a.out.h deleted file mode 100644 index 89cead6b176..00000000000 --- a/arch/powerpc/include/asm/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_POWERPC_A_OUT_H -#define _ASM_POWERPC_A_OUT_H - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* _ASM_POWERPC_A_OUT_H */ diff --git a/arch/powerpc/include/asm/siginfo.h b/arch/powerpc/include/asm/siginfo.h index 12f1bce037b..49495b0534e 100644 --- a/arch/powerpc/include/asm/siginfo.h +++ b/arch/powerpc/include/asm/siginfo.h @@ -15,11 +15,6 @@ #include <asm-generic/siginfo.h> -/* - * SIGTRAP si_codes - */ -#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */ -#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */ #undef NSIGTRAP #define NSIGTRAP 4 diff --git a/arch/powerpc/include/asm/statfs.h b/arch/powerpc/include/asm/statfs.h index 67024026c10..5244834583a 100644 --- a/arch/powerpc/include/asm/statfs.h +++ b/arch/powerpc/include/asm/statfs.h @@ -1,60 +1,6 @@ #ifndef _ASM_POWERPC_STATFS_H #define _ASM_POWERPC_STATFS_H -/* For ppc32 we just use the generic definitions, not so simple on ppc64 */ - -#ifndef __powerpc64__ #include <asm-generic/statfs.h> -#else - -#ifndef __KERNEL_STRICT_NAMES -#include <linux/types.h> -typedef __kernel_fsid_t fsid_t; -#endif - -/* - * We're already 64-bit, so duplicate the definition - */ -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; -#endif /* ! __powerpc64__ */ #endif diff --git a/arch/powerpc/kernel/softemu8xx.c b/arch/powerpc/kernel/softemu8xx.c index c906c4bf683..23c8c5e7dc4 100644 --- a/arch/powerpc/kernel/softemu8xx.c +++ b/arch/powerpc/kernel/softemu8xx.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <asm/pgtable.h> diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 81ccb8dd1a5..f5def6cf5cd 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/module.h> diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 690ca7b0dcf..2c8b8091250 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -659,7 +659,7 @@ enum { Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err, }; -static match_table_t spufs_tokens = { +static const match_table_t spufs_tokens = { { Opt_uid, "uid=%d" }, { Opt_gid, "gid=%d" }, { Opt_mode, "mode=%o" }, diff --git a/arch/powerpc/platforms/chrp/setup.c b/arch/powerpc/platforms/chrp/setup.c index 1ba7ce5aafa..272d79a8d28 100644 --- a/arch/powerpc/platforms/chrp/setup.c +++ b/arch/powerpc/platforms/chrp/setup.c @@ -17,7 +17,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/major.h> #include <linux/interrupt.h> diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 364714757cf..d4c61c3c966 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c @@ -23,7 +23,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/string.h> #include <linux/delay.h> diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 88ccf3a08a9..82c14d203d8 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c @@ -33,7 +33,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/string.h> #include <linux/delay.h> diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index 7b01d67b4e4..ec341707e41 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -25,7 +25,6 @@ #include <linux/unistd.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/a.out.h> #include <linux/tty.h> #include <linux/major.h> #include <linux/interrupt.h> diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index 7383781f3e6..36313801cd5 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -219,7 +219,7 @@ static int hypfs_release(struct inode *inode, struct file *filp) enum { opt_uid, opt_gid, opt_err }; -static match_table_t hypfs_tokens = { +static const match_table_t hypfs_tokens = { {opt_uid, "uid=%u"}, {opt_gid, "gid=%u"}, {opt_err, NULL} diff --git a/arch/s390/include/asm/statfs.h b/arch/s390/include/asm/statfs.h index 099a4557919..06cc70307ec 100644 --- a/arch/s390/include/asm/statfs.h +++ b/arch/s390/include/asm/statfs.h @@ -12,19 +12,16 @@ #ifndef __s390x__ #include <asm-generic/statfs.h> #else +/* + * We can't use <asm-generic/statfs.h> because in 64-bit mode + * we mix ints of different sizes in our struct statfs. + */ #ifndef __KERNEL_STRICT_NAMES - #include <linux/types.h> - typedef __kernel_fsid_t fsid_t; - #endif -/* - * This is ugly -- we're already 64-bit clean, so just duplicate the - * definitions. - */ struct statfs { int f_type; int f_bsize; diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index a214002114e..97671dac12a 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -20,6 +20,11 @@ config GENERIC_ISA_DMA bool default y +config GENERIC_GPIO + bool + help + Generic GPIO API support + config ARCH_NO_VIRT_TO_BUS def_bool y @@ -69,6 +74,9 @@ config SPARC select HAVE_OPROFILE select HAVE_ARCH_KGDB if !SMP select HAVE_ARCH_TRACEHOOK + select ARCH_WANT_OPTIONAL_GPIOLIB + select RTC_CLASS + select RTC_DRV_M48T59 # Identify this as a Sparc32 build config SPARC32 @@ -204,17 +212,6 @@ config SUN_PM Enable power management and CPU standby features on supported SPARC platforms. -config SUN4 - bool "Support for SUN4 machines (disables SUN4[CDM] support)" - depends on !SMP - default n - help - Say Y here if, and only if, your machine is a sun4. Note that - a kernel compiled with this option will run only on sun4. - (And the current version will probably work only on sun4/330.) - -if !SUN4 - config PCI bool "Support for PCI and PS/2 keyboard/mouse" help @@ -227,11 +224,6 @@ config PCI_SYSCALL source "drivers/pci/Kconfig" -endif - -config NO_DMA - def_bool !PCI - config SUN_OPENPROMFS tristate "Openprom tree appears in /proc/openprom" help @@ -263,9 +255,7 @@ source "net/Kconfig" source "drivers/Kconfig" -if !SUN4 source "drivers/sbus/char/Kconfig" -endif # This one must be before the filesystem configs. -DaveM diff --git a/arch/sparc/include/asm/Kbuild b/arch/sparc/include/asm/Kbuild index a5f0ce734ff..2d2769d766e 100644 --- a/arch/sparc/include/asm/Kbuild +++ b/arch/sparc/include/asm/Kbuild @@ -15,14 +15,11 @@ header-y += signal_32.h header-y += signal_64.h header-y += stat_32.h header-y += stat_64.h -header-y += statfs_32.h -header-y += statfs_64.h header-y += unistd_32.h header-y += unistd_64.h header-y += apc.h header-y += asi.h -header-y += bpp.h header-y += display7seg.h header-y += envctrl.h header-y += fbio.h @@ -41,5 +38,4 @@ header-y += reg_64.h header-y += traps.h header-y += uctx.h header-y += utrap.h -header-y += vfc_ioctls.h header-y += watchdog.h diff --git a/arch/sparc/include/asm/asmmacro.h b/arch/sparc/include/asm/asmmacro.h index a619a4d97aa..a995bf8aba3 100644 --- a/arch/sparc/include/asm/asmmacro.h +++ b/arch/sparc/include/asm/asmmacro.h @@ -34,12 +34,7 @@ /* sun4 probably wants half word accesses to ASI_SEGMAP, while sun4c+ likes byte accesses. These are to avoid ifdef mania. */ -#ifdef CONFIG_SUN4 -#define lduXa lduha -#define stXa stha -#else #define lduXa lduba #define stXa stba -#endif #endif /* !(_SPARC_ASMMACRO_H) */ diff --git a/arch/sparc/include/asm/bpp.h b/arch/sparc/include/asm/bpp.h deleted file mode 100644 index 31f515e499a..00000000000 --- a/arch/sparc/include/asm/bpp.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef _SPARC_BPP_H -#define _SPARC_BPP_H - -/* - * Copyright (c) 1995 Picture Elements - * Stephen Williams - * Gus Baldauf - * - * Linux/SPARC port by Peter Zaitcev. - * Integration into SPARC tree by Tom Dyas. - */ - -#include <linux/ioctl.h> - -/* - * This is a driver that supports IEEE Std 1284-1994 communications - * with compliant or compatible devices. It will use whatever features - * the device supports, prefering those that are typically faster. - * - * When the device is opened, it is left in COMPATIBILITY mode, and - * writes work like any printer device. The driver only attempt to - * negotiate 1284 modes when needed so that plugs can be pulled, - * switch boxes switched, etc., without disrupting things. It will - * also leave the device in compatibility mode when closed. - */ - - - -/* - * This driver also supplies ioctls to manually manipulate the - * pins. This is great for testing devices, or writing code to deal - * with bizzarro-mode of the ACME Special TurboThingy Plus. - * - * NOTE: These ioctl currently do not interact well with - * read/write. Caveat emptor. - * - * PUT_PINS allows us to assign the sense of all the pins, including - * the data pins if being driven by the host. The GET_PINS returns the - * pins that the peripheral drives, including data if appropriate. - */ - -# define BPP_PUT_PINS _IOW('B', 1, int) -# define BPP_GET_PINS _IOR('B', 2, char) /* that's bogus - should've been _IO */ -# define BPP_PUT_DATA _IOW('B', 3, int) -# define BPP_GET_DATA _IOR('B', 4, char) /* ditto */ - -/* - * Set the data bus to input mode. Disengage the data bin driver and - * be prepared to read values from the peripheral. If the arg is 0, - * then revert the bus to output mode. - */ -# define BPP_SET_INPUT _IOW('B', 5, int) - -/* - * These bits apply to the PUT operation... - */ -# define BPP_PP_nStrobe 0x0001 -# define BPP_PP_nAutoFd 0x0002 -# define BPP_PP_nInit 0x0004 -# define BPP_PP_nSelectIn 0x0008 - -/* - * These apply to the GET operation, which also reads the current value - * of the previously put values. A bit mask of these will be returned - * as a bit mask in the return code of the ioctl(). - */ -# define BPP_GP_nAck 0x0100 -# define BPP_GP_Busy 0x0200 -# define BPP_GP_PError 0x0400 -# define BPP_GP_Select 0x0800 -# define BPP_GP_nFault 0x1000 - -#endif diff --git a/arch/sparc/include/asm/bugs.h b/arch/sparc/include/asm/bugs.h index e179bc12f64..61d86bbbe2b 100644 --- a/arch/sparc/include/asm/bugs.h +++ b/arch/sparc/include/asm/bugs.h @@ -7,10 +7,6 @@ #include <asm/cpudata.h> #endif -#ifdef CONFIG_SPARC64 -#include <asm/sstate.h> -#endif - extern unsigned long loops_per_jiffy; static void __init check_bugs(void) @@ -18,7 +14,4 @@ static void __init check_bugs(void) #if defined(CONFIG_SPARC32) && !defined(CONFIG_SMP) cpu_data(0).udelay_val = loops_per_jiffy; #endif -#ifdef CONFIG_SPARC64 - sstate_running(); -#endif } diff --git a/arch/sparc/include/asm/cpudata_64.h b/arch/sparc/include/asm/cpudata_64.h index 532975ecfe1..7da7c13d23c 100644 --- a/arch/sparc/include/asm/cpudata_64.h +++ b/arch/sparc/include/asm/cpudata_64.h @@ -86,7 +86,6 @@ extern struct trap_per_cpu trap_block[NR_CPUS]; extern void init_cur_cpu_trap(struct thread_info *); extern void setup_tba(void); extern int ncpus_probed; -extern void __init cpu_probe(void); extern const struct seq_operations cpuinfo_op; extern unsigned long real_hard_smp_processor_id(void); diff --git a/arch/sparc/include/asm/dma-mapping_32.h b/arch/sparc/include/asm/dma-mapping_32.h index f3a641e6b2c..8a57ea0573e 100644 --- a/arch/sparc/include/asm/dma-mapping_32.h +++ b/arch/sparc/include/asm/dma-mapping_32.h @@ -1,11 +1,60 @@ #ifndef _ASM_SPARC_DMA_MAPPING_H #define _ASM_SPARC_DMA_MAPPING_H +#include <linux/types.h> -#ifdef CONFIG_PCI -#include <asm-generic/dma-mapping.h> -#else -#include <asm-generic/dma-mapping-broken.h> -#endif /* PCI */ +struct device; +struct scatterlist; +struct page; + +#define DMA_ERROR_CODE (~(dma_addr_t)0x0) + +extern int dma_supported(struct device *dev, u64 mask); +extern int dma_set_mask(struct device *dev, u64 dma_mask); +extern void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); +extern void dma_free_coherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle); +extern dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, + size_t size, + enum dma_data_direction direction); +extern void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction); +extern dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction); +extern void dma_unmap_page(struct device *dev, dma_addr_t dma_address, + size_t size, enum dma_data_direction direction); +extern int dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction); +extern void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction); +extern void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_for_device(struct device *dev, + dma_addr_t dma_handle, + size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, + size_t size, + enum dma_data_direction direction); +extern void dma_sync_single_range_for_device(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction); +extern void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction); +extern void dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction); +extern int dma_mapping_error(struct device *dev, dma_addr_t dma_addr); +extern int dma_get_cache_alignment(void); + +#define dma_alloc_noncoherent dma_alloc_coherent +#define dma_free_noncoherent dma_free_coherent #endif /* _ASM_SPARC_DMA_MAPPING_H */ diff --git a/arch/sparc/include/asm/dma.h b/arch/sparc/include/asm/dma.h index aa1d90ac04c..b554927bbaf 100644 --- a/arch/sparc/include/asm/dma.h +++ b/arch/sparc/include/asm/dma.h @@ -1,8 +1,139 @@ -#ifndef ___ASM_SPARC_DMA_H -#define ___ASM_SPARC_DMA_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/dma_64.h> +#ifndef _ASM_SPARC_DMA_H +#define _ASM_SPARC_DMA_H + +/* These are irrelevant for Sparc DMA, but we leave it in so that + * things can compile. + */ +#define MAX_DMA_CHANNELS 8 +#define DMA_MODE_READ 1 +#define DMA_MODE_WRITE 2 +#define MAX_DMA_ADDRESS (~0UL) + +/* Useful constants */ +#define SIZE_16MB (16*1024*1024) +#define SIZE_64K (64*1024) + +/* SBUS DMA controller reg offsets */ +#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ +#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ +#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ +#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ + +/* Fields in the cond_reg register */ +/* First, the version identification bits */ +#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */ +#define DMA_VERS0 0x00000000 /* Sunray DMA version */ +#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */ +#define DMA_VERS1 0x80000000 /* DMA rev 1 */ +#define DMA_VERS2 0xa0000000 /* DMA rev 2 */ +#define DMA_VERHME 0xb0000000 /* DMA hme gate array */ +#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */ + +#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */ +#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */ +#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */ +#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */ +#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */ +#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */ +#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */ +#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */ +#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */ +#define DMA_ST_WRITE 0x00000100 /* write from device to memory */ +#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */ +#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */ +#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */ +#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */ +#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */ +#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */ +#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */ +#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */ +#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */ +#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */ +#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */ +#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */ +#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */ +#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */ +#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */ +#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */ +#define DMA_BRST64 0x000c0000 /* SCSI: 64byte bursts (HME on UltraSparc only) */ +#define DMA_BRST32 0x00040000 /* SCSI: 32byte bursts */ +#define DMA_BRST16 0x00000000 /* SCSI: 16byte bursts */ +#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */ +#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */ +#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */ +#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */ +#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */ +#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */ +#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */ +#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */ +#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */ +#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */ +#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */ +#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */ + +/* Values describing the burst-size property from the PROM */ +#define DMA_BURST1 0x01 +#define DMA_BURST2 0x02 +#define DMA_BURST4 0x04 +#define DMA_BURST8 0x08 +#define DMA_BURST16 0x10 +#define DMA_BURST32 0x20 +#define DMA_BURST64 0x40 +#define DMA_BURSTBITS 0x7f + +/* From PCI */ + +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; #else -#include <asm/dma_32.h> +#define isa_dma_bridge_buggy (0) #endif + +#ifdef CONFIG_SPARC32 + +/* Routines for data transfer buffers. */ +BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long) +BTFIXUPDEF_CALL(void, mmu_unlockarea, char *, unsigned long) + +#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len) +#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len) + +struct page; +struct device; +struct scatterlist; + +/* These are implementations for sbus_map_sg/sbus_unmap_sg... collapse later */ +BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, struct device *, char *, unsigned long) +BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct device *, struct scatterlist *, int) +BTFIXUPDEF_CALL(void, mmu_release_scsi_one, struct device *, __u32, unsigned long) +BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct device *, struct scatterlist *, int) + +#define mmu_get_scsi_one(dev,vaddr,len) BTFIXUP_CALL(mmu_get_scsi_one)(dev,vaddr,len) +#define mmu_get_scsi_sgl(dev,sg,sz) BTFIXUP_CALL(mmu_get_scsi_sgl)(dev,sg,sz) +#define mmu_release_scsi_one(dev,vaddr,len) BTFIXUP_CALL(mmu_release_scsi_one)(dev,vaddr,len) +#define mmu_release_scsi_sgl(dev,sg,sz) BTFIXUP_CALL(mmu_release_scsi_sgl)(dev,sg,sz) + +/* + * mmu_map/unmap are provided by iommu/iounit; Invalid to call on IIep. + * + * The mmu_map_dma_area establishes two mappings in one go. + * These mappings point to pages normally mapped at 'va' (linear address). + * First mapping is for CPU visible address at 'a', uncached. + * This is an alias, but it works because it is an uncached mapping. + * Second mapping is for device visible address, or "bus" address. + * The bus address is returned at '*pba'. + * + * These functions seem distinct, but are hard to split. On sun4c, + * at least for now, 'a' is equal to bus address, and retured in *pba. + * On sun4m, page attributes depend on the CPU type, so we have to + * know if we are mapping RAM or I/O, so it has to be an additional argument + * to a separate mapping function for CPU visible mappings. + */ +BTFIXUPDEF_CALL(int, mmu_map_dma_area, struct device *, dma_addr_t *, unsigned long, unsigned long, int len) +BTFIXUPDEF_CALL(void, mmu_unmap_dma_area, struct device *, unsigned long busa, int len) + +#define mmu_map_dma_area(dev,pba,va,a,len) BTFIXUP_CALL(mmu_map_dma_area)(dev,pba,va,a,len) +#define mmu_unmap_dma_area(dev,ba,len) BTFIXUP_CALL(mmu_unmap_dma_area)(dev,ba,len) #endif + +#endif /* !(_ASM_SPARC_DMA_H) */ diff --git a/arch/sparc/include/asm/dma_32.h b/arch/sparc/include/asm/dma_32.h deleted file mode 100644 index cf7189c0079..00000000000 --- a/arch/sparc/include/asm/dma_32.h +++ /dev/null @@ -1,288 +0,0 @@ -/* include/asm/dma.h - * - * Copyright 1995 (C) David S. Miller (davem@davemloft.net) - */ - -#ifndef _ASM_SPARC_DMA_H -#define _ASM_SPARC_DMA_H - -#include <linux/kernel.h> -#include <linux/types.h> - -#include <asm/vac-ops.h> /* for invalidate's, etc. */ -#include <asm/sbus.h> -#include <asm/delay.h> -#include <asm/oplib.h> -#include <asm/system.h> -#include <asm/io.h> -#include <linux/spinlock.h> - -struct page; -extern spinlock_t dma_spin_lock; - -static inline unsigned long claim_dma_lock(void) -{ - unsigned long flags; - spin_lock_irqsave(&dma_spin_lock, flags); - return flags; -} - -static inline void release_dma_lock(unsigned long flags) -{ - spin_unlock_irqrestore(&dma_spin_lock, flags); -} - -/* These are irrelevant for Sparc DMA, but we leave it in so that - * things can compile. - */ -#define MAX_DMA_CHANNELS 8 -#define MAX_DMA_ADDRESS (~0UL) -#define DMA_MODE_READ 1 -#define DMA_MODE_WRITE 2 - -/* Useful constants */ -#define SIZE_16MB (16*1024*1024) -#define SIZE_64K (64*1024) - -/* SBUS DMA controller reg offsets */ -#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ -#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ -#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ -#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ - -/* DVMA chip revisions */ -enum dvma_rev { - dvmarev0, - dvmaesc1, - dvmarev1, - dvmarev2, - dvmarev3, - dvmarevplus, - dvmahme -}; - -#define DMA_HASCOUNT(rev) ((rev)==dvmaesc1) - -/* Linux DMA information structure, filled during probe. */ -struct sbus_dma { - struct sbus_dma *next; - struct sbus_dev *sdev; - void __iomem *regs; - - /* Status, misc info */ - int node; /* Prom node for this DMA device */ - int running; /* Are we doing DMA now? */ - int allocated; /* Are we "owned" by anyone yet? */ - - /* Transfer information. */ - unsigned long addr; /* Start address of current transfer */ - int nbytes; /* Size of current transfer */ - int realbytes; /* For splitting up large transfers, etc. */ - - /* DMA revision */ - enum dvma_rev revision; -}; - -extern struct sbus_dma *dma_chain; - -/* Broken hardware... */ -#ifdef CONFIG_SUN4 -/* Have to sort this out. Does rev0 work fine on sun4[cmd] without isbroken? - * Or is rev0 present only on sun4 boxes? -jj */ -#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev0 || (dma)->revision == dvmarev1) -#else -#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev1) -#endif -#define DMA_ISESC1(dma) ((dma)->revision == dvmaesc1) - -/* Main routines in dma.c */ -extern void dvma_init(struct sbus_bus *); - -/* Fields in the cond_reg register */ -/* First, the version identification bits */ -#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */ -#define DMA_VERS0 0x00000000 /* Sunray DMA version */ -#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */ -#define DMA_VERS1 0x80000000 /* DMA rev 1 */ -#define DMA_VERS2 0xa0000000 /* DMA rev 2 */ -#define DMA_VERHME 0xb0000000 /* DMA hme gate array */ -#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */ - -#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */ -#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */ -#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */ -#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */ -#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */ -#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */ -#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */ -#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */ -#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */ -#define DMA_RST_BPP DMA_RST_SCSI /* Reset the BPP controller */ -#define DMA_ST_WRITE 0x00000100 /* write from device to memory */ -#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */ -#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */ -#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */ -#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */ -#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */ -#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */ -#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */ -#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */ -#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */ -#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */ -#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */ -#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */ -#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */ -#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */ -#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */ -#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */ -#define DMA_BRST64 0x00080000 /* SCSI: 64byte bursts (HME on UltraSparc only) */ -#define DMA_BRST32 0x00040000 /* SCSI/BPP: 32byte bursts */ -#define DMA_BRST16 0x00000000 /* SCSI/BPP: 16byte bursts */ -#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */ -#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */ -#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */ -#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */ -#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */ -#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */ -#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */ -#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */ -#define DMA_BPP_ON DMA_SCSI_ON /* Enable BPP dma */ -#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */ -#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */ -#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */ -#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */ - -/* Values describing the burst-size property from the PROM */ -#define DMA_BURST1 0x01 -#define DMA_BURST2 0x02 -#define DMA_BURST4 0x04 -#define DMA_BURST8 0x08 -#define DMA_BURST16 0x10 -#define DMA_BURST32 0x20 -#define DMA_BURST64 0x40 -#define DMA_BURSTBITS 0x7f - -/* Determine highest possible final transfer address given a base */ -#define DMA_MAXEND(addr) (0x01000000UL-(((unsigned long)(addr))&0x00ffffffUL)) - -/* Yes, I hack a lot of elisp in my spare time... */ -#define DMA_ERROR_P(regs) ((((regs)->cond_reg) & DMA_HNDL_ERROR)) -#define DMA_IRQ_P(regs) ((((regs)->cond_reg) & (DMA_HNDL_INTR | DMA_HNDL_ERROR))) -#define DMA_WRITE_P(regs) ((((regs)->cond_reg) & DMA_ST_WRITE)) -#define DMA_OFF(regs) ((((regs)->cond_reg) &= (~DMA_ENABLE))) -#define DMA_INTSOFF(regs) ((((regs)->cond_reg) &= (~DMA_INT_ENAB))) -#define DMA_INTSON(regs) ((((regs)->cond_reg) |= (DMA_INT_ENAB))) -#define DMA_PUNTFIFO(regs) ((((regs)->cond_reg) |= DMA_FIFO_INV)) -#define DMA_SETSTART(regs, addr) ((((regs)->st_addr) = (char *) addr)) -#define DMA_BEGINDMA_W(regs) \ - ((((regs)->cond_reg |= (DMA_ST_WRITE|DMA_ENABLE|DMA_INT_ENAB)))) -#define DMA_BEGINDMA_R(regs) \ - ((((regs)->cond_reg |= ((DMA_ENABLE|DMA_INT_ENAB)&(~DMA_ST_WRITE))))) - -/* For certain DMA chips, we need to disable ints upon irq entry - * and turn them back on when we are done. So in any ESP interrupt - * handler you *must* call DMA_IRQ_ENTRY upon entry and DMA_IRQ_EXIT - * when leaving the handler. You have been warned... - */ -#define DMA_IRQ_ENTRY(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSOFF(dregs); \ - } while (0) - -#define DMA_IRQ_EXIT(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSON(dregs); \ - } while(0) - -#if 0 /* P3 this stuff is inline in ledma.c:init_restart_ledma() */ -/* Pause until counter runs out or BIT isn't set in the DMA condition - * register. - */ -static inline void sparc_dma_pause(struct sparc_dma_registers *regs, - unsigned long bit) -{ - int ctr = 50000; /* Let's find some bugs ;) */ - - /* Busy wait until the bit is not set any more */ - while((regs->cond_reg&bit) && (ctr>0)) { - ctr--; - __delay(5); - } - - /* Check for bogus outcome. */ - if(!ctr) - panic("DMA timeout"); -} - -/* Reset the friggin' thing... */ -#define DMA_RESET(dma) do { \ - struct sparc_dma_registers *regs = dma->regs; \ - /* Let the current FIFO drain itself */ \ - sparc_dma_pause(regs, (DMA_FIFO_ISDRAIN)); \ - /* Reset the logic */ \ - regs->cond_reg |= (DMA_RST_SCSI); /* assert */ \ - __delay(400); /* let the bits set ;) */ \ - regs->cond_reg &= ~(DMA_RST_SCSI); /* de-assert */ \ - sparc_dma_enable_interrupts(regs); /* Re-enable interrupts */ \ - /* Enable FAST transfers if available */ \ - if(dma->revision>dvmarev1) regs->cond_reg |= DMA_3CLKS; \ - dma->running = 0; \ -} while(0) -#endif - -#define for_each_dvma(dma) \ - for((dma) = dma_chain; (dma); (dma) = (dma)->next) - -extern int get_dma_list(char *); -extern int request_dma(unsigned int, __const__ char *); -extern void free_dma(unsigned int); - -/* From PCI */ - -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) -#endif - -/* Routines for data transfer buffers. */ -BTFIXUPDEF_CALL(char *, mmu_lockarea, char *, unsigned long) -BTFIXUPDEF_CALL(void, mmu_unlockarea, char *, unsigned long) - -#define mmu_lockarea(vaddr,len) BTFIXUP_CALL(mmu_lockarea)(vaddr,len) -#define mmu_unlockarea(vaddr,len) BTFIXUP_CALL(mmu_unlockarea)(vaddr,len) - -/* These are implementations for sbus_map_sg/sbus_unmap_sg... collapse later */ -BTFIXUPDEF_CALL(__u32, mmu_get_scsi_one, char *, unsigned long, struct sbus_bus *sbus) -BTFIXUPDEF_CALL(void, mmu_get_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus) -BTFIXUPDEF_CALL(void, mmu_release_scsi_one, __u32, unsigned long, struct sbus_bus *sbus) -BTFIXUPDEF_CALL(void, mmu_release_scsi_sgl, struct scatterlist *, int, struct sbus_bus *sbus) - -#define mmu_get_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_get_scsi_one)(vaddr,len,sbus) -#define mmu_get_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_get_scsi_sgl)(sg,sz,sbus) -#define mmu_release_scsi_one(vaddr,len,sbus) BTFIXUP_CALL(mmu_release_scsi_one)(vaddr,len,sbus) -#define mmu_release_scsi_sgl(sg,sz,sbus) BTFIXUP_CALL(mmu_release_scsi_sgl)(sg,sz,sbus) - -/* - * mmu_map/unmap are provided by iommu/iounit; Invalid to call on IIep. - * - * The mmu_map_dma_area establishes two mappings in one go. - * These mappings point to pages normally mapped at 'va' (linear address). - * First mapping is for CPU visible address at 'a', uncached. - * This is an alias, but it works because it is an uncached mapping. - * Second mapping is for device visible address, or "bus" address. - * The bus address is returned at '*pba'. - * - * These functions seem distinct, but are hard to split. On sun4c, - * at least for now, 'a' is equal to bus address, and retured in *pba. - * On sun4m, page attributes depend on the CPU type, so we have to - * know if we are mapping RAM or I/O, so it has to be an additional argument - * to a separate mapping function for CPU visible mappings. - */ -BTFIXUPDEF_CALL(int, mmu_map_dma_area, dma_addr_t *, unsigned long, unsigned long, int len) -BTFIXUPDEF_CALL(struct page *, mmu_translate_dvma, unsigned long busa) -BTFIXUPDEF_CALL(void, mmu_unmap_dma_area, unsigned long busa, int len) - -#define mmu_map_dma_area(pba,va,a,len) BTFIXUP_CALL(mmu_map_dma_area)(pba,va,a,len) -#define mmu_unmap_dma_area(ba,len) BTFIXUP_CALL(mmu_unmap_dma_area)(ba,len) -#define mmu_translate_dvma(ba) BTFIXUP_CALL(mmu_translate_dvma)(ba) - -#endif /* !(_ASM_SPARC_DMA_H) */ diff --git a/arch/sparc/include/asm/dma_64.h b/arch/sparc/include/asm/dma_64.h deleted file mode 100644 index 46a8aecffc0..00000000000 --- a/arch/sparc/include/asm/dma_64.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * include/asm/dma.h - * - * Copyright 1996 (C) David S. Miller (davem@caip.rutgers.edu) - */ - -#ifndef _ASM_SPARC64_DMA_H -#define _ASM_SPARC64_DMA_H - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/spinlock.h> - -#include <asm/sbus.h> -#include <asm/delay.h> -#include <asm/oplib.h> - -/* These are irrelevant for Sparc DMA, but we leave it in so that - * things can compile. - */ -#define MAX_DMA_CHANNELS 8 -#define DMA_MODE_READ 1 -#define DMA_MODE_WRITE 2 -#define MAX_DMA_ADDRESS (~0UL) - -/* Useful constants */ -#define SIZE_16MB (16*1024*1024) -#define SIZE_64K (64*1024) - -/* SBUS DMA controller reg offsets */ -#define DMA_CSR 0x00UL /* rw DMA control/status register 0x00 */ -#define DMA_ADDR 0x04UL /* rw DMA transfer address register 0x04 */ -#define DMA_COUNT 0x08UL /* rw DMA transfer count register 0x08 */ -#define DMA_TEST 0x0cUL /* rw DMA test/debug register 0x0c */ - -/* DVMA chip revisions */ -enum dvma_rev { - dvmarev0, - dvmaesc1, - dvmarev1, - dvmarev2, - dvmarev3, - dvmarevplus, - dvmahme -}; - -#define DMA_HASCOUNT(rev) ((rev)==dvmaesc1) - -/* Linux DMA information structure, filled during probe. */ -struct sbus_dma { - struct sbus_dma *next; - struct sbus_dev *sdev; - void __iomem *regs; - - /* Status, misc info */ - int node; /* Prom node for this DMA device */ - int running; /* Are we doing DMA now? */ - int allocated; /* Are we "owned" by anyone yet? */ - - /* Transfer information. */ - u32 addr; /* Start address of current transfer */ - int nbytes; /* Size of current transfer */ - int realbytes; /* For splitting up large transfers, etc. */ - - /* DMA revision */ - enum dvma_rev revision; -}; - -extern struct sbus_dma *dma_chain; - -/* Broken hardware... */ -#define DMA_ISBROKEN(dma) ((dma)->revision == dvmarev1) -#define DMA_ISESC1(dma) ((dma)->revision == dvmaesc1) - -/* Main routines in dma.c */ -extern void dvma_init(struct sbus_bus *); - -/* Fields in the cond_reg register */ -/* First, the version identification bits */ -#define DMA_DEVICE_ID 0xf0000000 /* Device identification bits */ -#define DMA_VERS0 0x00000000 /* Sunray DMA version */ -#define DMA_ESCV1 0x40000000 /* DMA ESC Version 1 */ -#define DMA_VERS1 0x80000000 /* DMA rev 1 */ -#define DMA_VERS2 0xa0000000 /* DMA rev 2 */ -#define DMA_VERHME 0xb0000000 /* DMA hme gate array */ -#define DMA_VERSPLUS 0x90000000 /* DMA rev 1 PLUS */ - -#define DMA_HNDL_INTR 0x00000001 /* An IRQ needs to be handled */ -#define DMA_HNDL_ERROR 0x00000002 /* We need to take an error */ -#define DMA_FIFO_ISDRAIN 0x0000000c /* The DMA FIFO is draining */ -#define DMA_INT_ENAB 0x00000010 /* Turn on interrupts */ -#define DMA_FIFO_INV 0x00000020 /* Invalidate the FIFO */ -#define DMA_ACC_SZ_ERR 0x00000040 /* The access size was bad */ -#define DMA_FIFO_STDRAIN 0x00000040 /* DMA_VERS1 Drain the FIFO */ -#define DMA_RST_SCSI 0x00000080 /* Reset the SCSI controller */ -#define DMA_RST_ENET DMA_RST_SCSI /* Reset the ENET controller */ -#define DMA_ST_WRITE 0x00000100 /* write from device to memory */ -#define DMA_ENABLE 0x00000200 /* Fire up DMA, handle requests */ -#define DMA_PEND_READ 0x00000400 /* DMA_VERS1/0/PLUS Pending Read */ -#define DMA_ESC_BURST 0x00000800 /* 1=16byte 0=32byte */ -#define DMA_READ_AHEAD 0x00001800 /* DMA read ahead partial longword */ -#define DMA_DSBL_RD_DRN 0x00001000 /* No EC drain on slave reads */ -#define DMA_BCNT_ENAB 0x00002000 /* If on, use the byte counter */ -#define DMA_TERM_CNTR 0x00004000 /* Terminal counter */ -#define DMA_SCSI_SBUS64 0x00008000 /* HME: Enable 64-bit SBUS mode. */ -#define DMA_CSR_DISAB 0x00010000 /* No FIFO drains during csr */ -#define DMA_SCSI_DISAB 0x00020000 /* No FIFO drains during reg */ -#define DMA_DSBL_WR_INV 0x00020000 /* No EC inval. on slave writes */ -#define DMA_ADD_ENABLE 0x00040000 /* Special ESC DVMA optimization */ -#define DMA_E_BURSTS 0x000c0000 /* ENET: SBUS r/w burst mask */ -#define DMA_E_BURST32 0x00040000 /* ENET: SBUS 32 byte r/w burst */ -#define DMA_E_BURST16 0x00000000 /* ENET: SBUS 16 byte r/w burst */ -#define DMA_BRST_SZ 0x000c0000 /* SCSI: SBUS r/w burst size */ -#define DMA_BRST64 0x000c0000 /* SCSI: 64byte bursts (HME on UltraSparc only) */ -#define DMA_BRST32 0x00040000 /* SCSI: 32byte bursts */ -#define DMA_BRST16 0x00000000 /* SCSI: 16byte bursts */ -#define DMA_BRST0 0x00080000 /* SCSI: no bursts (non-HME gate arrays) */ -#define DMA_ADDR_DISAB 0x00100000 /* No FIFO drains during addr */ -#define DMA_2CLKS 0x00200000 /* Each transfer = 2 clock ticks */ -#define DMA_3CLKS 0x00400000 /* Each transfer = 3 clock ticks */ -#define DMA_EN_ENETAUI DMA_3CLKS /* Put lance into AUI-cable mode */ -#define DMA_CNTR_DISAB 0x00800000 /* No IRQ when DMA_TERM_CNTR set */ -#define DMA_AUTO_NADDR 0x01000000 /* Use "auto nxt addr" feature */ -#define DMA_SCSI_ON 0x02000000 /* Enable SCSI dma */ -#define DMA_PARITY_OFF 0x02000000 /* HME: disable parity checking */ -#define DMA_LOADED_ADDR 0x04000000 /* Address has been loaded */ -#define DMA_LOADED_NADDR 0x08000000 /* Next address has been loaded */ -#define DMA_RESET_FAS366 0x08000000 /* HME: Assert RESET to FAS366 */ - -/* Values describing the burst-size property from the PROM */ -#define DMA_BURST1 0x01 -#define DMA_BURST2 0x02 -#define DMA_BURST4 0x04 -#define DMA_BURST8 0x08 -#define DMA_BURST16 0x10 -#define DMA_BURST32 0x20 -#define DMA_BURST64 0x40 -#define DMA_BURSTBITS 0x7f - -/* Determine highest possible final transfer address given a base */ -#define DMA_MAXEND(addr) (0x01000000UL-(((unsigned long)(addr))&0x00ffffffUL)) - -/* Yes, I hack a lot of elisp in my spare time... */ -#define DMA_ERROR_P(regs) ((sbus_readl((regs) + DMA_CSR) & DMA_HNDL_ERROR)) -#define DMA_IRQ_P(regs) ((sbus_readl((regs) + DMA_CSR)) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) -#define DMA_WRITE_P(regs) ((sbus_readl((regs) + DMA_CSR) & DMA_ST_WRITE)) -#define DMA_OFF(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp &= ~DMA_ENABLE; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_INTSOFF(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp &= ~DMA_INT_ENAB; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_INTSON(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= DMA_INT_ENAB; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_PUNTFIFO(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= DMA_FIFO_INV; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_SETSTART(__regs, __addr) \ - sbus_writel((u32)(__addr), (__regs) + DMA_ADDR); -#define DMA_BEGINDMA_W(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= (DMA_ST_WRITE|DMA_ENABLE|DMA_INT_ENAB); \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) -#define DMA_BEGINDMA_R(__regs) \ -do { u32 tmp = sbus_readl((__regs) + DMA_CSR); \ - tmp |= (DMA_ENABLE|DMA_INT_ENAB); \ - tmp &= ~DMA_ST_WRITE; \ - sbus_writel(tmp, (__regs) + DMA_CSR); \ -} while(0) - -/* For certain DMA chips, we need to disable ints upon irq entry - * and turn them back on when we are done. So in any ESP interrupt - * handler you *must* call DMA_IRQ_ENTRY upon entry and DMA_IRQ_EXIT - * when leaving the handler. You have been warned... - */ -#define DMA_IRQ_ENTRY(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSOFF(dregs); \ - } while (0) - -#define DMA_IRQ_EXIT(dma, dregs) do { \ - if(DMA_ISBROKEN(dma)) DMA_INTSON(dregs); \ - } while(0) - -#define for_each_dvma(dma) \ - for((dma) = dma_chain; (dma); (dma) = (dma)->next) - -/* From PCI */ - -#ifdef CONFIG_PCI -extern int isa_dma_bridge_buggy; -#else -#define isa_dma_bridge_buggy (0) -#endif - -#endif /* !(_ASM_SPARC64_DMA_H) */ diff --git a/arch/sparc/include/asm/ebus.h b/arch/sparc/include/asm/ebus.h deleted file mode 100644 index 83a6d16c22e..00000000000 --- a/arch/sparc/include/asm/ebus.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_EBUS_H -#define ___ASM_SPARC_EBUS_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/ebus_64.h> -#else -#include <asm/ebus_32.h> -#endif -#endif diff --git a/arch/sparc/include/asm/ebus_32.h b/arch/sparc/include/asm/ebus_32.h deleted file mode 100644 index f91f0b267ce..00000000000 --- a/arch/sparc/include/asm/ebus_32.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * ebus.h: PCI to Ebus pseudo driver software state. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Adopted for sparc by V. Roganov and G. Raiko. - */ - -#ifndef __SPARC_EBUS_H -#define __SPARC_EBUS_H - -#ifndef _LINUX_IOPORT_H -#include <linux/ioport.h> -#endif -#include <linux/of_device.h> -#include <asm/oplib.h> -#include <asm/prom.h> - -struct linux_ebus_child { - struct linux_ebus_child *next; - struct linux_ebus_device *parent; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; - -struct linux_ebus_device { - struct of_device ofdev; - struct linux_ebus_device *next; - struct linux_ebus_child *children; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; -#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev) - -struct linux_ebus { - struct of_device ofdev; - struct linux_ebus *next; - struct linux_ebus_device *devices; - struct linux_pbm_info *parent; - struct pci_dev *self; - struct device_node *prom_node; -}; -#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev) - -struct linux_ebus_dma { - unsigned int dcsr; - unsigned int dacr; - unsigned int dbcr; -}; - -#define EBUS_DCSR_INT_PEND 0x00000001 -#define EBUS_DCSR_ERR_PEND 0x00000002 -#define EBUS_DCSR_DRAIN 0x00000004 -#define EBUS_DCSR_INT_EN 0x00000010 -#define EBUS_DCSR_RESET 0x00000080 -#define EBUS_DCSR_WRITE 0x00000100 -#define EBUS_DCSR_EN_DMA 0x00000200 -#define EBUS_DCSR_CYC_PEND 0x00000400 -#define EBUS_DCSR_DIAG_RD_DONE 0x00000800 -#define EBUS_DCSR_DIAG_WR_DONE 0x00001000 -#define EBUS_DCSR_EN_CNT 0x00002000 -#define EBUS_DCSR_TC 0x00004000 -#define EBUS_DCSR_DIS_CSR_DRN 0x00010000 -#define EBUS_DCSR_BURST_SZ_MASK 0x000c0000 -#define EBUS_DCSR_BURST_SZ_1 0x00080000 -#define EBUS_DCSR_BURST_SZ_4 0x00000000 -#define EBUS_DCSR_BURST_SZ_8 0x00040000 -#define EBUS_DCSR_BURST_SZ_16 0x000c0000 -#define EBUS_DCSR_DIAG_EN 0x00100000 -#define EBUS_DCSR_DIS_ERR_PEND 0x00400000 -#define EBUS_DCSR_TCI_DIS 0x00800000 -#define EBUS_DCSR_EN_NEXT 0x01000000 -#define EBUS_DCSR_DMA_ON 0x02000000 -#define EBUS_DCSR_A_LOADED 0x04000000 -#define EBUS_DCSR_NA_LOADED 0x08000000 -#define EBUS_DCSR_DEV_ID_MASK 0xf0000000 - -extern struct linux_ebus *ebus_chain; - -extern void ebus_init(void); - -#define for_each_ebus(bus) \ - for((bus) = ebus_chain; (bus); (bus) = (bus)->next) - -#define for_each_ebusdev(dev, bus) \ - for((dev) = (bus)->devices; (dev); (dev) = (dev)->next) - -#define for_each_edevchild(dev, child) \ - for((child) = (dev)->children; (child); (child) = (child)->next) - -#endif /* !(__SPARC_EBUS_H) */ diff --git a/arch/sparc/include/asm/ebus_64.h b/arch/sparc/include/asm/ebus_64.h deleted file mode 100644 index 14c6a111f60..00000000000 --- a/arch/sparc/include/asm/ebus_64.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * ebus.h: PCI to Ebus pseudo driver software state. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1999 David S. Miller (davem@redhat.com) - */ - -#ifndef __SPARC64_EBUS_H -#define __SPARC64_EBUS_H - -#include <linux/of_device.h> - -#include <asm/oplib.h> -#include <asm/prom.h> - -struct linux_ebus_child { - struct linux_ebus_child *next; - struct linux_ebus_device *parent; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; - -struct linux_ebus_device { - struct of_device ofdev; - struct linux_ebus_device *next; - struct linux_ebus_child *children; - struct linux_ebus *bus; - struct device_node *prom_node; - struct resource resource[PROMREG_MAX]; - int num_addrs; - unsigned int irqs[PROMINTR_MAX]; - int num_irqs; -}; -#define to_ebus_device(d) container_of(d, struct linux_ebus_device, ofdev.dev) - -struct linux_ebus { - struct of_device ofdev; - struct linux_ebus *next; - struct linux_ebus_device *devices; - struct pci_dev *self; - int index; - int is_rio; - struct device_node *prom_node; -}; -#define to_ebus(d) container_of(d, struct linux_ebus, ofdev.dev) - -struct ebus_dma_info { - spinlock_t lock; - void __iomem *regs; - - unsigned int flags; -#define EBUS_DMA_FLAG_USE_EBDMA_HANDLER 0x00000001 -#define EBUS_DMA_FLAG_TCI_DISABLE 0x00000002 - - /* These are only valid is EBUS_DMA_FLAG_USE_EBDMA_HANDLER is - * set. - */ - void (*callback)(struct ebus_dma_info *p, int event, void *cookie); - void *client_cookie; - unsigned int irq; -#define EBUS_DMA_EVENT_ERROR 1 -#define EBUS_DMA_EVENT_DMA 2 -#define EBUS_DMA_EVENT_DEVICE 4 - - unsigned char name[64]; -}; - -extern int ebus_dma_register(struct ebus_dma_info *p); -extern int ebus_dma_irq_enable(struct ebus_dma_info *p, int on); -extern void ebus_dma_unregister(struct ebus_dma_info *p); -extern int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, - size_t len); -extern void ebus_dma_prepare(struct ebus_dma_info *p, int write); -extern unsigned int ebus_dma_residue(struct ebus_dma_info *p); -extern unsigned int ebus_dma_addr(struct ebus_dma_info *p); -extern void ebus_dma_enable(struct ebus_dma_info *p, int on); - -extern struct linux_ebus *ebus_chain; - -extern void ebus_init(void); - -#define for_each_ebus(bus) \ - for((bus) = ebus_chain; (bus); (bus) = (bus)->next) - -#define for_each_ebusdev(dev, bus) \ - for((dev) = (bus)->devices; (dev); (dev) = (dev)->next) - -#define for_each_edevchild(dev, child) \ - for((child) = (dev)->children; (child); (child) = (child)->next) - -#endif /* !(__SPARC64_EBUS_H) */ diff --git a/arch/sparc/include/asm/ebus_dma.h b/arch/sparc/include/asm/ebus_dma.h new file mode 100644 index 00000000000..f07a5b541c9 --- /dev/null +++ b/arch/sparc/include/asm/ebus_dma.h @@ -0,0 +1,35 @@ +#ifndef __ASM_SPARC_EBUS_DMA_H +#define __ASM_SPARC_EBUS_DMA_H + +struct ebus_dma_info { + spinlock_t lock; + void __iomem *regs; + + unsigned int flags; +#define EBUS_DMA_FLAG_USE_EBDMA_HANDLER 0x00000001 +#define EBUS_DMA_FLAG_TCI_DISABLE 0x00000002 + + /* These are only valid is EBUS_DMA_FLAG_USE_EBDMA_HANDLER is + * set. + */ + void (*callback)(struct ebus_dma_info *p, int event, void *cookie); + void *client_cookie; + unsigned int irq; +#define EBUS_DMA_EVENT_ERROR 1 +#define EBUS_DMA_EVENT_DMA 2 +#define EBUS_DMA_EVENT_DEVICE 4 + + unsigned char name[64]; +}; + +extern int ebus_dma_register(struct ebus_dma_info *p); +extern int ebus_dma_irq_enable(struct ebus_dma_info *p, int on); +extern void ebus_dma_unregister(struct ebus_dma_info *p); +extern int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, + size_t len); +extern void ebus_dma_prepare(struct ebus_dma_info *p, int write); +extern unsigned int ebus_dma_residue(struct ebus_dma_info *p); +extern unsigned int ebus_dma_addr(struct ebus_dma_info *p); +extern void ebus_dma_enable(struct ebus_dma_info *p, int on); + +#endif /* __ASM_SPARC_EBUS_DMA_H */ diff --git a/arch/sparc/include/asm/elf_32.h b/arch/sparc/include/asm/elf_32.h index d043f80bc2f..b7ab6054782 100644 --- a/arch/sparc/include/asm/elf_32.h +++ b/arch/sparc/include/asm/elf_32.h @@ -105,11 +105,8 @@ typedef struct { #define ELF_DATA ELFDATA2MSB #define USE_ELF_CORE_DUMP -#ifndef CONFIG_SUN4 + #define ELF_EXEC_PAGESIZE 4096 -#else -#define ELF_EXEC_PAGESIZE 8192 -#endif /* This is the location that an ET_DYN program is loaded if exec'ed. Typical @@ -126,7 +123,7 @@ typedef struct { /* Sun4c has none of the capabilities, most sun4m's have them all. * XXX This is gross, set some global variable at boot time. -DaveM */ -#define ELF_HWCAP ((ARCH_SUN4C_SUN4) ? 0 : \ +#define ELF_HWCAP ((ARCH_SUN4C) ? 0 : \ (HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | \ HWCAP_SPARC_SWAP | \ ((srmmu_modtype != Cypress && \ diff --git a/arch/sparc/include/asm/fhc.h b/arch/sparc/include/asm/fhc.h index 788cbc46a11..57f1b303ad5 100644 --- a/arch/sparc/include/asm/fhc.h +++ b/arch/sparc/include/asm/fhc.h @@ -1,5 +1,4 @@ -/* - * fhc.h: Structures for central/fhc pseudo driver on Sunfire/Starfire/Wildfire. +/* fhc.h: FHC and Clock board register definitions. * * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com) */ @@ -7,14 +6,6 @@ #ifndef _SPARC64_FHC_H #define _SPARC64_FHC_H -#include <linux/timer.h> - -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/upa.h> - -struct linux_fhc; - /* Clock board register offsets. */ #define CLOCK_CTRL 0x00UL /* Main control */ #define CLOCK_STAT1 0x10UL /* Status one */ @@ -29,21 +20,7 @@ struct linux_fhc; #define CLOCK_CTRL_MLED 0x02 /* Mid LED, 1 == on */ #define CLOCK_CTRL_RLED 0x01 /* RIght LED, 1 == on */ -struct linux_central { - struct linux_fhc *child; - unsigned long cfreg; - unsigned long clkregs; - unsigned long clkver; - int slots; - struct device_node *prom_node; - - struct linux_prom_ranges central_ranges[PROMREG_MAX]; - int num_central_ranges; -}; - /* Firehose controller register offsets */ -struct fhc_regs { - unsigned long pregs; /* FHC internal regs */ #define FHC_PREGS_ID 0x00UL /* FHC ID */ #define FHC_ID_VERS 0xf0000000 /* Version of this FHC */ #define FHC_ID_PARTID 0x0ffff000 /* Part ID code (0x0f9f == FHC) */ @@ -90,32 +67,14 @@ struct fhc_regs { #define FHC_JTAG_CTRL_MENAB 0x80000000 /* Indicates this is JTAG Master */ #define FHC_JTAG_CTRL_MNONE 0x40000000 /* Indicates no JTAG Master present */ #define FHC_PREGS_JCMD 0x100UL /* FHC JTAG Command Register */ - unsigned long ireg; /* FHC IGN reg */ #define FHC_IREG_IGN 0x00UL /* This FHC's IGN */ - unsigned long ffregs; /* FHC fanfail regs */ #define FHC_FFREGS_IMAP 0x00UL /* FHC Fanfail IMAP */ #define FHC_FFREGS_ICLR 0x10UL /* FHC Fanfail ICLR */ - unsigned long sregs; /* FHC system regs */ #define FHC_SREGS_IMAP 0x00UL /* FHC System IMAP */ #define FHC_SREGS_ICLR 0x10UL /* FHC System ICLR */ - unsigned long uregs; /* FHC uart regs */ #define FHC_UREGS_IMAP 0x00UL /* FHC Uart IMAP */ #define FHC_UREGS_ICLR 0x10UL /* FHC Uart ICLR */ - unsigned long tregs; /* FHC TOD regs */ #define FHC_TREGS_IMAP 0x00UL /* FHC TOD IMAP */ #define FHC_TREGS_ICLR 0x10UL /* FHC TOD ICLR */ -}; - -struct linux_fhc { - struct linux_fhc *next; - struct linux_central *parent; /* NULL if not central FHC */ - struct fhc_regs fhc_regs; - int board; - int jtag_master; - struct device_node *prom_node; - - struct linux_prom_ranges fhc_ranges[PROMREG_MAX]; - int num_fhc_ranges; -}; #endif /* !(_SPARC64_FHC_H) */ diff --git a/arch/sparc/include/asm/floppy_32.h b/arch/sparc/include/asm/floppy_32.h index ae3f00bf22f..c792830636d 100644 --- a/arch/sparc/include/asm/floppy_32.h +++ b/arch/sparc/include/asm/floppy_32.h @@ -6,6 +6,9 @@ #ifndef __ASM_SPARC_FLOPPY_H #define __ASM_SPARC_FLOPPY_H +#include <linux/of.h> +#include <linux/of_device.h> + #include <asm/page.h> #include <asm/pgtable.h> #include <asm/system.h> @@ -343,7 +346,7 @@ static int sun_floppy_init(void) r.flags = fd_regs[0].which_io; r.start = fd_regs[0].phys_addr; sun_fdc = (struct sun_flpy_controller *) - sbus_ioremap(&r, 0, fd_regs[0].reg_size, "floppy"); + of_ioremap(&r, 0, fd_regs[0].reg_size, "floppy"); /* Last minute sanity check... */ if(sun_fdc->status_82072 == 0xff) { @@ -385,4 +388,15 @@ static int sparc_eject(void) #define EXTRA_FLOPPY_PARAMS +static DEFINE_SPINLOCK(dma_spin_lock); + +#define claim_dma_lock() \ +({ unsigned long flags; \ + spin_lock_irqsave(&dma_spin_lock, flags); \ + flags; \ +}) + +#define release_dma_lock(__flags) \ + spin_unlock_irqrestore(&dma_spin_lock, __flags); + #endif /* !(__ASM_SPARC_FLOPPY_H) */ diff --git a/arch/sparc/include/asm/floppy_64.h b/arch/sparc/include/asm/floppy_64.h index c39db1060bc..36439d67ad7 100644 --- a/arch/sparc/include/asm/floppy_64.h +++ b/arch/sparc/include/asm/floppy_64.h @@ -1,6 +1,6 @@ /* floppy.h: Sparc specific parts of the Floppy driver. * - * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * * Ultra/PCI support added: Sep 1997 Eddie C. Dost (ecd@skynet.be) @@ -9,18 +9,11 @@ #ifndef __ASM_SPARC64_FLOPPY_H #define __ASM_SPARC64_FLOPPY_H -#include <linux/init.h> -#include <linux/pci.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/dma-mapping.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/system.h> -#include <asm/idprom.h> -#include <asm/oplib.h> #include <asm/auxio.h> -#include <asm/sbus.h> -#include <asm/irq.h> - /* * Define this to enable exchanging drive 0 and 1 if only drive 1 is @@ -50,7 +43,7 @@ struct sun_flpy_controller { /* You'll only ever find one controller on an Ultra anyways. */ static struct sun_flpy_controller *sun_fdc = (struct sun_flpy_controller *)-1; unsigned long fdc_status; -static struct sbus_dev *floppy_sdev = NULL; +static struct of_device *floppy_op = NULL; struct sun_floppy_ops { unsigned char (*fd_inb) (unsigned long port); @@ -291,12 +284,11 @@ static int sun_fd_eject(int drive) return 0; } -#ifdef CONFIG_PCI -#include <asm/ebus.h> +#include <asm/ebus_dma.h> #include <asm/ns87303.h> static struct ebus_dma_info sun_pci_fd_ebus_dma; -static struct pci_dev *sun_pci_ebus_dev; +static struct device *sun_floppy_dev; static int sun_pci_broken_drive = -1; struct sun_pci_dma_op { @@ -377,7 +369,7 @@ static void sun_pci_fd_enable_dma(void) sun_pci_dma_pending.addr = -1U; sun_pci_dma_current.addr = - pci_map_single(sun_pci_ebus_dev, + dma_map_single(sun_floppy_dev, sun_pci_dma_current.buf, sun_pci_dma_current.len, sun_pci_dma_current.direction); @@ -394,7 +386,7 @@ static void sun_pci_fd_disable_dma(void) { ebus_dma_enable(&sun_pci_fd_ebus_dma, 0); if (sun_pci_dma_current.addr != -1U) - pci_unmap_single(sun_pci_ebus_dev, + dma_unmap_single(sun_floppy_dev, sun_pci_dma_current.addr, sun_pci_dma_current.len, sun_pci_dma_current.direction); @@ -404,9 +396,9 @@ static void sun_pci_fd_disable_dma(void) static void sun_pci_fd_set_dma_mode(int mode) { if (mode == DMA_MODE_WRITE) - sun_pci_dma_pending.direction = PCI_DMA_TODEVICE; + sun_pci_dma_pending.direction = DMA_TO_DEVICE; else - sun_pci_dma_pending.direction = PCI_DMA_FROMDEVICE; + sun_pci_dma_pending.direction = DMA_FROM_DEVICE; ebus_dma_prepare(&sun_pci_fd_ebus_dma, mode != DMA_MODE_WRITE); } @@ -538,80 +530,84 @@ static int sun_pci_fd_test_drive(unsigned long port, int drive) #undef MSR #undef DOR -#endif /* CONFIG_PCI */ - -#ifdef CONFIG_PCI -static int __init ebus_fdthree_p(struct linux_ebus_device *edev) +static int __init ebus_fdthree_p(struct device_node *dp) { - if (!strcmp(edev->prom_node->name, "fdthree")) + if (!strcmp(dp->name, "fdthree")) return 1; - if (!strcmp(edev->prom_node->name, "floppy")) { + if (!strcmp(dp->name, "floppy")) { const char *compat; - compat = of_get_property(edev->prom_node, - "compatible", NULL); + compat = of_get_property(dp, "compatible", NULL); if (compat && !strcmp(compat, "fdthree")) return 1; } return 0; } -#endif static unsigned long __init sun_floppy_init(void) { - char state[128]; - struct sbus_bus *bus; - struct sbus_dev *sdev = NULL; static int initialized = 0; + struct device_node *dp; + struct of_device *op; + const char *prop; + char state[128]; if (initialized) return sun_floppy_types[0]; initialized = 1; - for_all_sbusdev (sdev, bus) { - if (!strcmp(sdev->prom_name, "SUNW,fdtwo")) + op = NULL; + + for_each_node_by_name(dp, "SUNW,fdtwo") { + if (strcmp(dp->parent->name, "sbus")) + continue; + op = of_find_device_by_node(dp); + if (op) break; } - if(sdev) { - floppy_sdev = sdev; - FLOPPY_IRQ = sdev->irqs[0]; + if (op) { + floppy_op = op; + FLOPPY_IRQ = op->irqs[0]; } else { -#ifdef CONFIG_PCI - struct linux_ebus *ebus; - struct linux_ebus_device *edev = NULL; - unsigned long config = 0; + struct device_node *ebus_dp; void __iomem *auxio_reg; const char *state_prop; + unsigned long config; - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (ebus_fdthree_p(edev)) - goto ebus_done; + dp = NULL; + for_each_node_by_name(ebus_dp, "ebus") { + for (dp = ebus_dp->child; dp; dp = dp->sibling) { + if (ebus_fdthree_p(dp)) + goto found_fdthree; } } - ebus_done: - if (!edev) + found_fdthree: + if (!dp) + return 0; + + op = of_find_device_by_node(dp); + if (!op) return 0; - state_prop = of_get_property(edev->prom_node, "status", NULL); + state_prop = of_get_property(op->node, "status", NULL); if (state_prop && !strncmp(state_prop, "disabled", 8)) return 0; - FLOPPY_IRQ = edev->irqs[0]; + FLOPPY_IRQ = op->irqs[0]; /* Make sure the high density bit is set, some systems * (most notably Ultra5/Ultra10) come up with it clear. */ - auxio_reg = (void __iomem *) edev->resource[2].start; + auxio_reg = (void __iomem *) op->resource[2].start; writel(readl(auxio_reg)|0x2, auxio_reg); - sun_pci_ebus_dev = ebus->self; + sun_floppy_dev = &op->dev; spin_lock_init(&sun_pci_fd_ebus_dma.lock); /* XXX ioremap */ sun_pci_fd_ebus_dma.regs = (void __iomem *) - edev->resource[1].start; + op->resource[1].start; if (!sun_pci_fd_ebus_dma.regs) return 0; @@ -625,7 +621,7 @@ static unsigned long __init sun_floppy_init(void) return 0; /* XXX ioremap */ - sun_fdc = (struct sun_flpy_controller *)edev->resource[0].start; + sun_fdc = (struct sun_flpy_controller *) op->resource[0].start; sun_fdops.fd_inb = sun_pci_fd_inb; sun_fdops.fd_outb = sun_pci_fd_outb; @@ -662,12 +658,15 @@ static unsigned long __init sun_floppy_init(void) /* * Find NS87303 SuperIO config registers (through ecpp). */ - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "ecpp")) { - config = edev->resource[1].start; - goto config_done; - } + config = 0; + for (dp = ebus_dp->child; dp; dp = dp->sibling) { + if (!strcmp(dp->name, "ecpp")) { + struct of_device *ecpp_op; + + ecpp_op = of_find_device_by_node(dp); + if (ecpp_op) + config = ecpp_op->resource[1].start; + goto config_done; } } config_done: @@ -716,26 +715,23 @@ static unsigned long __init sun_floppy_init(void) #endif /* PCI_FDC_SWAP_DRIVES */ return sun_floppy_types[0]; -#else - return 0; -#endif } - prom_getproperty(sdev->prom_node, "status", state, sizeof(state)); - if(!strncmp(state, "disabled", 8)) + prop = of_get_property(op->node, "status", NULL); + if (prop && !strncmp(state, "disabled", 8)) return 0; /* - * We cannot do sbus_ioremap here: it does request_region, + * We cannot do of_ioremap here: it does request_region, * which the generic floppy driver tries to do once again. * But we must use the sdev resource values as they have * had parent ranges applied. */ sun_fdc = (struct sun_flpy_controller *) - (sdev->resource[0].start + - ((sdev->resource[0].flags & 0x1ffUL) << 32UL)); + (op->resource[0].start + + ((op->resource[0].flags & 0x1ffUL) << 32UL)); /* Last minute sanity check... */ - if(sbus_readb(&sun_fdc->status1_82077) == 0xff) { + if (sbus_readb(&sun_fdc->status1_82077) == 0xff) { sun_fdc = (struct sun_flpy_controller *)-1; return 0; } diff --git a/arch/sparc/include/asm/gpio.h b/arch/sparc/include/asm/gpio.h new file mode 100644 index 00000000000..a0e3ac0af59 --- /dev/null +++ b/arch/sparc/include/asm/gpio.h @@ -0,0 +1,36 @@ +#ifndef __ASM_SPARC_GPIO_H +#define __ASM_SPARC_GPIO_H + +#include <linux/errno.h> +#include <asm-generic/gpio.h> + +#ifdef CONFIG_GPIOLIB + +static inline int gpio_get_value(unsigned int gpio) +{ + return __gpio_get_value(gpio); +} + +static inline void gpio_set_value(unsigned int gpio, int value) +{ + __gpio_set_value(gpio, value); +} + +static inline int gpio_cansleep(unsigned int gpio) +{ + return __gpio_cansleep(gpio); +} + +static inline int gpio_to_irq(unsigned int gpio) +{ + return -ENOSYS; +} + +static inline int irq_to_gpio(unsigned int irq) +{ + return -EINVAL; +} + +#endif /* CONFIG_GPIOLIB */ + +#endif /* __ASM_SPARC_GPIO_H */ diff --git a/arch/sparc/include/asm/io-unit.h b/arch/sparc/include/asm/io-unit.h index 96823b47fd4..01ab2f613e9 100644 --- a/arch/sparc/include/asm/io-unit.h +++ b/arch/sparc/include/asm/io-unit.h @@ -55,8 +55,4 @@ struct iounit_struct { #define IOUNIT_BMAPM_START IOUNIT_BMAP2_END #define IOUNIT_BMAPM_END ((IOUNIT_DMA_SIZE - IOUNIT_DVMA_SIZE) >> PAGE_SHIFT) -extern __u32 iounit_map_dma_init(struct sbus_bus *, int); -#define iounit_map_dma_finish(sbus, addr, len) mmu_release_scsi_one(addr, len, sbus) -extern __u32 iounit_map_dma_page(__u32, void *, struct sbus_bus *); - #endif /* !(_SPARC_IO_UNIT_H) */ diff --git a/arch/sparc/include/asm/io_32.h b/arch/sparc/include/asm/io_32.h index 10d7da45007..93fe21e02c8 100644 --- a/arch/sparc/include/asm/io_32.h +++ b/arch/sparc/include/asm/io_32.h @@ -293,14 +293,6 @@ extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); extern void pci_iounmap(struct pci_dev *dev, void __iomem *); /* - * Bus number may be in res->flags... somewhere. - */ -extern void __iomem *sbus_ioremap(struct resource *res, unsigned long offset, - unsigned long size, char *name); -extern void sbus_iounmap(volatile void __iomem *vaddr, unsigned long size); - - -/* * At the moment, we do not use CMOS_READ anywhere outside of rtc.c, * so rtc_port is static in it. This should not change unless a new * hardware pops up. @@ -308,6 +300,17 @@ extern void sbus_iounmap(volatile void __iomem *vaddr, unsigned long size); #define RTC_PORT(x) (rtc_port + (x)) #define RTC_ALWAYS_BCD 0 +static inline int sbus_can_dma_64bit(void) +{ + return 0; /* actually, sparc_cpu_model==sun4d */ +} +static inline int sbus_can_burst64(void) +{ + return 0; /* actually, sparc_cpu_model==sun4d */ +} +struct device; +extern void sbus_set_sbus64(struct device *, int); + #endif #define __ARCH_HAS_NO_PAGE_ZERO_MAPPED 1 diff --git a/arch/sparc/include/asm/io_64.h b/arch/sparc/include/asm/io_64.h index 0bff078ffdd..4aee21dc9c6 100644 --- a/arch/sparc/include/asm/io_64.h +++ b/arch/sparc/include/asm/io_64.h @@ -482,18 +482,16 @@ struct pci_dev; extern void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long max); extern void pci_iounmap(struct pci_dev *dev, void __iomem *); -/* Similarly for SBUS. */ -#define sbus_ioremap(__res, __offset, __size, __name) \ -({ unsigned long __ret; \ - __ret = (__res)->start + (((__res)->flags & 0x1ffUL) << 32UL); \ - __ret += (unsigned long) (__offset); \ - if (! request_region((__ret), (__size), (__name))) \ - __ret = 0UL; \ - (void __iomem *) __ret; \ -}) - -#define sbus_iounmap(__addr, __size) \ - release_region((unsigned long)(__addr), (__size)) +static inline int sbus_can_dma_64bit(void) +{ + return 1; +} +static inline int sbus_can_burst64(void) +{ + return 1; +} +struct device; +extern void sbus_set_sbus64(struct device *, int); /* * Convert a physical pointer to a virtual kernel pointer for /dev/mem diff --git a/arch/sparc/include/asm/iommu_64.h b/arch/sparc/include/asm/iommu_64.h index d7b9afcba08..caf798b5619 100644 --- a/arch/sparc/include/asm/iommu_64.h +++ b/arch/sparc/include/asm/iommu_64.h @@ -48,6 +48,9 @@ struct strbuf { unsigned long strbuf_control; unsigned long strbuf_pflush; unsigned long strbuf_fsync; + unsigned long strbuf_err_stat; + unsigned long strbuf_tag_diag; + unsigned long strbuf_line_diag; unsigned long strbuf_ctxflush; unsigned long strbuf_ctxmatch_base; unsigned long strbuf_flushflag_pa; diff --git a/arch/sparc/include/asm/irq_64.h b/arch/sparc/include/asm/irq_64.h index e3dd9303643..71673eca366 100644 --- a/arch/sparc/include/asm/irq_64.h +++ b/arch/sparc/include/asm/irq_64.h @@ -56,7 +56,6 @@ extern unsigned int sun4u_build_msi(u32 portid, unsigned int *virt_irq_p, unsigned long imap_base, unsigned long iclr_base); extern void sun4u_destroy_msi(unsigned int virt_irq); -extern unsigned int sbus_build_irq(void *sbus, unsigned int ino); extern unsigned char virt_irq_alloc(unsigned int dev_handle, unsigned int dev_ino); diff --git a/arch/sparc/include/asm/mc146818rtc_64.h b/arch/sparc/include/asm/mc146818rtc_64.h index e9c0fcc25c6..7238d174e0e 100644 --- a/arch/sparc/include/asm/mc146818rtc_64.h +++ b/arch/sparc/include/asm/mc146818rtc_64.h @@ -7,12 +7,8 @@ #include <asm/io.h> #ifndef RTC_PORT -#ifdef CONFIG_PCI -extern unsigned long ds1287_regs; -#else -#define ds1287_regs (0UL) -#endif -#define RTC_PORT(x) (ds1287_regs + (x)) +extern unsigned long cmos_regs; +#define RTC_PORT(x) (cmos_regs + (x)) #define RTC_ALWAYS_BCD 0 #endif @@ -29,6 +25,4 @@ outb_p((addr),RTC_PORT(0)); \ outb_p((val),RTC_PORT(1)); \ }) -#define RTC_IRQ 8 - #endif /* __ASM_SPARC64_MC146818RTC_H */ diff --git a/arch/sparc/include/asm/memctrl.h b/arch/sparc/include/asm/memctrl.h new file mode 100644 index 00000000000..4065c56af7b --- /dev/null +++ b/arch/sparc/include/asm/memctrl.h @@ -0,0 +1,9 @@ +#ifndef _SPARC_MEMCTRL_H +#define _SPARC_MEMCTRL_H + +typedef int (*dimm_printer_t)(int synd_code, unsigned long paddr, char *buf, int buflen); + +int register_dimm_printer(dimm_printer_t func); +void unregister_dimm_printer(dimm_printer_t func); + +#endif /* _SPARC_MEMCTRL_H */ diff --git a/arch/sparc/include/asm/mostek.h b/arch/sparc/include/asm/mostek.h deleted file mode 100644 index 433be3e0a69..00000000000 --- a/arch/sparc/include/asm/mostek.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_MOSTEK_H -#define ___ASM_SPARC_MOSTEK_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/mostek_64.h> -#else -#include <asm/mostek_32.h> -#endif -#endif diff --git a/arch/sparc/include/asm/mostek_32.h b/arch/sparc/include/asm/mostek_32.h deleted file mode 100644 index a99590c4c50..00000000000 --- a/arch/sparc/include/asm/mostek_32.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * mostek.h: Describes the various Mostek time of day clock registers. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - * Added intersil code 05/25/98 Chris Davis (cdavis@cois.on.ca) - */ - -#ifndef _SPARC_MOSTEK_H -#define _SPARC_MOSTEK_H - -#include <asm/idprom.h> -#include <asm/io.h> - -/* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ) - * - * Data - * Address Function - * Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0 - * 7ff - - - - - - - - Year 00-99 - * 7fe 0 0 0 - - - - - Month 01-12 - * 7fd 0 0 - - - - - - Date 01-31 - * 7fc 0 FT 0 0 0 - - - Day 01-07 - * 7fb KS 0 - - - - - - Hours 00-23 - * 7fa 0 - - - - - - - Minutes 00-59 - * 7f9 ST - - - - - - - Seconds 00-59 - * 7f8 W R S - - - - - Control - * - * * ST is STOP BIT - * * W is WRITE BIT - * * R is READ BIT - * * S is SIGN BIT - * * FT is FREQ TEST BIT - * * KS is KICK START BIT - */ - -/* The Mostek 48t02 real time clock and NVRAM chip. The registers - * other than the control register are in binary coded decimal. Some - * control bits also live outside the control register. - */ -#define mostek_read(_addr) readb(_addr) -#define mostek_write(_addr,_val) writeb(_val, _addr) -#define MOSTEK_EEPROM 0x0000UL -#define MOSTEK_IDPROM 0x07d8UL -#define MOSTEK_CREG 0x07f8UL -#define MOSTEK_SEC 0x07f9UL -#define MOSTEK_MIN 0x07faUL -#define MOSTEK_HOUR 0x07fbUL -#define MOSTEK_DOW 0x07fcUL -#define MOSTEK_DOM 0x07fdUL -#define MOSTEK_MONTH 0x07feUL -#define MOSTEK_YEAR 0x07ffUL - -struct mostek48t02 { - volatile char eeprom[2008]; /* This is the eeprom, don't touch! */ - struct idprom idprom; /* The idprom lives here. */ - volatile unsigned char creg; /* Control register */ - volatile unsigned char sec; /* Seconds (0-59) */ - volatile unsigned char min; /* Minutes (0-59) */ - volatile unsigned char hour; /* Hour (0-23) */ - volatile unsigned char dow; /* Day of the week (1-7) */ - volatile unsigned char dom; /* Day of the month (1-31) */ - volatile unsigned char month; /* Month of year (1-12) */ - volatile unsigned char year; /* Year (0-99) */ -}; - -extern spinlock_t mostek_lock; -extern void __iomem *mstk48t02_regs; - -/* Control register values. */ -#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */ -#define MSTK_CREG_READ 0x40 /* Stop updates to allow a clean read. */ -#define MSTK_CREG_SIGN 0x20 /* Slow/speed clock in calibration mode. */ - -/* Control bits that live in the other registers. */ -#define MSTK_STOP 0x80 /* Stop the clock oscillator. (sec) */ -#define MSTK_KICK_START 0x80 /* Kick start the clock chip. (hour) */ -#define MSTK_FREQ_TEST 0x40 /* Frequency test mode. (day) */ - -#define MSTK_YEAR_ZERO 1968 /* If year reg has zero, it is 1968. */ -#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YEAR_ZERO) - -/* Masks that define how much space each value takes up. */ -#define MSTK_SEC_MASK 0x7f -#define MSTK_MIN_MASK 0x7f -#define MSTK_HOUR_MASK 0x3f -#define MSTK_DOW_MASK 0x07 -#define MSTK_DOM_MASK 0x3f -#define MSTK_MONTH_MASK 0x1f -#define MSTK_YEAR_MASK 0xffU - -/* Binary coded decimal conversion macros. */ -#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04)) -#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0x0A) << 0x04) + ((x) % 0x0A)) - -/* Generic register set and get macros for internal use. */ -#define MSTK_GET(regs,var,mask) (MSTK_REGVAL_TO_DECIMAL(((struct mostek48t02 *)regs)->var & MSTK_ ## mask ## _MASK)) -#define MSTK_SET(regs,var,value,mask) do { ((struct mostek48t02 *)regs)->var &= ~(MSTK_ ## mask ## _MASK); ((struct mostek48t02 *)regs)->var |= MSTK_DECIMAL_TO_REGVAL(value) & (MSTK_ ## mask ## _MASK); } while (0) - -/* Macros to make register access easier on our fingers. These give you - * the decimal value of the register requested if applicable. You pass - * the a pointer to a 'struct mostek48t02'. - */ -#define MSTK_REG_CREG(regs) (((struct mostek48t02 *)regs)->creg) -#define MSTK_REG_SEC(regs) MSTK_GET(regs,sec,SEC) -#define MSTK_REG_MIN(regs) MSTK_GET(regs,min,MIN) -#define MSTK_REG_HOUR(regs) MSTK_GET(regs,hour,HOUR) -#define MSTK_REG_DOW(regs) MSTK_GET(regs,dow,DOW) -#define MSTK_REG_DOM(regs) MSTK_GET(regs,dom,DOM) -#define MSTK_REG_MONTH(regs) MSTK_GET(regs,month,MONTH) -#define MSTK_REG_YEAR(regs) MSTK_GET(regs,year,YEAR) - -#define MSTK_SET_REG_SEC(regs,value) MSTK_SET(regs,sec,value,SEC) -#define MSTK_SET_REG_MIN(regs,value) MSTK_SET(regs,min,value,MIN) -#define MSTK_SET_REG_HOUR(regs,value) MSTK_SET(regs,hour,value,HOUR) -#define MSTK_SET_REG_DOW(regs,value) MSTK_SET(regs,dow,value,DOW) -#define MSTK_SET_REG_DOM(regs,value) MSTK_SET(regs,dom,value,DOM) -#define MSTK_SET_REG_MONTH(regs,value) MSTK_SET(regs,month,value,MONTH) -#define MSTK_SET_REG_YEAR(regs,value) MSTK_SET(regs,year,value,YEAR) - - -/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the - * same (basically) layout of the 48t02 chip except for the extra - * NVRAM on board (8 KB against the 48t02's 2 KB). - */ -struct mostek48t08 { - char offset[6*1024]; /* Magic things may be here, who knows? */ - struct mostek48t02 regs; /* Here is what we are interested in. */ -}; - -#ifdef CONFIG_SUN4 -enum sparc_clock_type { MSTK48T02, MSTK48T08, \ -INTERSIL, MSTK_INVALID }; -#else -enum sparc_clock_type { MSTK48T02, MSTK48T08, \ -MSTK_INVALID }; -#endif - -#ifdef CONFIG_SUN4 -/* intersil on a sun 4/260 code data from harris doc */ -struct intersil_dt { - volatile unsigned char int_csec; - volatile unsigned char int_hour; - volatile unsigned char int_min; - volatile unsigned char int_sec; - volatile unsigned char int_month; - volatile unsigned char int_day; - volatile unsigned char int_year; - volatile unsigned char int_dow; -}; - -struct intersil { - struct intersil_dt clk; - struct intersil_dt cmp; - volatile unsigned char int_intr_reg; - volatile unsigned char int_cmd_reg; -}; - -#define INTERSIL_STOP 0x0 -#define INTERSIL_START 0x8 -#define INTERSIL_INTR_DISABLE 0x0 -#define INTERSIL_INTR_ENABLE 0x10 -#define INTERSIL_32K 0x0 -#define INTERSIL_NORMAL 0x0 -#define INTERSIL_24H 0x4 -#define INTERSIL_INT_100HZ 0x2 - -/* end of intersil info */ -#endif - -#endif /* !(_SPARC_MOSTEK_H) */ diff --git a/arch/sparc/include/asm/mostek_64.h b/arch/sparc/include/asm/mostek_64.h deleted file mode 100644 index c5652de2ace..00000000000 --- a/arch/sparc/include/asm/mostek_64.h +++ /dev/null @@ -1,143 +0,0 @@ -/* mostek.h: Describes the various Mostek time of day clock registers. - * - * Copyright (C) 1995 David S. Miller (davem@davemloft.net) - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - */ - -#ifndef _SPARC64_MOSTEK_H -#define _SPARC64_MOSTEK_H - -#include <asm/idprom.h> - -/* M48T02 Register Map (adapted from Sun NVRAM/Hostid FAQ) - * - * Data - * Address Function - * Bit 7 Bit 6 Bit 5 Bit 4Bit 3 Bit 2 Bit 1 Bit 0 - * 7ff - - - - - - - - Year 00-99 - * 7fe 0 0 0 - - - - - Month 01-12 - * 7fd 0 0 - - - - - - Date 01-31 - * 7fc 0 FT 0 0 0 - - - Day 01-07 - * 7fb KS 0 - - - - - - Hours 00-23 - * 7fa 0 - - - - - - - Minutes 00-59 - * 7f9 ST - - - - - - - Seconds 00-59 - * 7f8 W R S - - - - - Control - * - * * ST is STOP BIT - * * W is WRITE BIT - * * R is READ BIT - * * S is SIGN BIT - * * FT is FREQ TEST BIT - * * KS is KICK START BIT - */ - -/* The Mostek 48t02 real time clock and NVRAM chip. The registers - * other than the control register are in binary coded decimal. Some - * control bits also live outside the control register. - * - * We now deal with physical addresses for I/O to the chip. -DaveM - */ -static inline u8 mostek_read(void __iomem *addr) -{ - u8 ret; - - __asm__ __volatile__("lduba [%1] %2, %0" - : "=r" (ret) - : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)); - return ret; -} - -static inline void mostek_write(void __iomem *addr, u8 val) -{ - __asm__ __volatile__("stba %0, [%1] %2" - : /* no outputs */ - : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)); -} - -#define MOSTEK_EEPROM 0x0000UL -#define MOSTEK_IDPROM 0x07d8UL -#define MOSTEK_CREG 0x07f8UL -#define MOSTEK_SEC 0x07f9UL -#define MOSTEK_MIN 0x07faUL -#define MOSTEK_HOUR 0x07fbUL -#define MOSTEK_DOW 0x07fcUL -#define MOSTEK_DOM 0x07fdUL -#define MOSTEK_MONTH 0x07feUL -#define MOSTEK_YEAR 0x07ffUL - -extern spinlock_t mostek_lock; -extern void __iomem *mstk48t02_regs; - -/* Control register values. */ -#define MSTK_CREG_WRITE 0x80 /* Must set this before placing values. */ -#define MSTK_CREG_READ 0x40 /* Stop updates to allow a clean read. */ -#define MSTK_CREG_SIGN 0x20 /* Slow/speed clock in calibration mode. */ - -/* Control bits that live in the other registers. */ -#define MSTK_STOP 0x80 /* Stop the clock oscillator. (sec) */ -#define MSTK_KICK_START 0x80 /* Kick start the clock chip. (hour) */ -#define MSTK_FREQ_TEST 0x40 /* Frequency test mode. (day) */ - -#define MSTK_YEAR_ZERO 1968 /* If year reg has zero, it is 1968. */ -#define MSTK_CVT_YEAR(yr) ((yr) + MSTK_YEAR_ZERO) - -/* Masks that define how much space each value takes up. */ -#define MSTK_SEC_MASK 0x7f -#define MSTK_MIN_MASK 0x7f -#define MSTK_HOUR_MASK 0x3f -#define MSTK_DOW_MASK 0x07 -#define MSTK_DOM_MASK 0x3f -#define MSTK_MONTH_MASK 0x1f -#define MSTK_YEAR_MASK 0xffU - -/* Binary coded decimal conversion macros. */ -#define MSTK_REGVAL_TO_DECIMAL(x) (((x) & 0x0F) + 0x0A * ((x) >> 0x04)) -#define MSTK_DECIMAL_TO_REGVAL(x) ((((x) / 0x0A) << 0x04) + ((x) % 0x0A)) - -/* Generic register set and get macros for internal use. */ -#define MSTK_GET(regs,name) \ - (MSTK_REGVAL_TO_DECIMAL(mostek_read(regs + MOSTEK_ ## name) & MSTK_ ## name ## _MASK)) -#define MSTK_SET(regs,name,value) \ -do { u8 __val = mostek_read(regs + MOSTEK_ ## name); \ - __val &= ~(MSTK_ ## name ## _MASK); \ - __val |= (MSTK_DECIMAL_TO_REGVAL(value) & \ - (MSTK_ ## name ## _MASK)); \ - mostek_write(regs + MOSTEK_ ## name, __val); \ -} while(0) - -/* Macros to make register access easier on our fingers. These give you - * the decimal value of the register requested if applicable. You pass - * the a pointer to a 'struct mostek48t02'. - */ -#define MSTK_REG_CREG(regs) (mostek_read((regs) + MOSTEK_CREG)) -#define MSTK_REG_SEC(regs) MSTK_GET(regs,SEC) -#define MSTK_REG_MIN(regs) MSTK_GET(regs,MIN) -#define MSTK_REG_HOUR(regs) MSTK_GET(regs,HOUR) -#define MSTK_REG_DOW(regs) MSTK_GET(regs,DOW) -#define MSTK_REG_DOM(regs) MSTK_GET(regs,DOM) -#define MSTK_REG_MONTH(regs) MSTK_GET(regs,MONTH) -#define MSTK_REG_YEAR(regs) MSTK_GET(regs,YEAR) - -#define MSTK_SET_REG_SEC(regs,value) MSTK_SET(regs,SEC,value) -#define MSTK_SET_REG_MIN(regs,value) MSTK_SET(regs,MIN,value) -#define MSTK_SET_REG_HOUR(regs,value) MSTK_SET(regs,HOUR,value) -#define MSTK_SET_REG_DOW(regs,value) MSTK_SET(regs,DOW,value) -#define MSTK_SET_REG_DOM(regs,value) MSTK_SET(regs,DOM,value) -#define MSTK_SET_REG_MONTH(regs,value) MSTK_SET(regs,MONTH,value) -#define MSTK_SET_REG_YEAR(regs,value) MSTK_SET(regs,YEAR,value) - - -/* The Mostek 48t08 clock chip. Found on Sun4m's I think. It has the - * same (basically) layout of the 48t02 chip except for the extra - * NVRAM on board (8 KB against the 48t02's 2 KB). - */ -#define MOSTEK_48T08_OFFSET 0x0000UL /* Lower NVRAM portions */ -#define MOSTEK_48T08_48T02 0x1800UL /* Offset to 48T02 chip */ - -/* SUN5 systems usually have 48t59 model clock chipsets. But we keep the older - * clock chip definitions around just in case. - */ -#define MOSTEK_48T59_OFFSET 0x0000UL /* Lower NVRAM portions */ -#define MOSTEK_48T59_48T02 0x1800UL /* Offset to 48T02 chip */ - -#endif /* !(_SPARC64_MOSTEK_H) */ diff --git a/arch/sparc/include/asm/obio.h b/arch/sparc/include/asm/obio.h index 1a7544ceb57..4ade0c8a2c7 100644 --- a/arch/sparc/include/asm/obio.h +++ b/arch/sparc/include/asm/obio.h @@ -155,17 +155,6 @@ static inline void bw_set_ctrl(int cpu, unsigned ctrl) "i" (ASI_M_CTL)); } -extern unsigned char cpu_leds[32]; - -static inline void show_leds(int cpuid) -{ - cpuid &= 0x1e; - __asm__ __volatile__ ("stba %0, [%1] %2" : : - "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]), - "r" (ECSR_BASE(cpuid) | BB_LEDS), - "i" (ASI_M_CTL)); -} - static inline unsigned cc_get_ipen(void) { unsigned pending; diff --git a/arch/sparc/include/asm/of_device.h b/arch/sparc/include/asm/of_device.h index bba777a416d..a5d9811f969 100644 --- a/arch/sparc/include/asm/of_device.h +++ b/arch/sparc/include/asm/of_device.h @@ -30,6 +30,8 @@ struct of_device extern void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name); extern void of_iounmap(struct resource *res, void __iomem *base, unsigned long size); +extern void of_propagate_archdata(struct of_device *bus); + /* This is just here during the transition */ #include <linux/of_platform.h> diff --git a/arch/sparc/include/asm/of_platform.h b/arch/sparc/include/asm/of_platform.h index 2348ab90a57..90da99059f8 100644 --- a/arch/sparc/include/asm/of_platform.h +++ b/arch/sparc/include/asm/of_platform.h @@ -13,9 +13,6 @@ * */ -extern struct bus_type ebus_bus_type; -extern struct bus_type sbus_bus_type; - #define of_bus_type of_platform_bus_type /* for compatibility */ #endif diff --git a/arch/sparc/include/asm/oplib_32.h b/arch/sparc/include/asm/oplib_32.h index b2631da259e..699da05235c 100644 --- a/arch/sparc/include/asm/oplib_32.h +++ b/arch/sparc/include/asm/oplib_32.h @@ -21,7 +21,6 @@ enum prom_major_version { PROM_V2, /* sun4c and early sun4m V2 prom */ PROM_V3, /* sun4m and later, up to sun4d/sun4e machines V3 */ PROM_P1275, /* IEEE compliant ISA based Sun PROM, only sun4u */ - PROM_SUN4, /* Old sun4 proms are totally different, but we'll shoehorn it to make it fit */ }; extern enum prom_major_version prom_vers; diff --git a/arch/sparc/include/asm/page_32.h b/arch/sparc/include/asm/page_32.h index cf5fb70ca1c..d1806edc095 100644 --- a/arch/sparc/include/asm/page_32.h +++ b/arch/sparc/include/asm/page_32.h @@ -8,11 +8,8 @@ #ifndef _SPARC_PAGE_H #define _SPARC_PAGE_H -#ifdef CONFIG_SUN4 -#define PAGE_SHIFT 13 -#else #define PAGE_SHIFT 12 -#endif + #ifndef __ASSEMBLY__ /* I have my suspicions... -DaveM */ #define PAGE_SIZE (1UL << PAGE_SHIFT) diff --git a/arch/sparc/include/asm/page_64.h b/arch/sparc/include/asm/page_64.h index b579b910ef5..4274ed13ddb 100644 --- a/arch/sparc/include/asm/page_64.h +++ b/arch/sparc/include/asm/page_64.h @@ -38,6 +38,8 @@ #ifndef __ASSEMBLY__ +#define WANT_PAGE_VIRTUAL + extern void _clear_page(void *page); #define clear_page(X) _clear_page((void *)(X)) struct page; diff --git a/arch/sparc/include/asm/parport.h b/arch/sparc/include/asm/parport.h index d9830621c90..dff3f0253aa 100644 --- a/arch/sparc/include/asm/parport.h +++ b/arch/sparc/include/asm/parport.h @@ -8,7 +8,7 @@ #include <linux/of_device.h> -#include <asm/ebus.h> +#include <asm/ebus_dma.h> #include <asm/ns87303.h> #include <asm/prom.h> @@ -215,7 +215,7 @@ static int __devexit ecpp_remove(struct of_device *op) return 0; } -static struct of_device_id ecpp_match[] = { +static const struct of_device_id ecpp_match[] = { { .name = "ecpp", }, diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h index 0ee949d220c..b41c4c19815 100644 --- a/arch/sparc/include/asm/pci_32.h +++ b/arch/sparc/include/asm/pci_32.h @@ -3,6 +3,8 @@ #ifdef __KERNEL__ +#include <linux/dma-mapping.h> + /* Can be used to override the logic in pci_scan_bus for skipping * already-configured bus numbers - to be used for buggy BIOSes * or architectures with incomplete PCI setup by the loader. diff --git a/arch/sparc/include/asm/pgtable_32.h b/arch/sparc/include/asm/pgtable_32.h index 08237fda887..e0cabe790ec 100644 --- a/arch/sparc/include/asm/pgtable_32.h +++ b/arch/sparc/include/asm/pgtable_32.h @@ -14,11 +14,7 @@ #include <linux/spinlock.h> #include <linux/swap.h> #include <asm/types.h> -#ifdef CONFIG_SUN4 -#include <asm/pgtsun4.h> -#else #include <asm/pgtsun4c.h> -#endif #include <asm/pgtsrmmu.h> #include <asm/vac-ops.h> #include <asm/oplib.h> diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h index bb9ec2cce35..b049abf9902 100644 --- a/arch/sparc/include/asm/pgtable_64.h +++ b/arch/sparc/include/asm/pgtable_64.h @@ -770,6 +770,8 @@ extern void sun4v_patch_tlb_handlers(void); extern unsigned long cmdline_memory_size; +extern asmlinkage void do_sparc64_fault(struct pt_regs *regs); + #endif /* !(__ASSEMBLY__) */ #endif /* !(_SPARC64_PGTABLE_H) */ diff --git a/arch/sparc/include/asm/prom.h b/arch/sparc/include/asm/prom.h index fd55522481c..900d44714f8 100644 --- a/arch/sparc/include/asm/prom.h +++ b/arch/sparc/include/asm/prom.h @@ -18,6 +18,7 @@ */ #include <linux/types.h> #include <linux/proc_fs.h> +#include <linux/mutex.h> #include <asm/atomic.h> #define OF_ROOT_NODE_ADDR_CELLS_DEFAULT 2 @@ -73,6 +74,7 @@ struct of_irq_controller { extern struct device_node *of_find_node_by_cpuid(int cpuid); extern int of_set_property(struct device_node *node, const char *name, void *val, int len); +extern struct mutex of_set_property_mutex; extern int of_getintprop_default(struct device_node *np, const char *name, int def); @@ -94,6 +96,16 @@ static inline void of_node_put(struct device_node *node) { } +/* These routines are here to provide compatibility with how powerpc + * handles IRQ mapping for OF device nodes. We precompute and permanently + * register them in the of_device objects, whereas powerpc computes them + * on request. + */ +extern unsigned int irq_of_parse_and_map(struct device_node *node, int index); +static inline void irq_dispose_mapping(unsigned int virq) +{ +} + /* * NB: This is here while we transition from using asm/prom.h * to linux/of.h diff --git a/arch/sparc/include/asm/ptrace_64.h b/arch/sparc/include/asm/ptrace_64.h index 06e4914c13f..3d3e9c161d8 100644 --- a/arch/sparc/include/asm/ptrace_64.h +++ b/arch/sparc/include/asm/ptrace_64.h @@ -113,6 +113,8 @@ struct sparc_trapf { #ifdef __KERNEL__ +#include <linux/threads.h> + static inline int pt_regs_trap_type(struct pt_regs *regs) { return regs->magic & 0x1ff; @@ -138,6 +140,7 @@ struct global_reg_snapshot { struct thread_info *thread; unsigned long pad1; }; +extern struct global_reg_snapshot global_reg_snapshot[NR_CPUS]; #define __ARCH_WANT_COMPAT_SYS_PTRACE diff --git a/arch/sparc/include/asm/reboot.h b/arch/sparc/include/asm/reboot.h deleted file mode 100644 index 3f3f43f5be5..00000000000 --- a/arch/sparc/include/asm/reboot.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SPARC64_REBOOT_H -#define _SPARC64_REBOOT_H - -extern void machine_alt_power_off(void); - -#endif /* _SPARC64_REBOOT_H */ diff --git a/arch/sparc/include/asm/rtc.h b/arch/sparc/include/asm/rtc.h deleted file mode 100644 index f9ecb1fe2ec..00000000000 --- a/arch/sparc/include/asm/rtc.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * rtc.h: Definitions for access to the Mostek real time clock - * - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - */ - -#ifndef _RTC_H -#define _RTC_H - -#include <linux/ioctl.h> - -struct rtc_time -{ - int sec; /* Seconds (0-59) */ - int min; /* Minutes (0-59) */ - int hour; /* Hour (0-23) */ - int dow; /* Day of the week (1-7) */ - int dom; /* Day of the month (1-31) */ - int month; /* Month of year (1-12) */ - int year; /* Year (0-99) */ -}; - -#define RTCGET _IOR('p', 20, struct rtc_time) -#define RTCSET _IOW('p', 21, struct rtc_time) - -#endif diff --git a/arch/sparc/include/asm/sbus.h b/arch/sparc/include/asm/sbus.h deleted file mode 100644 index f82481ab44d..00000000000 --- a/arch/sparc/include/asm/sbus.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ___ASM_SPARC_SBUS_H -#define ___ASM_SPARC_SBUS_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/sbus_64.h> -#else -#include <asm/sbus_32.h> -#endif -#endif diff --git a/arch/sparc/include/asm/sbus_32.h b/arch/sparc/include/asm/sbus_32.h deleted file mode 100644 index a7b4fa21931..00000000000 --- a/arch/sparc/include/asm/sbus_32.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * sbus.h: Defines for the Sun SBus. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ - -#ifndef _SPARC_SBUS_H -#define _SPARC_SBUS_H - -#include <linux/dma-mapping.h> -#include <linux/ioport.h> -#include <linux/of_device.h> - -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/scatterlist.h> - -/* We scan which devices are on the SBus using the PROM node device - * tree. SBus devices are described in two different ways. You can - * either get an absolute address at which to access the device, or - * you can get a SBus 'slot' number and an offset within that slot. - */ - -/* The base address at which to calculate device OBIO addresses. */ -#define SUN_SBUS_BVADDR 0xf8000000 -#define SBUS_OFF_MASK 0x01ffffff - -/* These routines are used to calculate device address from slot - * numbers + offsets, and vice versa. - */ - -static inline unsigned long sbus_devaddr(int slotnum, unsigned long offset) -{ - return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<25)+(offset)); -} - -static inline int sbus_dev_slot(unsigned long dev_addr) -{ - return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>25); -} - -struct sbus_bus; - -/* Linux SBUS device tables */ -struct sbus_dev { - struct of_device ofdev; - struct sbus_bus *bus; - struct sbus_dev *next; - struct sbus_dev *child; - struct sbus_dev *parent; - int prom_node; - char prom_name[64]; - int slot; - - struct resource resource[PROMREG_MAX]; - - struct linux_prom_registers reg_addrs[PROMREG_MAX]; - int num_registers; - - struct linux_prom_ranges device_ranges[PROMREG_MAX]; - int num_device_ranges; - - unsigned int irqs[4]; - int num_irqs; -}; -#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev) - -/* This struct describes the SBus(s) found on this machine. */ -struct sbus_bus { - struct of_device ofdev; - struct sbus_dev *devices; /* Link to devices on this SBus */ - struct sbus_bus *next; /* next SBus, if more than one SBus */ - int prom_node; /* PROM device tree node for this SBus */ - char prom_name[64]; /* Usually "sbus" or "sbi" */ - int clock_freq; - - struct linux_prom_ranges sbus_ranges[PROMREG_MAX]; - int num_sbus_ranges; - - int devid; - int board; -}; -#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev) - -extern struct sbus_bus *sbus_root; - -static inline int -sbus_is_slave(struct sbus_dev *dev) -{ - /* XXX Have to write this for sun4c's */ - return 0; -} - -/* Device probing routines could find these handy */ -#define for_each_sbus(bus) \ - for((bus) = sbus_root; (bus); (bus)=(bus)->next) - -#define for_each_sbusdev(device, bus) \ - for((device) = (bus)->devices; (device); (device)=(device)->next) - -#define for_all_sbusdev(device, bus) \ - for ((bus) = sbus_root; (bus); (bus) = (bus)->next) \ - for ((device) = (bus)->devices; (device); (device) = (device)->next) - -/* Driver DVMA interfaces. */ -#define sbus_can_dma_64bit(sdev) (0) /* actually, sparc_cpu_model==sun4d */ -#define sbus_can_burst64(sdev) (0) /* actually, sparc_cpu_model==sun4d */ -extern void sbus_set_sbus64(struct sbus_dev *, int); -extern void sbus_fill_device_irq(struct sbus_dev *); - -/* These yield IOMMU mappings in consistent mode. */ -extern void *sbus_alloc_consistent(struct sbus_dev *, long, u32 *dma_addrp); -extern void sbus_free_consistent(struct sbus_dev *, long, void *, u32); -void prom_adjust_ranges(struct linux_prom_ranges *, int, - struct linux_prom_ranges *, int); - -#define SBUS_DMA_BIDIRECTIONAL DMA_BIDIRECTIONAL -#define SBUS_DMA_TODEVICE DMA_TO_DEVICE -#define SBUS_DMA_FROMDEVICE DMA_FROM_DEVICE -#define SBUS_DMA_NONE DMA_NONE - -/* All the rest use streaming mode mappings. */ -extern dma_addr_t sbus_map_single(struct sbus_dev *, void *, size_t, int); -extern void sbus_unmap_single(struct sbus_dev *, dma_addr_t, size_t, int); -extern int sbus_map_sg(struct sbus_dev *, struct scatterlist *, int, int); -extern void sbus_unmap_sg(struct sbus_dev *, struct scatterlist *, int, int); - -/* Finally, allow explicit synchronization of streamable mappings. */ -extern void sbus_dma_sync_single_for_cpu(struct sbus_dev *, dma_addr_t, size_t, int); -#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu -extern void sbus_dma_sync_single_for_device(struct sbus_dev *, dma_addr_t, size_t, int); -extern void sbus_dma_sync_sg_for_cpu(struct sbus_dev *, struct scatterlist *, int, int); -#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu -extern void sbus_dma_sync_sg_for_device(struct sbus_dev *, struct scatterlist *, int, int); - -/* Eric Brower (ebrower@usa.net) - * Translate SBus interrupt levels to ino values-- - * this is used when converting sbus "interrupts" OBP - * node values to "intr" node values, and is platform - * dependent. If only we could call OBP with - * "sbus-intr>cpu (sbint -- ino)" from kernel... - * See .../drivers/sbus/sbus.c for details. - */ -BTFIXUPDEF_CALL(unsigned int, sbint_to_irq, struct sbus_dev *sdev, unsigned int) -#define sbint_to_irq(sdev, sbint) BTFIXUP_CALL(sbint_to_irq)(sdev, sbint) - -extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *); -extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *); -extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *); -extern int sbus_arch_preinit(void); -extern void sbus_arch_postinit(void); - -#endif /* !(_SPARC_SBUS_H) */ diff --git a/arch/sparc/include/asm/sbus_64.h b/arch/sparc/include/asm/sbus_64.h deleted file mode 100644 index b606c14343f..00000000000 --- a/arch/sparc/include/asm/sbus_64.h +++ /dev/null @@ -1,190 +0,0 @@ -/* sbus.h: Defines for the Sun SBus. - * - * Copyright (C) 1996, 1999, 2007 David S. Miller (davem@davemloft.net) - */ - -#ifndef _SPARC64_SBUS_H -#define _SPARC64_SBUS_H - -#include <linux/dma-mapping.h> -#include <linux/ioport.h> -#include <linux/of_device.h> - -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/iommu.h> -#include <asm/scatterlist.h> - -/* We scan which devices are on the SBus using the PROM node device - * tree. SBus devices are described in two different ways. You can - * either get an absolute address at which to access the device, or - * you can get a SBus 'slot' number and an offset within that slot. - */ - -/* The base address at which to calculate device OBIO addresses. */ -#define SUN_SBUS_BVADDR 0x00000000 -#define SBUS_OFF_MASK 0x0fffffff - -/* These routines are used to calculate device address from slot - * numbers + offsets, and vice versa. - */ - -static inline unsigned long sbus_devaddr(int slotnum, unsigned long offset) -{ - return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<28)+(offset)); -} - -static inline int sbus_dev_slot(unsigned long dev_addr) -{ - return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>28); -} - -struct sbus_bus; - -/* Linux SBUS device tables */ -struct sbus_dev { - struct of_device ofdev; - struct sbus_bus *bus; - struct sbus_dev *next; - struct sbus_dev *child; - struct sbus_dev *parent; - int prom_node; - char prom_name[64]; - int slot; - - struct resource resource[PROMREG_MAX]; - - struct linux_prom_registers reg_addrs[PROMREG_MAX]; - int num_registers; - - struct linux_prom_ranges device_ranges[PROMREG_MAX]; - int num_device_ranges; - - unsigned int irqs[4]; - int num_irqs; -}; -#define to_sbus_device(d) container_of(d, struct sbus_dev, ofdev.dev) - -/* This struct describes the SBus(s) found on this machine. */ -struct sbus_bus { - struct of_device ofdev; - struct sbus_dev *devices; /* Tree of SBUS devices */ - struct sbus_bus *next; /* Next SBUS in system */ - int prom_node; /* OBP node of SBUS */ - char prom_name[64]; /* Usually "sbus" or "sbi" */ - int clock_freq; - - struct linux_prom_ranges sbus_ranges[PROMREG_MAX]; - int num_sbus_ranges; - - int portid; -}; -#define to_sbus(d) container_of(d, struct sbus_bus, ofdev.dev) - -extern struct sbus_bus *sbus_root; - -/* Device probing routines could find these handy */ -#define for_each_sbus(bus) \ - for((bus) = sbus_root; (bus); (bus)=(bus)->next) - -#define for_each_sbusdev(device, bus) \ - for((device) = (bus)->devices; (device); (device)=(device)->next) - -#define for_all_sbusdev(device, bus) \ - for ((bus) = sbus_root; (bus); (bus) = (bus)->next) \ - for ((device) = (bus)->devices; (device); (device) = (device)->next) - -/* Driver DVMA interfaces. */ -#define sbus_can_dma_64bit(sdev) (1) -#define sbus_can_burst64(sdev) (1) -extern void sbus_set_sbus64(struct sbus_dev *, int); -extern void sbus_fill_device_irq(struct sbus_dev *); - -static inline void *sbus_alloc_consistent(struct sbus_dev *sdev , size_t size, - dma_addr_t *dma_handle) -{ - return dma_alloc_coherent(&sdev->ofdev.dev, size, - dma_handle, GFP_ATOMIC); -} - -static inline void sbus_free_consistent(struct sbus_dev *sdev, size_t size, - void *vaddr, dma_addr_t dma_handle) -{ - return dma_free_coherent(&sdev->ofdev.dev, size, vaddr, dma_handle); -} - -#define SBUS_DMA_BIDIRECTIONAL DMA_BIDIRECTIONAL -#define SBUS_DMA_TODEVICE DMA_TO_DEVICE -#define SBUS_DMA_FROMDEVICE DMA_FROM_DEVICE -#define SBUS_DMA_NONE DMA_NONE - -/* All the rest use streaming mode mappings. */ -static inline dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *ptr, - size_t size, int direction) -{ - return dma_map_single(&sdev->ofdev.dev, ptr, size, - (enum dma_data_direction) direction); -} - -static inline void sbus_unmap_single(struct sbus_dev *sdev, - dma_addr_t dma_addr, size_t size, - int direction) -{ - dma_unmap_single(&sdev->ofdev.dev, dma_addr, size, - (enum dma_data_direction) direction); -} - -static inline int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, - int nents, int direction) -{ - return dma_map_sg(&sdev->ofdev.dev, sg, nents, - (enum dma_data_direction) direction); -} - -static inline void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, - int nents, int direction) -{ - dma_unmap_sg(&sdev->ofdev.dev, sg, nents, - (enum dma_data_direction) direction); -} - -/* Finally, allow explicit synchronization of streamable mappings. */ -static inline void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - dma_sync_single_for_cpu(&sdev->ofdev.dev, dma_handle, size, - (enum dma_data_direction) direction); -} -#define sbus_dma_sync_single sbus_dma_sync_single_for_cpu - -static inline void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, - dma_addr_t dma_handle, - size_t size, int direction) -{ - /* No flushing needed to sync cpu writes to the device. */ -} - -static inline void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, - struct scatterlist *sg, - int nents, int direction) -{ - dma_sync_sg_for_cpu(&sdev->ofdev.dev, sg, nents, - (enum dma_data_direction) direction); -} -#define sbus_dma_sync_sg sbus_dma_sync_sg_for_cpu - -static inline void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, - struct scatterlist *sg, - int nents, int direction) -{ - /* No flushing needed to sync cpu writes to the device. */ -} - -extern void sbus_arch_bus_ranges_init(struct device_node *, struct sbus_bus *); -extern void sbus_setup_iommu(struct sbus_bus *, struct device_node *); -extern void sbus_setup_arch_props(struct sbus_bus *, struct device_node *); -extern int sbus_arch_preinit(void); -extern void sbus_arch_postinit(void); - -#endif /* !(_SPARC64_SBUS_H) */ diff --git a/arch/sparc/include/asm/serial.h b/arch/sparc/include/asm/serial.h new file mode 100644 index 00000000000..f90d61c2805 --- /dev/null +++ b/arch/sparc/include/asm/serial.h @@ -0,0 +1,6 @@ +#ifndef __SPARC_SERIAL_H +#define __SPARC_SERIAL_H + +#define BASE_BAUD ( 1843200 / 16 ) + +#endif /* __SPARC_SERIAL_H */ diff --git a/arch/sparc/include/asm/spinlock_32.h b/arch/sparc/include/asm/spinlock_32.h index de2249b267c..bf2d532593e 100644 --- a/arch/sparc/include/asm/spinlock_32.h +++ b/arch/sparc/include/asm/spinlock_32.h @@ -6,8 +6,6 @@ #ifndef __SPARC_SPINLOCK_H #define __SPARC_SPINLOCK_H -#include <linux/threads.h> /* For NR_CPUS */ - #ifndef __ASSEMBLY__ #include <asm/psr.h> diff --git a/arch/sparc/include/asm/spinlock_64.h b/arch/sparc/include/asm/spinlock_64.h index 0006fe9f8c7..120cfe4577c 100644 --- a/arch/sparc/include/asm/spinlock_64.h +++ b/arch/sparc/include/asm/spinlock_64.h @@ -6,8 +6,6 @@ #ifndef __SPARC64_SPINLOCK_H #define __SPARC64_SPINLOCK_H -#include <linux/threads.h> /* For NR_CPUS */ - #ifndef __ASSEMBLY__ /* To get debugging spinlocks which detect and catch diff --git a/arch/sparc/include/asm/sstate.h b/arch/sparc/include/asm/sstate.h deleted file mode 100644 index a7c35dbcb28..00000000000 --- a/arch/sparc/include/asm/sstate.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _SPARC64_SSTATE_H -#define _SPARC64_SSTATE_H - -extern void sstate_booting(void); -extern void sstate_running(void); -extern void sstate_halt(void); -extern void sstate_poweroff(void); -extern void sstate_panic(void); -extern void sstate_reboot(void); - -extern void sun4v_sstate_init(void); - -#endif /* _SPARC64_SSTATE_H */ diff --git a/arch/sparc/include/asm/starfire.h b/arch/sparc/include/asm/starfire.h index 07bafd31e33..d56ce60a599 100644 --- a/arch/sparc/include/asm/starfire.h +++ b/arch/sparc/include/asm/starfire.h @@ -12,7 +12,6 @@ extern int this_is_starfire; extern void check_if_starfire(void); -extern void starfire_cpu_setup(void); extern int starfire_hard_smp_processor_id(void); extern void starfire_hookup(int); extern unsigned int starfire_translate(unsigned long imap, unsigned int upaid); diff --git a/arch/sparc/include/asm/statfs.h b/arch/sparc/include/asm/statfs.h index 5e937a73743..55e607ad461 100644 --- a/arch/sparc/include/asm/statfs.h +++ b/arch/sparc/include/asm/statfs.h @@ -1,8 +1,6 @@ #ifndef ___ASM_SPARC_STATFS_H #define ___ASM_SPARC_STATFS_H -#if defined(__sparc__) && defined(__arch64__) -#include <asm/statfs_64.h> -#else -#include <asm/statfs_32.h> -#endif + +#include <asm-generic/statfs.h> + #endif diff --git a/arch/sparc/include/asm/statfs_32.h b/arch/sparc/include/asm/statfs_32.h deleted file mode 100644 index 304520fa886..00000000000 --- a/arch/sparc/include/asm/statfs_32.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _SPARC_STATFS_H -#define _SPARC_STATFS_H - -#include <asm-generic/statfs.h> - -#endif diff --git a/arch/sparc/include/asm/statfs_64.h b/arch/sparc/include/asm/statfs_64.h deleted file mode 100644 index 79b3c890a5f..00000000000 --- a/arch/sparc/include/asm/statfs_64.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef _SPARC64_STATFS_H -#define _SPARC64_STATFS_H - -#ifndef __KERNEL_STRICT_NAMES - -#include <linux/types.h> - -typedef __kernel_fsid_t fsid_t; - -#endif - -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; - -#endif diff --git a/arch/sparc/include/asm/sun4paddr.h b/arch/sparc/include/asm/sun4paddr.h deleted file mode 100644 index d52985f19f4..00000000000 --- a/arch/sparc/include/asm/sun4paddr.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * sun4paddr.h: Various physical addresses on sun4 machines - * - * Copyright (C) 1997 Anton Blanchard (anton@progsoc.uts.edu.au) - * Copyright (C) 1998 Chris Davis (cdavis@cois.on.ca) - * - * Now supports more sun4's - */ - -#ifndef _SPARC_SUN4PADDR_H -#define _SPARC_SUN4PADDR_H - -#define SUN4_IE_PHYSADDR 0xf5000000 -#define SUN4_UNUSED_PHYSADDR 0 - -/* these work for me */ -#define SUN4_200_MEMREG_PHYSADDR 0xf4000000 -#define SUN4_200_CLOCK_PHYSADDR 0xf3000000 -#define SUN4_200_BWTWO_PHYSADDR 0xfd000000 -#define SUN4_200_ETH_PHYSADDR 0xf6000000 -#define SUN4_200_SI_PHYSADDR 0xff200000 - -/* these were here before */ -#define SUN4_300_MEMREG_PHYSADDR 0xf4000000 -#define SUN4_300_CLOCK_PHYSADDR 0xf2000000 -#define SUN4_300_TIMER_PHYSADDR 0xef000000 -#define SUN4_300_ETH_PHYSADDR 0xf9000000 -#define SUN4_300_BWTWO_PHYSADDR 0xfb400000 -#define SUN4_300_DMA_PHYSADDR 0xfa001000 -#define SUN4_300_ESP_PHYSADDR 0xfa000000 - -/* Are these right? */ -#define SUN4_400_MEMREG_PHYSADDR 0xf4000000 -#define SUN4_400_CLOCK_PHYSADDR 0xf2000000 -#define SUN4_400_TIMER_PHYSADDR 0xef000000 -#define SUN4_400_ETH_PHYSADDR 0xf9000000 -#define SUN4_400_BWTWO_PHYSADDR 0xfb400000 -#define SUN4_400_DMA_PHYSADDR 0xfa001000 -#define SUN4_400_ESP_PHYSADDR 0xfa000000 - -/* - these are the actual values set and used in the code. Unused items set - to SUN_UNUSED_PHYSADDR - */ - -extern int sun4_memreg_physaddr; /* memory register (ecc?) */ -extern int sun4_clock_physaddr; /* system clock */ -extern int sun4_timer_physaddr; /* timer, where applicable */ -extern int sun4_eth_physaddr; /* onboard ethernet (ie/le) */ -extern int sun4_si_physaddr; /* sun3 scsi adapter */ -extern int sun4_bwtwo_physaddr; /* onboard bw2 */ -extern int sun4_dma_physaddr; /* scsi dma */ -extern int sun4_esp_physaddr; /* esp scsi */ -extern int sun4_ie_physaddr; /* interrupt enable */ - -#endif /* !(_SPARC_SUN4PADDR_H) */ diff --git a/arch/sparc/include/asm/sun4prom.h b/arch/sparc/include/asm/sun4prom.h deleted file mode 100644 index 9c8b4cbf629..00000000000 --- a/arch/sparc/include/asm/sun4prom.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * sun4prom.h -- interface to sun4 PROM monitor. We don't use most of this, - * so most of these are just placeholders. - */ - -#ifndef _SUN4PROM_H_ -#define _SUN4PROM_H_ - -/* - * Although this looks similar to an romvec for a OpenProm machine, it is - * actually closer to what was used in the Sun2 and Sun3. - * - * V2 entries exist only in version 2 PROMs and later, V3 in version 3 and later. - * - * Many of the function prototypes are guesses. Some are certainly wrong. - * Use with care. - */ - -typedef struct { - char *initSP; /* Initial system stack ptr */ - void (*startmon)(void); /* Initial PC for hardware */ - int *diagberr; /* Bus err handler for diags */ - struct linux_arguments_v0 **bootParam; /* Info for bootstrapped pgm */ - unsigned int *memorysize; /* Usable memory in bytes */ - unsigned char (*getchar)(void); /* Get char from input device */ - void (*putchar)(char); /* Put char to output device */ - int (*mayget)(void); /* Maybe get char, or -1 */ - int (*mayput)(int); /* Maybe put char, or -1 */ - unsigned char *echo; /* Should getchar echo? */ - unsigned char *insource; /* Input source selector */ - unsigned char *outsink; /* Output sink selector */ - int (*getkey)(void); /* Get next key if one exists */ - void (*initgetkey)(void); /* Initialize get key */ - unsigned int *translation; /* Kbd translation selector */ - unsigned char *keybid; /* Keyboard ID byte */ - int *screen_x; /* V2: Screen x pos (r/o) */ - int *screen_y; /* V2: Screen y pos (r/o) */ - struct keybuf *keybuf; /* Up/down keycode buffer */ - char *monid; /* Monitor version ID */ - void (*fbwritechar)(char); /* Write a character to FB */ - int *fbAddr; /* Address of frame buffer */ - char **font; /* Font table for FB */ - void (*fbwritestr)(char *); /* Write string to FB */ - void (*reboot)(char *); /* e.g. reboot("sd()vmlinux") */ - unsigned char *linebuf; /* The line input buffer */ - unsigned char **lineptr; /* Cur pointer into linebuf */ - int *linesize; /* length of line in linebuf */ - void (*getline)(char *); /* Get line from user */ - unsigned char (*getnextchar)(void); /* Get next char from linebuf */ - unsigned char (*peeknextchar)(void); /* Peek at next char */ - int *fbthere; /* =1 if frame buffer there */ - int (*getnum)(void); /* Grab hex num from line */ - int (*printf)(char *, ...); /* See prom_printf() instead */ - void (*printhex)(int); /* Format N digits in hex */ - unsigned char *leds; /* RAM copy of LED register */ - void (*setLEDs)(unsigned char *); /* Sets LED's and RAM copy */ - void (*NMIaddr)(void *); /* Addr for level 7 vector */ - void (*abortentry)(void); /* Entry for keyboard abort */ - int *nmiclock; /* Counts up in msec */ - int *FBtype; /* Frame buffer type */ - unsigned int romvecversion; /* Version number for this romvec */ - struct globram *globram; /* monitor global variables ??? */ - void * kbdaddr; /* Addr of keyboard in use */ - int *keyrinit; /* ms before kbd repeat */ - unsigned char *keyrtick; /* ms between repetitions */ - unsigned int *memoryavail; /* V1: Main mem usable size */ - long *resetaddr; /* where to jump on a reset */ - long *resetmap; /* pgmap entry for resetaddr */ - void (*exittomon)(void); /* Exit from user program */ - unsigned char **memorybitmap; /* V1: &{0 or &bits} */ - void (*setcxsegmap)(int ctxt, char *va, int pmeg); /* Set seg in any context */ - void (**vector_cmd)(void *); /* V2: Handler for 'v' cmd */ - unsigned long *expectedtrapsig; /* V3: Location of the expected trap signal */ - unsigned long *trapvectorbasetable; /* V3: Address of the trap vector table */ - int unused1; - int unused2; - int unused3; - int unused4; -} linux_sun4_romvec; - -extern linux_sun4_romvec *sun4_romvec; - -#endif /* _SUN4PROM_H_ */ diff --git a/arch/sparc/include/asm/system_32.h b/arch/sparc/include/asm/system_32.h index b4b024445fc..8623fc48fe2 100644 --- a/arch/sparc/include/asm/system_32.h +++ b/arch/sparc/include/asm/system_32.h @@ -34,13 +34,7 @@ enum sparc_cpu { extern enum sparc_cpu sparc_cpu_model; -#ifndef CONFIG_SUN4 -#define ARCH_SUN4C_SUN4 (sparc_cpu_model==sun4c) -#define ARCH_SUN4 0 -#else -#define ARCH_SUN4C_SUN4 1 -#define ARCH_SUN4 1 -#endif +#define ARCH_SUN4C (sparc_cpu_model==sun4c) #define SUN4M_NCPUS 4 /* Architectural limit of sun4m. */ @@ -55,6 +49,7 @@ extern unsigned long empty_zero_page; extern void sun_do_break(void); extern int serial_console; extern int stop_a_enabled; +extern int scons_pwroff; static inline int con_is_present(void) { diff --git a/arch/sparc/include/asm/system_64.h b/arch/sparc/include/asm/system_64.h index db9e742a406..8759f2a1b83 100644 --- a/arch/sparc/include/asm/system_64.h +++ b/arch/sparc/include/asm/system_64.h @@ -26,9 +26,8 @@ enum sparc_cpu { #define sparc_cpu_model sun4u -/* This cannot ever be a sun4c nor sun4 :) That's just history. */ -#define ARCH_SUN4C_SUN4 0 -#define ARCH_SUN4 0 +/* This cannot ever be a sun4c :) That's just history. */ +#define ARCH_SUN4C 0 extern char reboot_command[]; @@ -118,6 +117,7 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \ extern void sun_do_break(void); extern int stop_a_enabled; +extern int scons_pwroff; extern void fault_in_user_windows(void); extern void synchronize_user_stack(void); diff --git a/arch/sparc/include/asm/thread_info_32.h b/arch/sparc/include/asm/thread_info_32.h index cbb892d0dff..29899fd5b1b 100644 --- a/arch/sparc/include/asm/thread_info_32.h +++ b/arch/sparc/include/asm/thread_info_32.h @@ -80,11 +80,7 @@ register struct thread_info *current_thread_info_reg asm("g6"); /* * thread information allocation */ -#if PAGE_SHIFT == 13 -#define THREAD_INFO_ORDER 0 -#else /* PAGE_SHIFT */ #define THREAD_INFO_ORDER 1 -#endif #define __HAVE_ARCH_THREAD_INFO_ALLOCATOR diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h index 361e53898dd..2ec030ef381 100644 --- a/arch/sparc/include/asm/timer_32.h +++ b/arch/sparc/include/asm/timer_32.h @@ -9,96 +9,9 @@ #define _SPARC_TIMER_H #include <asm/system.h> /* For SUN4M_NCPUS */ -#include <asm/sun4paddr.h> #include <asm/btfixup.h> -/* Timer structures. The interrupt timer has two properties which - * are the counter (which is handled in do_timer in sched.c) and the limit. - * This limit is where the timer's counter 'wraps' around. Oddly enough, - * the sun4c timer when it hits the limit wraps back to 1 and not zero - * thus when calculating the value at which it will fire a microsecond you - * must adjust by one. Thanks SUN for designing such great hardware ;( - */ - -/* Note that I am only going to use the timer that interrupts at - * Sparc IRQ 10. There is another one available that can fire at - * IRQ 14. Currently it is left untouched, we keep the PROM's limit - * register value and let the prom take these interrupts. This allows - * L1-A to work. - */ - -struct sun4c_timer_info { - __volatile__ unsigned int cur_count10; - __volatile__ unsigned int timer_limit10; - __volatile__ unsigned int cur_count14; - __volatile__ unsigned int timer_limit14; -}; - -#define SUN4C_TIMER_PHYSADDR 0xf3000000 -#ifdef CONFIG_SUN4 -#define SUN_TIMER_PHYSADDR SUN4_300_TIMER_PHYSADDR -#else -#define SUN_TIMER_PHYSADDR SUN4C_TIMER_PHYSADDR -#endif - -/* A sun4m has two blocks of registers which are probably of the same - * structure. LSI Logic's L64851 is told to _decrement_ from the limit - * value. Aurora behaves similarly but its limit value is compacted in - * other fashion (it's wider). Documented fields are defined here. - */ - -/* As with the interrupt register, we have two classes of timer registers - * which are per-cpu and master. Per-cpu timers only hit that cpu and are - * only level 14 ticks, master timer hits all cpus and is level 10. - */ - -#define SUN4M_PRM_CNT_L 0x80000000 -#define SUN4M_PRM_CNT_LVALUE 0x7FFFFC00 - -struct sun4m_timer_percpu_info { - __volatile__ unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */ - __volatile__ unsigned int l14_cur_count; - - /* This register appears to be write only and/or inaccessible - * on Uni-Processor sun4m machines. - */ - __volatile__ unsigned int l14_limit_noclear; /* Data access error is here */ - - __volatile__ unsigned int cntrl; /* =1 after POST on Aurora */ - __volatile__ unsigned char space[PAGE_SIZE - 16]; -}; - -struct sun4m_timer_regs { - struct sun4m_timer_percpu_info cpu_timers[SUN4M_NCPUS]; - volatile unsigned int l10_timer_limit; - volatile unsigned int l10_cur_count; - - /* Again, this appears to be write only and/or inaccessible - * on uni-processor sun4m machines. - */ - volatile unsigned int l10_limit_noclear; - - /* This register too, it must be magic. */ - volatile unsigned int foobar; - - volatile unsigned int cfg; /* equals zero at boot time... */ -}; - -#define SUN4D_PRM_CNT_L 0x80000000 -#define SUN4D_PRM_CNT_LVALUE 0x7FFFFC00 - -struct sun4d_timer_regs { - volatile unsigned int l10_timer_limit; - volatile unsigned int l10_cur_countx; - volatile unsigned int l10_limit_noclear; - volatile unsigned int ctrl; - volatile unsigned int l10_cur_count; -}; - -extern struct sun4d_timer_regs *sun4d_timers; - extern __volatile__ unsigned int *master_l10_counter; -extern __volatile__ unsigned int *master_l10_limit; /* FIXME: Make do_[gs]ettimeofday btfixup calls */ BTFIXUPDEF_CALL(int, bus_do_settimeofday, struct timespec *tv) diff --git a/arch/sparc/include/asm/vac-ops.h b/arch/sparc/include/asm/vac-ops.h index d10527611f1..a63e88ef042 100644 --- a/arch/sparc/include/asm/vac-ops.h +++ b/arch/sparc/include/asm/vac-ops.h @@ -76,11 +76,7 @@ * cacheable bit in the pte's of all such pages. */ -#ifdef CONFIG_SUN4 -#define S4CVAC_BADBITS 0x0001e000 -#else #define S4CVAC_BADBITS 0x0000f000 -#endif /* The following is true if vaddr1 and vaddr2 would cause * a 'bad alias'. @@ -94,10 +90,7 @@ */ struct sun4c_vac_props { unsigned int num_bytes; /* Size of the cache */ - unsigned int num_lines; /* Number of cache lines */ unsigned int do_hwflushes; /* Hardware flushing available? */ - enum { VAC_NONE, VAC_WRITE_THROUGH, - VAC_WRITE_BACK } type; /* What type of VAC? */ unsigned int linesize; /* Size of each line in bytes */ unsigned int log2lsize; /* log2(linesize) */ unsigned int on; /* VAC is enabled */ diff --git a/arch/sparc/include/asm/vfc_ioctls.h b/arch/sparc/include/asm/vfc_ioctls.h deleted file mode 100644 index af8b69007b2..00000000000 --- a/arch/sparc/include/asm/vfc_ioctls.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 1996 by Manish Vachharajani */ - -#ifndef _LINUX_VFC_IOCTLS_H_ -#define _LINUX_VFC_IOCTLS_H_ - - /* IOCTLs */ -#define VFC_IOCTL(a) (('j' << 8) | a) -#define VFCGCTRL (VFC_IOCTL (0)) /* get vfc attributes */ -#define VFCSCTRL (VFC_IOCTL (1)) /* set vfc attributes */ -#define VFCGVID (VFC_IOCTL (2)) /* get video decoder attributes */ -#define VFCSVID (VFC_IOCTL (3)) /* set video decoder attributes */ -#define VFCHUE (VFC_IOCTL (4)) /* set hue */ -#define VFCPORTCHG (VFC_IOCTL (5)) /* change port */ -#define VFCRDINFO (VFC_IOCTL (6)) /* read info */ - - /* Options for setting the vfc attributes and status */ -#define MEMPRST 0x1 /* reset FIFO ptr. */ -#define CAPTRCMD 0x2 /* start capture and wait */ -#define DIAGMODE 0x3 /* diag mode */ -#define NORMMODE 0x4 /* normal mode */ -#define CAPTRSTR 0x5 /* start capture */ -#define CAPTRWAIT 0x6 /* wait for capture to finish */ - - - /* Options for the decoder */ -#define STD_NTSC 0x1 /* NTSC mode */ -#define STD_PAL 0x2 /* PAL mode */ -#define COLOR_ON 0x3 /* force color ON */ -#define MONO 0x4 /* force color OFF */ - - /* Values returned by ioctl 2 */ - -#define NO_LOCK 1 -#define NTSC_COLOR 2 -#define NTSC_NOCOLOR 3 -#define PAL_COLOR 4 -#define PAL_NOCOLOR 5 - -/* Not too sure what this does yet */ - /* Options for setting Field number */ -#define ODD_FIELD 0x1 -#define EVEN_FIELD 0x0 -#define ACTIVE_ONLY 0x2 -#define NON_ACTIVE 0x0 - -/* Debug options */ -#define VFC_I2C_SEND 0 -#define VFC_I2C_RECV 1 - -struct vfc_debug_inout -{ - unsigned long addr; - unsigned long ret; - unsigned long len; - unsigned char __user *buffer; -}; - -#endif /* _LINUX_VFC_IOCTLS_H_ */ diff --git a/arch/sparc/include/asm/visasm.h b/arch/sparc/include/asm/visasm.h index de797b9bf55..39ca301920d 100644 --- a/arch/sparc/include/asm/visasm.h +++ b/arch/sparc/include/asm/visasm.h @@ -57,6 +57,7 @@ static inline void save_and_clear_fpu(void) { " " : : "i" (FPRS_FEF|FPRS_DU) : "o5", "g1", "g2", "g3", "g7", "cc"); } +extern int vis_emul(struct pt_regs *, unsigned int); #endif #endif /* _SPARC64_ASI_H */ diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 6e03a2a7863..2d658209509 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -13,15 +13,13 @@ obj-y := entry.o wof.o wuf.o etrap.o rtrap.o traps.o $(IRQ_OBJS) \ time.o windows.o cpu.o devices.o \ tadpole.o tick14.o ptrace.o \ unaligned.o una_asm.o muldiv.o \ - prom.o of_device.o devres.o + prom.o of_device.o devres.o dma.o devres-y = ../../../kernel/irq/devres.o obj-$(CONFIG_PCI) += pcic.o -obj-$(CONFIG_SUN4) += sun4setup.o obj-$(CONFIG_SMP) += trampoline.o smp.o sun4m_smp.o sun4d_smp.o obj-$(CONFIG_SUN_AUXIO) += auxio.o -obj-$(CONFIG_PCI) += ebus.o obj-$(CONFIG_SUN_PM) += apc.o pmc.o obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o obj-$(CONFIG_SPARC_LED) += led.o diff --git a/arch/sparc/kernel/apc.c b/arch/sparc/kernel/apc.c index 5267d48fb2c..4dd1ba752ce 100644 --- a/arch/sparc/kernel/apc.c +++ b/arch/sparc/kernel/apc.c @@ -12,9 +12,10 @@ #include <linux/miscdevice.h> #include <linux/smp_lock.h> #include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/auxio.h> @@ -29,11 +30,10 @@ #define APC_OBPNAME "power-management" #define APC_DEVNAME "apc" -volatile static u8 __iomem *regs; -static int apc_regsize; +static u8 __iomem *regs; static int apc_no_idle __initdata = 0; -#define apc_readb(offs) (sbus_readb(regs+offs)) +#define apc_readb(offs) (sbus_readb(regs+offs)) #define apc_writeb(val, offs) (sbus_writeb(val, regs+offs)) /* Specify "apc=noidle" on the kernel command line to @@ -69,9 +69,9 @@ static void apc_swift_idle(void) #endif } -static inline void apc_free(void) +static inline void apc_free(struct of_device *op) { - sbus_iounmap(regs, apc_regsize); + of_iounmap(&op->resource[0], regs, resource_size(&op->resource[0])); } static int apc_open(struct inode *inode, struct file *f) @@ -153,52 +153,56 @@ static const struct file_operations apc_fops = { static struct miscdevice apc_miscdev = { APC_MINOR, APC_DEVNAME, &apc_fops }; -static int __init apc_probe(void) +static int __devinit apc_probe(struct of_device *op, + const struct of_device_id *match) { - struct sbus_bus *sbus = NULL; - struct sbus_dev *sdev = NULL; - int iTmp = 0; - - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, APC_OBPNAME)) { - goto sbus_done; - } - } - } + int err; -sbus_done: - if (!sdev) { - return -ENODEV; - } - - apc_regsize = sdev->reg_addrs[0].reg_size; - regs = sbus_ioremap(&sdev->resource[0], 0, - apc_regsize, APC_OBPNAME); - if(!regs) { + regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), APC_OBPNAME); + if (!regs) { printk(KERN_ERR "%s: unable to map registers\n", APC_DEVNAME); return -ENODEV; } - iTmp = misc_register(&apc_miscdev); - if (iTmp != 0) { + err = misc_register(&apc_miscdev); + if (err) { printk(KERN_ERR "%s: unable to register device\n", APC_DEVNAME); - apc_free(); + apc_free(op); return -ENODEV; } /* Assign power management IDLE handler */ - if(!apc_no_idle) + if (!apc_no_idle) pm_idle = apc_swift_idle; printk(KERN_INFO "%s: power management initialized%s\n", - APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : ""); + APC_DEVNAME, apc_no_idle ? " (CPU idle disabled)" : ""); + return 0; } +static struct of_device_id __initdata apc_match[] = { + { + .name = APC_OBPNAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, apc_match); + +static struct of_platform_driver apc_driver = { + .name = "apc", + .match_table = apc_match, + .probe = apc_probe, +}; + +static int __init apc_init(void) +{ + return of_register_driver(&apc_driver, &of_bus_type); +} + /* This driver is not critical to the boot process * and is easiest to ioremap when SBus is already * initialized, so we install ourselves thusly: */ -__initcall(apc_probe); - +__initcall(apc_init); diff --git a/arch/sparc/kernel/auxio.c b/arch/sparc/kernel/auxio.c index baf4ed3fb0f..09c857215a5 100644 --- a/arch/sparc/kernel/auxio.c +++ b/arch/sparc/kernel/auxio.c @@ -6,6 +6,8 @@ #include <linux/stddef.h> #include <linux/init.h> #include <linux/spinlock.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/oplib.h> #include <asm/io.h> #include <asm/auxio.h> @@ -59,7 +61,7 @@ void __init auxio_probe(void) r.flags = auxregs[0].which_io & 0xF; r.start = auxregs[0].phys_addr; r.end = auxregs[0].phys_addr + auxregs[0].reg_size - 1; - auxio_register = sbus_ioremap(&r, 0, auxregs[0].reg_size, "auxio"); + auxio_register = of_ioremap(&r, 0, auxregs[0].reg_size, "auxio"); /* Fix the address on sun4m and sun4c. */ if((((unsigned long) auxregs[0].phys_addr) & 3) == 3 || sparc_cpu_model == sun4c) @@ -128,7 +130,7 @@ void __init auxio_power_probe(void) r.flags = regs.which_io & 0xF; r.start = regs.phys_addr; r.end = regs.phys_addr + regs.reg_size - 1; - auxio_power_register = (unsigned char *) sbus_ioremap(&r, 0, + auxio_power_register = (unsigned char *) of_ioremap(&r, 0, regs.reg_size, "auxpower"); /* Display a quick message on the console. */ diff --git a/arch/sparc/kernel/devices.c b/arch/sparc/kernel/devices.c index b240b8863fd..ad656b044b8 100644 --- a/arch/sparc/kernel/devices.c +++ b/arch/sparc/kernel/devices.c @@ -143,7 +143,7 @@ void __init device_scan(void) #endif clock_stop_probe(); - if (ARCH_SUN4C_SUN4) + if (ARCH_SUN4C) sun4c_probe_memerr_reg(); return; diff --git a/arch/sparc/kernel/dma.c b/arch/sparc/kernel/dma.c new file mode 100644 index 00000000000..ebc8403b035 --- /dev/null +++ b/arch/sparc/kernel/dma.c @@ -0,0 +1,227 @@ +/* dma.c: PCI and SBUS DMA accessors for 32-bit sparc. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> +#include <linux/mm.h> + +#ifdef CONFIG_PCI +#include <linux/pci.h> +#endif + +#include "dma.h" + +int dma_supported(struct device *dev, u64 mask) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_dma_supported(to_pci_dev(dev), mask); +#endif + return 0; +} +EXPORT_SYMBOL(dma_supported); + +int dma_set_mask(struct device *dev, u64 dma_mask) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_set_dma_mask(to_pci_dev(dev), dma_mask); +#endif + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(dma_set_mask); + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_alloc_consistent(to_pci_dev(dev), size, dma_handle); +#endif + return sbus_alloc_consistent(dev, size, dma_handle); +} +EXPORT_SYMBOL(dma_alloc_coherent); + +void dma_free_coherent(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t dma_handle) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_free_consistent(to_pci_dev(dev), size, + cpu_addr, dma_handle); + return; + } +#endif + sbus_free_consistent(dev, size, cpu_addr, dma_handle); +} +EXPORT_SYMBOL(dma_free_coherent); + +dma_addr_t dma_map_single(struct device *dev, void *cpu_addr, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_map_single(to_pci_dev(dev), cpu_addr, + size, (int)direction); +#endif + return sbus_map_single(dev, cpu_addr, size, (int)direction); +} +EXPORT_SYMBOL(dma_map_single); + +void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, + size_t size, + enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_unmap_single(to_pci_dev(dev), dma_addr, + size, (int)direction); + return; + } +#endif + sbus_unmap_single(dev, dma_addr, size, (int)direction); +} +EXPORT_SYMBOL(dma_unmap_single); + +dma_addr_t dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_map_page(to_pci_dev(dev), page, offset, + size, (int)direction); +#endif + return sbus_map_single(dev, page_address(page) + offset, + size, (int)direction); +} +EXPORT_SYMBOL(dma_map_page); + +void dma_unmap_page(struct device *dev, dma_addr_t dma_address, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_unmap_page(to_pci_dev(dev), dma_address, + size, (int)direction); + return; + } +#endif + sbus_unmap_single(dev, dma_address, size, (int)direction); +} +EXPORT_SYMBOL(dma_unmap_page); + +int dma_map_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) + return pci_map_sg(to_pci_dev(dev), sg, nents, (int)direction); +#endif + return sbus_map_sg(dev, sg, nents, direction); +} +EXPORT_SYMBOL(dma_map_sg); + +void dma_unmap_sg(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_unmap_sg(to_pci_dev(dev), sg, nents, (int)direction); + return; + } +#endif + sbus_unmap_sg(dev, sg, nents, (int)direction); +} +EXPORT_SYMBOL(dma_unmap_sg); + +void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_single_for_cpu(to_pci_dev(dev), dma_handle, + size, (int)direction); + return; + } +#endif + sbus_dma_sync_single_for_cpu(dev, dma_handle, size, (int) direction); +} +EXPORT_SYMBOL(dma_sync_single_for_cpu); + +void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_single_for_device(to_pci_dev(dev), dma_handle, + size, (int)direction); + return; + } +#endif + sbus_dma_sync_single_for_device(dev, dma_handle, size, (int) direction); +} +EXPORT_SYMBOL(dma_sync_single_for_device); + +void dma_sync_single_range_for_cpu(struct device *dev, + dma_addr_t dma_handle, + unsigned long offset, + size_t size, + enum dma_data_direction direction) +{ + dma_sync_single_for_cpu(dev, dma_handle+offset, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_range_for_cpu); + +void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + dma_sync_single_for_device(dev, dma_handle+offset, size, direction); +} +EXPORT_SYMBOL(dma_sync_single_range_for_device); + +void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, + int nelems, enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_sg_for_cpu(to_pci_dev(dev), sg, + nelems, (int)direction); + return; + } +#endif + BUG(); +} +EXPORT_SYMBOL(dma_sync_sg_for_cpu); + +void dma_sync_sg_for_device(struct device *dev, + struct scatterlist *sg, int nelems, + enum dma_data_direction direction) +{ +#ifdef CONFIG_PCI + if (dev->bus == &pci_bus_type) { + pci_dma_sync_sg_for_device(to_pci_dev(dev), sg, + nelems, (int)direction); + return; + } +#endif + BUG(); +} +EXPORT_SYMBOL(dma_sync_sg_for_device); + +int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) +{ + return (dma_addr == DMA_ERROR_CODE); +} +EXPORT_SYMBOL(dma_mapping_error); + +int dma_get_cache_alignment(void) +{ + return 32; +} +EXPORT_SYMBOL(dma_get_cache_alignment); diff --git a/arch/sparc/kernel/dma.h b/arch/sparc/kernel/dma.h new file mode 100644 index 00000000000..f8d8951adb5 --- /dev/null +++ b/arch/sparc/kernel/dma.h @@ -0,0 +1,14 @@ +void *sbus_alloc_consistent(struct device *dev, long len, u32 *dma_addrp); +void sbus_free_consistent(struct device *dev, long n, void *p, u32 ba); +dma_addr_t sbus_map_single(struct device *dev, void *va, + size_t len, int direction); +void sbus_unmap_single(struct device *dev, dma_addr_t ba, + size_t n, int direction); +int sbus_map_sg(struct device *dev, struct scatterlist *sg, + int n, int direction); +void sbus_unmap_sg(struct device *dev, struct scatterlist *sg, + int n, int direction); +void sbus_dma_sync_single_for_cpu(struct device *dev, dma_addr_t ba, + size_t size, int direction); +void sbus_dma_sync_single_for_device(struct device *dev, dma_addr_t ba, + size_t size, int direction); diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c deleted file mode 100644 index 97294232259..00000000000 --- a/arch/sparc/kernel/ebus.c +++ /dev/null @@ -1,393 +0,0 @@ -/* - * ebus.c: PCI to EBus bridge device. - * - * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) - * - * Adopted for sparc by V. Roganov and G. Raiko. - * Fixes for different platforms by Pete Zaitcev. - */ - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/string.h> - -#include <asm/system.h> -#include <asm/page.h> -#include <asm/pbm.h> -#include <asm/ebus.h> -#include <asm/io.h> -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/bpp.h> - -struct linux_ebus *ebus_chain = NULL; - -/* We are together with pcic.c under CONFIG_PCI. */ -extern unsigned int pcic_pin_to_irq(unsigned int, const char *name); - -/* - * IRQ Blacklist - * Here we list PROMs and systems that are known to supply crap as IRQ numbers. - */ -struct ebus_device_irq { - char *name; - unsigned int pin; -}; - -struct ebus_system_entry { - char *esname; - struct ebus_device_irq *ipt; -}; - -static struct ebus_device_irq je1_1[] = { - { "8042", 3 }, - { "SUNW,CS4231", 0 }, - { "parallel", 0 }, - { "se", 2 }, - { NULL, 0 } -}; - -/* - * Gleb's JE1 supplied reasonable pin numbers, but mine did not (OBP 2.32). - * Blacklist the sucker... Note that Gleb's system will work. - */ -static struct ebus_system_entry ebus_blacklist[] = { - { "SUNW,JavaEngine1", je1_1 }, - { NULL, NULL } -}; - -static struct ebus_device_irq *ebus_blackp = NULL; - -/* - */ -static inline unsigned long ebus_alloc(size_t size) -{ - return (unsigned long)kmalloc(size, GFP_ATOMIC); -} - -/* - */ -static int __init ebus_blacklist_irq(const char *name) -{ - struct ebus_device_irq *dp; - - if ((dp = ebus_blackp) != NULL) { - for (; dp->name != NULL; dp++) { - if (strcmp(name, dp->name) == 0) { - return pcic_pin_to_irq(dp->pin, name); - } - } - } - return 0; -} - -static void __init fill_ebus_child(struct device_node *dp, - struct linux_ebus_child *dev) -{ - const int *regs; - const int *irqs; - int i, len; - - dev->prom_node = dp; - regs = of_get_property(dp, "reg", &len); - if (!regs) - len = 0; - dev->num_addrs = len / sizeof(regs[0]); - - for (i = 0; i < dev->num_addrs; i++) { - if (regs[i] >= dev->parent->num_addrs) { - prom_printf("UGH: property for %s was %d, need < %d\n", - dev->prom_node->name, len, - dev->parent->num_addrs); - panic(__func__); - } - - /* XXX resource */ - dev->resource[i].start = - dev->parent->resource[regs[i]].start; - } - - for (i = 0; i < PROMINTR_MAX; i++) - dev->irqs[i] = PCI_IRQ_NONE; - - if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_node->name)) != 0) { - dev->num_irqs = 1; - } else { - irqs = of_get_property(dp, "interrupts", &len); - if (!irqs) { - dev->num_irqs = 0; - dev->irqs[0] = 0; - if (dev->parent->num_irqs != 0) { - dev->num_irqs = 1; - dev->irqs[0] = dev->parent->irqs[0]; - } - } else { - dev->num_irqs = len / sizeof(irqs[0]); - if (irqs[0] == 0 || irqs[0] >= 8) { - /* - * XXX Zero is a valid pin number... - * This works as long as Ebus is not wired - * to INTA#. - */ - printk("EBUS: %s got bad irq %d from PROM\n", - dev->prom_node->name, irqs[0]); - dev->num_irqs = 0; - dev->irqs[0] = 0; - } else { - dev->irqs[0] = - pcic_pin_to_irq(irqs[0], - dev->prom_node->name); - } - } - } -} - -static void __init fill_ebus_device(struct device_node *dp, - struct linux_ebus_device *dev) -{ - const struct linux_prom_registers *regs; - struct linux_ebus_child *child; - struct dev_archdata *sd; - const int *irqs; - int i, n, len; - unsigned long baseaddr; - - dev->prom_node = dp; - - regs = of_get_property(dp, "reg", &len); - if (!regs) - len = 0; - if (len % sizeof(struct linux_prom_registers)) { - prom_printf("UGH: proplen for %s was %d, need multiple of %d\n", - dev->prom_node->name, len, - (int)sizeof(struct linux_prom_registers)); - panic(__func__); - } - dev->num_addrs = len / sizeof(struct linux_prom_registers); - - for (i = 0; i < dev->num_addrs; i++) { - /* - * XXX Collect JE-1 PROM - * - * Example - JS-E with 3.11: - * /ebus - * regs - * 0x00000000, 0x0, 0x00000000, 0x0, 0x00000000, - * 0x82000010, 0x0, 0xf0000000, 0x0, 0x01000000, - * 0x82000014, 0x0, 0x38800000, 0x0, 0x00800000, - * ranges - * 0x00, 0x00000000, 0x02000010, 0x0, 0x0, 0x01000000, - * 0x01, 0x01000000, 0x02000014, 0x0, 0x0, 0x00800000, - * /ebus/8042 - * regs - * 0x00000001, 0x00300060, 0x00000008, - * 0x00000001, 0x00300060, 0x00000008, - */ - n = regs[i].which_io; - if (n >= 4) { - /* XXX This is copied from old JE-1 by Gleb. */ - n = (regs[i].which_io - 0x10) >> 2; - } else { - ; - } - -/* - * XXX Now as we have regions, why don't we make an on-demand allocation... - */ - dev->resource[i].start = 0; - if ((baseaddr = dev->bus->self->resource[n].start + - regs[i].phys_addr) != 0) { - /* dev->resource[i].name = dev->prom_name; */ - if ((baseaddr = (unsigned long) ioremap(baseaddr, - regs[i].reg_size)) == 0) { - panic("ebus: unable to remap dev %s", - dev->prom_node->name); - } - } - dev->resource[i].start = baseaddr; /* XXX Unaligned */ - } - - for (i = 0; i < PROMINTR_MAX; i++) - dev->irqs[i] = PCI_IRQ_NONE; - - if ((dev->irqs[0] = ebus_blacklist_irq(dev->prom_node->name)) != 0) { - dev->num_irqs = 1; - } else { - irqs = of_get_property(dp, "interrupts", &len); - if (!irqs) { - dev->num_irqs = 0; - if ((dev->irqs[0] = dev->bus->self->irq) != 0) { - dev->num_irqs = 1; -/* P3 */ /* printk("EBUS: child %s irq %d from parent\n", dev->prom_name, dev->irqs[0]); */ - } - } else { - dev->num_irqs = 1; /* dev->num_irqs = len / sizeof(irqs[0]); */ - if (irqs[0] == 0 || irqs[0] >= 8) { - /* See above for the parent. XXX */ - printk("EBUS: %s got bad irq %d from PROM\n", - dev->prom_node->name, irqs[0]); - dev->num_irqs = 0; - dev->irqs[0] = 0; - } else { - dev->irqs[0] = - pcic_pin_to_irq(irqs[0], - dev->prom_node->name); - } - } - } - - sd = &dev->ofdev.dev.archdata; - sd->prom_node = dp; - sd->op = &dev->ofdev; - sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu; - - dev->ofdev.node = dp; - dev->ofdev.dev.parent = &dev->bus->ofdev.dev; - dev->ofdev.dev.bus = &ebus_bus_type; - sprintf(dev->ofdev.dev.bus_id, "ebus[%08x]", dp->node); - - /* Register with core */ - if (of_device_register(&dev->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - if ((dp = dp->child) != NULL) { - dev->children = (struct linux_ebus_child *) - ebus_alloc(sizeof(struct linux_ebus_child)); - - child = dev->children; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child); - - while ((dp = dp->sibling) != NULL) { - child->next = (struct linux_ebus_child *) - ebus_alloc(sizeof(struct linux_ebus_child)); - - child = child->next; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child); - } - } -} - -void __init ebus_init(void) -{ - const struct linux_prom_pci_registers *regs; - struct linux_pbm_info *pbm; - struct linux_ebus_device *dev; - struct linux_ebus *ebus; - struct ebus_system_entry *sp; - struct pci_dev *pdev; - struct pcidev_cookie *cookie; - struct device_node *dp; - struct resource *p; - unsigned short pci_command; - int len, reg, nreg; - int num_ebus = 0; - - dp = of_find_node_by_path("/"); - for (sp = ebus_blacklist; sp->esname != NULL; sp++) { - if (strcmp(dp->name, sp->esname) == 0) { - ebus_blackp = sp->ipt; - break; - } - } - - pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_EBUS, NULL); - if (!pdev) - return; - - cookie = pdev->sysdata; - dp = cookie->prom_node; - - ebus_chain = ebus = (struct linux_ebus *) - ebus_alloc(sizeof(struct linux_ebus)); - ebus->next = NULL; - - while (dp) { - struct device_node *nd; - - ebus->prom_node = dp; - ebus->self = pdev; - ebus->parent = pbm = cookie->pbm; - - /* Enable BUS Master. */ - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - pci_command |= PCI_COMMAND_MASTER; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); - - regs = of_get_property(dp, "reg", &len); - if (!regs) { - prom_printf("%s: can't find reg property\n", - __func__); - prom_halt(); - } - nreg = len / sizeof(struct linux_prom_pci_registers); - - p = &ebus->self->resource[0]; - for (reg = 0; reg < nreg; reg++) { - if (!(regs[reg].which_io & 0x03000000)) - continue; - - (p++)->start = regs[reg].phys_lo; - } - - ebus->ofdev.node = dp; - ebus->ofdev.dev.parent = &pdev->dev; - ebus->ofdev.dev.bus = &ebus_bus_type; - sprintf(ebus->ofdev.dev.bus_id, "ebus%d", num_ebus); - - /* Register with core */ - if (of_device_register(&ebus->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - - nd = dp->child; - if (!nd) - goto next_ebus; - - ebus->devices = (struct linux_ebus_device *) - ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = ebus->devices; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(nd, dev); - - while ((nd = nd->sibling) != NULL) { - dev->next = (struct linux_ebus_device *) - ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = dev->next; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(nd, dev); - } - - next_ebus: - pdev = pci_get_device(PCI_VENDOR_ID_SUN, - PCI_DEVICE_ID_SUN_EBUS, pdev); - if (!pdev) - break; - - cookie = pdev->sysdata; - dp = cookie->prom_node; - - ebus->next = (struct linux_ebus *) - ebus_alloc(sizeof(struct linux_ebus)); - ebus = ebus->next; - ebus->next = NULL; - ++num_ebus; - } - if (pdev) - pci_dev_put(pdev); -} diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index e8cdf715a54..faf9ccd9ef5 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S @@ -20,11 +20,7 @@ #include <asm/memreg.h> #include <asm/page.h> #include <asm/pgtable.h> -#ifdef CONFIG_SUN4 -#include <asm/pgtsun4.h> -#else #include <asm/pgtsun4c.h> -#endif #include <asm/winmacro.h> #include <asm/signal.h> #include <asm/obio.h> @@ -276,17 +272,18 @@ smp4m_ticker: */ maybe_smp4m_msg: GET_PROCESSOR4M_ID(o3) - set sun4m_interrupts, %l5 - ld [%l5], %o5 + sethi %hi(sun4m_irq_percpu), %l5 + sll %o3, 2, %o3 + or %l5, %lo(sun4m_irq_percpu), %o5 sethi %hi(0x40000000), %o2 - sll %o3, 12, %o3 ld [%o5 + %o3], %o1 - andcc %o1, %o2, %g0 + ld [%o1 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending + andcc %o3, %o2, %g0 be,a smp4m_ticker cmp %l7, 14 - st %o2, [%o5 + 0x4] + st %o2, [%o1 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x40000000 WRITE_PAUSE - ld [%o5], %g0 + ld [%o1 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending WRITE_PAUSE or %l0, PSR_PIL, %l4 wr %l4, 0x0, %psr @@ -304,16 +301,16 @@ linux_trap_ipi15_sun4m: SAVE_ALL sethi %hi(0x80000000), %o2 GET_PROCESSOR4M_ID(o0) - set sun4m_interrupts, %l5 - ld [%l5], %o5 - sll %o0, 12, %o0 - add %o5, %o0, %o5 - ld [%o5], %o3 + sethi %hi(sun4m_irq_percpu), %l5 + or %l5, %lo(sun4m_irq_percpu), %o5 + sll %o0, 2, %o0 + ld [%o5 + %o0], %o5 + ld [%o5 + 0x00], %o3 ! sun4m_irq_percpu[cpu]->pending andcc %o3, %o2, %g0 be 1f ! Must be an NMI async memory error - st %o2, [%o5 + 4] + st %o2, [%o5 + 0x04] ! sun4m_irq_percpu[cpu]->clear=0x80000000 WRITE_PAUSE - ld [%o5], %g0 + ld [%o5 + 0x00], %g0 ! sun4m_irq_percpu[cpu]->pending WRITE_PAUSE or %l0, PSR_PIL, %l4 wr %l4, 0x0, %psr @@ -327,12 +324,11 @@ linux_trap_ipi15_sun4m: 1: /* NMI async memory error handling. */ sethi %hi(0x80000000), %l4 - sethi %hi(0x4000), %o3 - sub %o5, %o0, %o5 - add %o5, %o3, %l5 - st %l4, [%l5 + 0xc] + sethi %hi(sun4m_irq_global), %o5 + ld [%o5 + %lo(sun4m_irq_global)], %l5 + st %l4, [%l5 + 0x0c] ! sun4m_irq_global->mask_set=0x80000000 WRITE_PAUSE - ld [%l5], %g0 + ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending WRITE_PAUSE or %l0, PSR_PIL, %l4 wr %l4, 0x0, %psr @@ -341,9 +337,9 @@ linux_trap_ipi15_sun4m: WRITE_PAUSE call sun4m_nmi nop - st %l4, [%l5 + 0x8] + st %l4, [%l5 + 0x08] ! sun4m_irq_global->mask_clear=0x80000000 WRITE_PAUSE - ld [%l5], %g0 + ld [%l5 + 0x00], %g0 ! sun4m_irq_global->pending WRITE_PAUSE RESTORE_ALL @@ -775,11 +771,7 @@ vac_linesize_patch_32: subcc %l7, 32, %l7 * Ugly, but we cant use hardware flushing on the sun4 and we'd require * two instructions (Anton) */ -#ifdef CONFIG_SUN4 -vac_hwflush_patch1_on: nop -#else vac_hwflush_patch1_on: addcc %l7, -PAGE_SIZE, %l7 -#endif vac_hwflush_patch2_on: sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG @@ -798,42 +790,10 @@ vac_hwflush_patch2_on: sta %g0, [%l3 + %l7] ASI_HWFLUSHSEG ! %l7 = 1 for textfault ! We want error in %l5, vaddr in %l6 sun4c_fault: -#ifdef CONFIG_SUN4 - sethi %hi(sun4c_memerr_reg), %l4 - ld [%l4+%lo(sun4c_memerr_reg)], %l4 ! memerr ctrl reg addr - ld [%l4], %l6 ! memerr ctrl reg - ld [%l4 + 4], %l5 ! memerr vaddr reg - andcc %l6, 0x80, %g0 ! check for error type - st %g0, [%l4 + 4] ! clear the error - be 0f ! normal error - sethi %hi(AC_BUS_ERROR), %l4 ! bus err reg addr - - call prom_halt ! something weird happened - ! what exactly did happen? - ! what should we do here? - -0: or %l4, %lo(AC_BUS_ERROR), %l4 ! bus err reg addr - lduba [%l4] ASI_CONTROL, %l6 ! bus err reg - - cmp %l7, 1 ! text fault? - be 1f ! yes - nop - - ld [%l1], %l4 ! load instruction that caused fault - srl %l4, 21, %l4 - andcc %l4, 1, %g0 ! store instruction? - - be 1f ! no - sethi %hi(SUN4C_SYNC_BADWRITE), %l4 ! yep - ! %lo(SUN4C_SYNC_BADWRITE) = 0 - or %l4, %l6, %l6 ! set write bit to emulate sun4c -1: -#else sethi %hi(AC_SYNC_ERR), %l4 add %l4, 0x4, %l6 ! AC_SYNC_VA in %l6 lda [%l6] ASI_CONTROL, %l5 ! Address lda [%l4] ASI_CONTROL, %l6 ! Error, retained for a bit -#endif andn %l5, 0xfff, %l5 ! Encode all info into l7 srl %l6, 14, %l4 @@ -880,12 +840,7 @@ sun4c_fault: or %l4, %lo(swapper_pg_dir), %l4 sll %l6, 2, %l6 ld [%l4 + %l6], %l4 -#ifdef CONFIG_SUN4 - sethi %hi(PAGE_MASK), %l6 - andcc %l4, %l6, %g0 -#else andcc %l4, PAGE_MASK, %g0 -#endif be sun4c_fault_fromuser lduXa [%l5] ASI_SEGMAP, %l4 @@ -937,11 +892,7 @@ invalid_segment_patch1: ld [%l6 + 0x08], %l3 ! tmp = entry->vaddr ! Flush segment from the cache. -#ifdef CONFIG_SUN4 - sethi %hi((128 * 1024)), %l7 -#else sethi %hi((64 * 1024)), %l7 -#endif 9: vac_hwflush_patch1: vac_linesize_patch: @@ -1029,12 +980,7 @@ invalid_segment_patch2: or %l4, %lo(swapper_pg_dir), %l4 sll %l3, 2, %l3 ld [%l4 + %l3], %l4 -#ifndef CONFIG_SUN4 and %l4, PAGE_MASK, %l4 -#else - sethi %hi(PAGE_MASK), %l6 - and %l4, %l6, %l4 -#endif srl %l5, (PAGE_SHIFT - 2), %l6 and %l6, ((SUN4C_PTRS_PER_PTE - 1) << 2), %l6 diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index 50d9a16af79..2d325fd8457 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S @@ -63,15 +63,9 @@ cputypvar_sun4m: .align 4 -#ifndef CONFIG_SUN4 sun4_notsup: - .asciz "Sparc-Linux sun4 needs a specially compiled kernel, turn CONFIG_SUN4 on.\n\n" + .asciz "Sparc-Linux sun4 support does no longer exist.\n\n" .align 4 -#else -sun4cdm_notsup: - .asciz "Kernel compiled with CONFIG_SUN4 cannot run on SUN4C/SUN4M/SUN4D\nTurn CONFIG_SUN4 off.\n\n" - .align 4 -#endif sun4e_notsup: .asciz "Sparc-Linux sun4e support does not exist\n\n" @@ -780,15 +774,6 @@ execute_in_high_mem: nop found_version: -#ifdef CONFIG_SUN4 -/* For people who try sun4 kernels, even if Configure.help advises them. */ - ld [%g7 + 0x68], %o1 - set sun4cdm_notsup, %o0 - call %o1 - nop - b halt_me - nop -#endif /* Get the machine type via the mysterious romvec node operations. */ add %g7, 0x1c, %l1 @@ -1150,15 +1135,6 @@ sun4c_continue_boot: nop sun4_init: -#ifdef CONFIG_SUN4 -/* There, happy now Adrian? */ - set cputypval, %o2 ! Let everyone know we - set ' ', %o0 ! are a "sun4 " architecture - stb %o0, [%o2 + 0x4] - - b got_prop - nop -#else sethi %hi(SUN4_PROM_VECTOR+0x84), %o1 ld [%o1 + %lo(SUN4_PROM_VECTOR+0x84)], %o1 set sun4_notsup, %o0 @@ -1170,7 +1146,7 @@ sun4_init: nop 1: ba 1b ! Cannot exit into KMON nop -#endif + no_sun4e_here: ld [%g7 + 0x68], %o1 set sun4e_notsup, %o0 diff --git a/arch/sparc/kernel/idprom.c b/arch/sparc/kernel/idprom.c index fc511f3c4c1..223a6582e1e 100644 --- a/arch/sparc/kernel/idprom.c +++ b/arch/sparc/kernel/idprom.c @@ -12,10 +12,6 @@ #include <asm/oplib.h> #include <asm/idprom.h> #include <asm/machines.h> /* Fun with Sun released architectures. */ -#ifdef CONFIG_SUN4 -#include <asm/sun4paddr.h> -extern void sun4setup(void); -#endif struct idprom *idprom; static struct idprom idprom_buffer; @@ -101,7 +97,4 @@ void __init idprom_init(void) idprom->id_ethaddr[0], idprom->id_ethaddr[1], idprom->id_ethaddr[2], idprom->id_ethaddr[3], idprom->id_ethaddr[4], idprom->id_ethaddr[5]); -#ifdef CONFIG_SUN4 - sun4setup(); -#endif } diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 2a8a847764d..4f025b36934 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -42,10 +42,13 @@ #include <asm/vaddrs.h> #include <asm/oplib.h> #include <asm/prom.h> -#include <asm/sbus.h> #include <asm/page.h> #include <asm/pgalloc.h> #include <asm/dma.h> +#include <asm/iommu.h> +#include <asm/io-unit.h> + +#include "dma.h" #define mmu_inval_dma_area(p, l) /* Anton pulled it out for 2.4.0-xx */ @@ -139,15 +142,6 @@ void iounmap(volatile void __iomem *virtual) } } -/* - */ -void __iomem *sbus_ioremap(struct resource *phyres, unsigned long offset, - unsigned long size, char *name) -{ - return _sparc_alloc_io(phyres->flags & 0xF, - phyres->start + offset, size, name); -} - void __iomem *of_ioremap(struct resource *res, unsigned long offset, unsigned long size, char *name) { @@ -164,13 +158,6 @@ void of_iounmap(struct resource *res, void __iomem *base, unsigned long size) EXPORT_SYMBOL(of_iounmap); /* - */ -void sbus_iounmap(volatile void __iomem *addr, unsigned long size) -{ - iounmap(addr); -} - -/* * Meat of mapping */ static void __iomem *_sparc_alloc_io(unsigned int busno, unsigned long phys, @@ -246,63 +233,19 @@ static void _sparc_free_io(struct resource *res) #ifdef CONFIG_SBUS -void sbus_set_sbus64(struct sbus_dev *sdev, int x) +void sbus_set_sbus64(struct device *dev, int x) { printk("sbus_set_sbus64: unsupported\n"); } -extern unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq); -void __init sbus_fill_device_irq(struct sbus_dev *sdev) -{ - struct linux_prom_irqs irqs[PROMINTR_MAX]; - int len; - - len = prom_getproperty(sdev->prom_node, "intr", - (char *)irqs, sizeof(irqs)); - if (len != -1) { - sdev->num_irqs = len / 8; - if (sdev->num_irqs == 0) { - sdev->irqs[0] = 0; - } else if (sparc_cpu_model == sun4d) { - for (len = 0; len < sdev->num_irqs; len++) - sdev->irqs[len] = - sun4d_build_irq(sdev, irqs[len].pri); - } else { - for (len = 0; len < sdev->num_irqs; len++) - sdev->irqs[len] = irqs[len].pri; - } - } else { - int interrupts[PROMINTR_MAX]; - - /* No "intr" node found-- check for "interrupts" node. - * This node contains SBus interrupt levels, not IPLs - * as in "intr", and no vector values. We convert - * SBus interrupt levels to PILs (platform specific). - */ - len = prom_getproperty(sdev->prom_node, "interrupts", - (char *)interrupts, sizeof(interrupts)); - if (len == -1) { - sdev->irqs[0] = 0; - sdev->num_irqs = 0; - } else { - sdev->num_irqs = len / sizeof(int); - for (len = 0; len < sdev->num_irqs; len++) { - sdev->irqs[len] = - sbint_to_irq(sdev, interrupts[len]); - } - } - } -} - /* * Allocate a chunk of memory suitable for DMA. * Typically devices use them for control blocks. * CPU may access them without any explicit flushing. - * - * XXX Some clever people know that sdev is not used and supply NULL. Watch. */ -void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp) +void *sbus_alloc_consistent(struct device *dev, long len, u32 *dma_addrp) { + struct of_device *op = to_of_device(dev); unsigned long len_total = (len + PAGE_SIZE-1) & PAGE_MASK; unsigned long va; struct resource *res; @@ -336,13 +279,10 @@ void *sbus_alloc_consistent(struct sbus_dev *sdev, long len, u32 *dma_addrp) * XXX That's where sdev would be used. Currently we load * all iommu tables with the same translations. */ - if (mmu_map_dma_area(dma_addrp, va, res->start, len_total) != 0) + if (mmu_map_dma_area(dev, dma_addrp, va, res->start, len_total) != 0) goto err_noiommu; - /* Set the resource name, if known. */ - if (sdev) { - res->name = sdev->prom_name; - } + res->name = op->node->name; return (void *)(unsigned long)res->start; @@ -356,7 +296,7 @@ err_nopages: return NULL; } -void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) +void sbus_free_consistent(struct device *dev, long n, void *p, u32 ba) { struct resource *res; struct page *pgv; @@ -383,8 +323,8 @@ void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) kfree(res); /* mmu_inval_dma_area(va, n); */ /* it's consistent, isn't it */ - pgv = mmu_translate_dvma(ba); - mmu_unmap_dma_area(ba, n); + pgv = virt_to_page(p); + mmu_unmap_dma_area(dev, ba, n); __free_pages(pgv, get_order(n)); } @@ -394,7 +334,7 @@ void sbus_free_consistent(struct sbus_dev *sdev, long n, void *p, u32 ba) * CPU view of this memory may be inconsistent with * a device view and explicit flushing is necessary. */ -dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *va, size_t len, int direction) +dma_addr_t sbus_map_single(struct device *dev, void *va, size_t len, int direction) { /* XXX why are some lengths signed, others unsigned? */ if (len <= 0) { @@ -404,17 +344,17 @@ dma_addr_t sbus_map_single(struct sbus_dev *sdev, void *va, size_t len, int dire if (len > 256*1024) { /* __get_free_pages() limit */ return 0; } - return mmu_get_scsi_one(va, len, sdev->bus); + return mmu_get_scsi_one(dev, va, len); } -void sbus_unmap_single(struct sbus_dev *sdev, dma_addr_t ba, size_t n, int direction) +void sbus_unmap_single(struct device *dev, dma_addr_t ba, size_t n, int direction) { - mmu_release_scsi_one(ba, n, sdev->bus); + mmu_release_scsi_one(dev, ba, n); } -int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +int sbus_map_sg(struct device *dev, struct scatterlist *sg, int n, int direction) { - mmu_get_scsi_sgl(sg, n, sdev->bus); + mmu_get_scsi_sgl(dev, sg, n); /* * XXX sparc64 can return a partial length here. sun4c should do this @@ -423,145 +363,28 @@ int sbus_map_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direct return n; } -void sbus_unmap_sg(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) -{ - mmu_release_scsi_sgl(sg, n, sdev->bus); -} - -/* - */ -void sbus_dma_sync_single_for_cpu(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) -{ -#if 0 - unsigned long va; - struct resource *res; - - /* We do not need the resource, just print a message if invalid. */ - res = _sparc_find_resource(&_sparc_dvma, ba); - if (res == NULL) - panic("sbus_dma_sync_single: 0x%x\n", ba); - - va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */ - /* - * XXX This bogosity will be fixed with the iommu rewrite coming soon - * to a kernel near you. - Anton - */ - /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */ -#endif -} - -void sbus_dma_sync_single_for_device(struct sbus_dev *sdev, dma_addr_t ba, size_t size, int direction) +void sbus_unmap_sg(struct device *dev, struct scatterlist *sg, int n, int direction) { -#if 0 - unsigned long va; - struct resource *res; - - /* We do not need the resource, just print a message if invalid. */ - res = _sparc_find_resource(&_sparc_dvma, ba); - if (res == NULL) - panic("sbus_dma_sync_single: 0x%x\n", ba); - - va = page_address(mmu_translate_dvma(ba)); /* XXX higmem */ - /* - * XXX This bogosity will be fixed with the iommu rewrite coming soon - * to a kernel near you. - Anton - */ - /* mmu_inval_dma_area(va, (size + PAGE_SIZE-1) & PAGE_MASK); */ -#endif + mmu_release_scsi_sgl(dev, sg, n); } -void sbus_dma_sync_sg_for_cpu(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +void sbus_dma_sync_single_for_cpu(struct device *dev, dma_addr_t ba, size_t size, int direction) { - printk("sbus_dma_sync_sg_for_cpu: not implemented yet\n"); } -void sbus_dma_sync_sg_for_device(struct sbus_dev *sdev, struct scatterlist *sg, int n, int direction) +void sbus_dma_sync_single_for_device(struct device *dev, dma_addr_t ba, size_t size, int direction) { - printk("sbus_dma_sync_sg_for_device: not implemented yet\n"); -} - -/* Support code for sbus_init(). */ -/* - * XXX This functions appears to be a distorted version of - * prom_sbus_ranges_init(), with all sun4d stuff cut away. - * Ask DaveM what is going on here, how is sun4d supposed to work... XXX - */ -/* added back sun4d patch from Thomas Bogendoerfer - should be OK (crn) */ -void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus) -{ - int parent_node = pn->node; - - if (sparc_cpu_model == sun4d) { - struct linux_prom_ranges iounit_ranges[PROMREG_MAX]; - int num_iounit_ranges, len; - - len = prom_getproperty(parent_node, "ranges", - (char *) iounit_ranges, - sizeof (iounit_ranges)); - if (len != -1) { - num_iounit_ranges = - (len / sizeof(struct linux_prom_ranges)); - prom_adjust_ranges(sbus->sbus_ranges, - sbus->num_sbus_ranges, - iounit_ranges, num_iounit_ranges); - } - } } -void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp) -{ -#ifndef CONFIG_SUN4 - struct device_node *parent = dp->parent; - - if (sparc_cpu_model != sun4d && - parent != NULL && - !strcmp(parent->name, "iommu")) { - extern void iommu_init(int iommu_node, struct sbus_bus *sbus); - - iommu_init(parent->node, sbus); - } - - if (sparc_cpu_model == sun4d) { - extern void iounit_init(int sbi_node, int iounit_node, - struct sbus_bus *sbus); - - iounit_init(dp->node, parent->node, sbus); - } -#endif -} - -void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp) -{ - if (sparc_cpu_model == sun4d) { - struct device_node *parent = dp->parent; - - sbus->devid = of_getintprop_default(parent, "device-id", 0); - sbus->board = of_getintprop_default(parent, "board#", 0); - } -} - -int __init sbus_arch_preinit(void) +static int __init sparc_register_ioport(void) { register_proc_sparc_ioport(); -#ifdef CONFIG_SUN4 - { - extern void sun4_dvma_init(void); - sun4_dvma_init(); - } - return 1; -#else return 0; -#endif } -void __init sbus_arch_postinit(void) -{ - if (sparc_cpu_model == sun4d) { - extern void sun4d_init_sbi_irq(void); - sun4d_init_sbi_irq(); - } -} +arch_initcall(sparc_register_ioport); + #endif /* CONFIG_SBUS */ #ifdef CONFIG_PCI diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index 32ef3ebd0a8..db751388153 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h @@ -13,7 +13,6 @@ BTFIXUPDEF_CALL(void, enable_irq, unsigned int) BTFIXUPDEF_CALL(void, disable_pil_irq, unsigned int) BTFIXUPDEF_CALL(void, enable_pil_irq, unsigned int) BTFIXUPDEF_CALL(void, clear_clock_irq, void) -BTFIXUPDEF_CALL(void, clear_profile_irq, int) BTFIXUPDEF_CALL(void, load_profile_irq, int, unsigned int) static inline void __disable_irq(unsigned int irq) @@ -41,11 +40,6 @@ static inline void clear_clock_irq(void) BTFIXUP_CALL(clear_clock_irq)(); } -static inline void clear_profile_irq(int irq) -{ - BTFIXUP_CALL(clear_profile_irq)(irq); -} - static inline void load_profile_irq(int cpu, int limit) { BTFIXUP_CALL(load_profile_irq)(cpu, limit); diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c index f58c537446a..0837bd52e28 100644 --- a/arch/sparc/kernel/of_device.c +++ b/arch/sparc/kernel/of_device.c @@ -29,15 +29,38 @@ struct of_device *of_find_device_by_node(struct device_node *dp) } EXPORT_SYMBOL(of_find_device_by_node); -#ifdef CONFIG_PCI -struct bus_type ebus_bus_type; -EXPORT_SYMBOL(ebus_bus_type); -#endif +unsigned int irq_of_parse_and_map(struct device_node *node, int index) +{ + struct of_device *op = of_find_device_by_node(node); + + if (!op || index >= op->num_irqs) + return 0; + + return op->irqs[index]; +} +EXPORT_SYMBOL(irq_of_parse_and_map); + +/* Take the archdata values for IOMMU, STC, and HOSTDATA found in + * BUS and propagate to all child of_device objects. + */ +void of_propagate_archdata(struct of_device *bus) +{ + struct dev_archdata *bus_sd = &bus->dev.archdata; + struct device_node *bus_dp = bus->node; + struct device_node *dp; + + for (dp = bus_dp->child; dp; dp = dp->sibling) { + struct of_device *op = of_find_device_by_node(dp); -#ifdef CONFIG_SBUS -struct bus_type sbus_bus_type; -EXPORT_SYMBOL(sbus_bus_type); -#endif + op->dev.archdata.iommu = bus_sd->iommu; + op->dev.archdata.stc = bus_sd->stc; + op->dev.archdata.host_controller = bus_sd->host_controller; + op->dev.archdata.numa_node = bus_sd->numa_node; + + if (dp->child) + of_propagate_archdata(op); + } +} struct bus_type of_platform_bus_type; EXPORT_SYMBOL(of_platform_bus_type); @@ -327,6 +350,27 @@ static int __init build_one_resource(struct device_node *parent, return 1; } +static int __init use_1to1_mapping(struct device_node *pp) +{ + /* If we have a ranges property in the parent, use it. */ + if (of_find_property(pp, "ranges", NULL) != NULL) + return 0; + + /* Some SBUS devices use intermediate nodes to express + * hierarchy within the device itself. These aren't + * real bus nodes, and don't have a 'ranges' property. + * But, we should still pass the translation work up + * to the SBUS itself. + */ + if (!strcmp(pp->name, "dma") || + !strcmp(pp->name, "espdma") || + !strcmp(pp->name, "ledma") || + !strcmp(pp->name, "lebuffer")) + return 0; + + return 1; +} + static int of_resource_verbose; static void __init build_device_resources(struct of_device *op, @@ -373,10 +417,7 @@ static void __init build_device_resources(struct of_device *op, flags = bus->get_flags(reg, 0); - /* If the immediate parent has no ranges property to apply, - * just use a 1<->1 mapping. - */ - if (of_find_property(pp, "ranges", NULL) == NULL) { + if (use_1to1_mapping(pp)) { result = of_read_addr(addr, na); goto build_res; } @@ -565,15 +606,6 @@ static int __init of_bus_driver_init(void) int err; err = of_bus_type_init(&of_platform_bus_type, "of"); -#ifdef CONFIG_PCI - if (!err) - err = of_bus_type_init(&ebus_bus_type, "ebus"); -#endif -#ifdef CONFIG_SBUS - if (!err) - err = of_bus_type_init(&sbus_bus_type, "sbus"); -#endif - if (!err) scan_of_devices(); diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index a6a6f982337..462584e55fb 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -17,8 +17,6 @@ #include <linux/slab.h> #include <linux/jiffies.h> -#include <asm/ebus.h> -#include <asm/sbus.h> /* for sanity check... */ #include <asm/swift.h> /* for cache flushing. */ #include <asm/io.h> @@ -430,7 +428,6 @@ static int __init pcic_init(void) pcic_pbm_scan_bus(pcic); - ebus_init(); return 0; } @@ -493,10 +490,6 @@ static void pcic_map_pci_device(struct linux_pcic *pcic, * do ioremap() before accessing PC-style I/O, * we supply virtual, ready to access address. * - * Ebus devices do not come here even if - * CheerIO makes a similar conversion. - * See ebus.c for details. - * * Note that request_region() * works for these devices. * @@ -677,7 +670,7 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) } /* - * pcic_pin_to_irq() is exported to ebus.c. + * pcic_pin_to_irq() is exported to bus probing code */ unsigned int pcic_pin_to_irq(unsigned int pin, const char *name) @@ -904,11 +897,6 @@ static void pcic_enable_irq(unsigned int irq_nr) local_irq_restore(flags); } -static void pcic_clear_profile_irq(int cpu) -{ - printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__); -} - static void pcic_load_profile_irq(int cpu, unsigned int limit) { printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__); @@ -934,7 +922,6 @@ void __init sun4m_pci_init_IRQ(void) BTFIXUPSET_CALL(enable_pil_irq, pcic_enable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, pcic_disable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, pcic_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, pcic_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, pcic_load_profile_irq, BTFIXUPCALL_NORM); } diff --git a/arch/sparc/kernel/pmc.c b/arch/sparc/kernel/pmc.c index 7eca8871ff4..2afcfab4f11 100644 --- a/arch/sparc/kernel/pmc.c +++ b/arch/sparc/kernel/pmc.c @@ -8,11 +8,11 @@ #include <linux/fs.h> #include <linux/errno.h> #include <linux/init.h> -#include <linux/miscdevice.h> #include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/auxio.h> @@ -23,17 +23,15 @@ * #define PMC_NO_IDLE */ -#define PMC_MINOR MISC_DYNAMIC_MINOR #define PMC_OBPNAME "SUNW,pmc" #define PMC_DEVNAME "pmc" #define PMC_IDLE_REG 0x00 #define PMC_IDLE_ON 0x01 -volatile static u8 __iomem *regs; -static int pmc_regsize; +static u8 __iomem *regs; -#define pmc_readb(offs) (sbus_readb(regs+offs)) +#define pmc_readb(offs) (sbus_readb(regs+offs)) #define pmc_writeb(val, offs) (sbus_writeb(val, regs+offs)) /* @@ -53,31 +51,11 @@ void pmc_swift_idle(void) #endif } -static inline void pmc_free(void) +static int __devinit pmc_probe(struct of_device *op, + const struct of_device_id *match) { - sbus_iounmap(regs, pmc_regsize); -} - -static int __init pmc_probe(void) -{ - struct sbus_bus *sbus = NULL; - struct sbus_dev *sdev = NULL; - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, PMC_OBPNAME)) { - goto sbus_done; - } - } - } - -sbus_done: - if (!sdev) { - return -ENODEV; - } - - pmc_regsize = sdev->reg_addrs[0].reg_size; - regs = sbus_ioremap(&sdev->resource[0], 0, - pmc_regsize, PMC_OBPNAME); + regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), PMC_OBPNAME); if (!regs) { printk(KERN_ERR "%s: unable to map registers\n", PMC_DEVNAME); return -ENODEV; @@ -92,8 +70,27 @@ sbus_done: return 0; } +static struct of_device_id __initdata pmc_match[] = { + { + .name = PMC_OBPNAME, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, pmc_match); + +static struct of_platform_driver pmc_driver = { + .name = "pmc", + .match_table = pmc_match, + .probe = pmc_probe, +}; + +static int __init pmc_init(void) +{ + return of_register_driver(&pmc_driver, &of_bus_type); +} + /* This driver is not critical to the boot process * and is easiest to ioremap when SBus is already * initialized, so we install ourselves thusly: */ -__initcall(pmc_probe); +__initcall(pmc_init); diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 4bb430940a6..e8c43ffe317 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -75,7 +75,7 @@ void cpu_idle(void) { /* endless idle loop with no priority at all */ for (;;) { - if (ARCH_SUN4C_SUN4) { + if (ARCH_SUN4C) { static int count = HZ; static unsigned long last_jiffies; static unsigned long last_faults; diff --git a/arch/sparc/kernel/prom.c b/arch/sparc/kernel/prom.c index cd4fb79aa3a..eee5efcfe50 100644 --- a/arch/sparc/kernel/prom.c +++ b/arch/sparc/kernel/prom.c @@ -54,6 +54,9 @@ int of_getintprop_default(struct device_node *np, const char *name, int def) } EXPORT_SYMBOL(of_getintprop_default); +DEFINE_MUTEX(of_set_property_mutex); +EXPORT_SYMBOL(of_set_property_mutex); + int of_set_property(struct device_node *dp, const char *name, void *val, int len) { struct property **prevp; @@ -77,7 +80,10 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len void *old_val = prop->value; int ret; + mutex_lock(&of_set_property_mutex); ret = prom_setprop(dp->node, (char *) name, val, len); + mutex_unlock(&of_set_property_mutex); + err = -EINVAL; if (ret >= 0) { prop->value = new_val; @@ -436,7 +442,6 @@ static void __init of_console_init(void) switch (prom_vers) { case PROM_V0: - case PROM_SUN4: skip = 0; switch (*romvec->pv_stdout) { case PROMDEV_SCREEN: diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 9e451b21202..24fe3078bd4 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -213,23 +213,25 @@ void __init setup_arch(char **cmdline_p) /* Initialize PROM console and command line. */ *cmdline_p = prom_getbootargs(); strcpy(boot_command_line, *cmdline_p); + parse_early_param(); /* Set sparc_cpu_model */ sparc_cpu_model = sun_unknown; - if(!strcmp(&cputypval,"sun4 ")) { sparc_cpu_model=sun4; } - if(!strcmp(&cputypval,"sun4c")) { sparc_cpu_model=sun4c; } - if(!strcmp(&cputypval,"sun4m")) { sparc_cpu_model=sun4m; } - if(!strcmp(&cputypval,"sun4s")) { sparc_cpu_model=sun4m; } /* CP-1200 with PROM 2.30 -E */ - if(!strcmp(&cputypval,"sun4d")) { sparc_cpu_model=sun4d; } - if(!strcmp(&cputypval,"sun4e")) { sparc_cpu_model=sun4e; } - if(!strcmp(&cputypval,"sun4u")) { sparc_cpu_model=sun4u; } - -#ifdef CONFIG_SUN4 - if (sparc_cpu_model != sun4) { - prom_printf("This kernel is for Sun4 architecture only.\n"); - prom_halt(); - } -#endif + if (!strcmp(&cputypval,"sun4 ")) + sparc_cpu_model = sun4; + if (!strcmp(&cputypval,"sun4c")) + sparc_cpu_model = sun4c; + if (!strcmp(&cputypval,"sun4m")) + sparc_cpu_model = sun4m; + if (!strcmp(&cputypval,"sun4s")) + sparc_cpu_model = sun4m; /* CP-1200 with PROM 2.30 -E */ + if (!strcmp(&cputypval,"sun4d")) + sparc_cpu_model = sun4d; + if (!strcmp(&cputypval,"sun4e")) + sparc_cpu_model = sun4e; + if (!strcmp(&cputypval,"sun4u")) + sparc_cpu_model = sun4u; + printk("ARCH: "); switch(sparc_cpu_model) { case sun4: @@ -263,7 +265,7 @@ void __init setup_arch(char **cmdline_p) boot_flags_init(*cmdline_p); idprom_init(); - if (ARCH_SUN4C_SUN4) + if (ARCH_SUN4C) sun4c_probe_vac(); load_mmu(); diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index b23cea5ca5d..b0dfff84865 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -38,17 +38,12 @@ #include <asm/idprom.h> #include <asm/head.h> #include <asm/smp.h> -#include <asm/mostek.h> #include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/checksum.h> #ifdef CONFIG_SBUS -#include <asm/sbus.h> #include <asm/dma.h> #endif -#ifdef CONFIG_PCI -#include <asm/ebus.h> -#endif #include <asm/io-unit.h> #include <asm/bug.h> @@ -127,16 +122,11 @@ EXPORT_SYMBOL(phys_cpu_present_map); EXPORT_SYMBOL(__udelay); EXPORT_SYMBOL(__ndelay); EXPORT_SYMBOL(rtc_lock); -EXPORT_SYMBOL(mostek_lock); -EXPORT_SYMBOL(mstk48t02_regs); #ifdef CONFIG_SUN_AUXIO EXPORT_SYMBOL(set_auxio); EXPORT_SYMBOL(get_auxio); #endif EXPORT_SYMBOL(io_remap_pfn_range); - /* P3: iounit_xxx may be needed, sun4d users */ -/* EXPORT_SYMBOL(iounit_map_dma_init); */ -/* EXPORT_SYMBOL(iounit_map_dma_page); */ #ifndef CONFIG_SMP EXPORT_SYMBOL(BTFIXUP_CALL(___xchg32)); @@ -153,24 +143,9 @@ EXPORT_SYMBOL(BTFIXUP_CALL(mmu_release_scsi_one)); EXPORT_SYMBOL(BTFIXUP_CALL(pgprot_noncached)); #ifdef CONFIG_SBUS -EXPORT_SYMBOL(sbus_root); -EXPORT_SYMBOL(dma_chain); EXPORT_SYMBOL(sbus_set_sbus64); -EXPORT_SYMBOL(sbus_alloc_consistent); -EXPORT_SYMBOL(sbus_free_consistent); -EXPORT_SYMBOL(sbus_map_single); -EXPORT_SYMBOL(sbus_unmap_single); -EXPORT_SYMBOL(sbus_map_sg); -EXPORT_SYMBOL(sbus_unmap_sg); -EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_single_for_device); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_device); -EXPORT_SYMBOL(sbus_iounmap); -EXPORT_SYMBOL(sbus_ioremap); #endif #ifdef CONFIG_PCI -EXPORT_SYMBOL(ebus_chain); EXPORT_SYMBOL(insb); EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(insw); diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index 340fc395fe2..5dc8a576948 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c @@ -18,6 +18,8 @@ #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include "irq.h" #include <asm/ptrace.h> @@ -31,15 +33,8 @@ #include <asm/traps.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sun4paddr.h> #include <asm/idprom.h> #include <asm/machines.h> -#include <asm/sbus.h> - -#if 0 -static struct resource sun4c_timer_eb = { "sun4c_timer" }; -static struct resource sun4c_intr_eb = { "sun4c_intr" }; -#endif /* * Bit field defines for the interrupt registers on various @@ -64,19 +59,7 @@ static struct resource sun4c_intr_eb = { "sun4c_intr" }; * * so don't go making it static, like I tried. sigh. */ -unsigned char *interrupt_enable = NULL; - -static int sun4c_pil_map[] = { 0, 1, 2, 3, 5, 7, 8, 9 }; - -static unsigned int sun4c_sbint_to_irq(struct sbus_dev *sdev, - unsigned int sbint) -{ - if (sbint >= sizeof(sun4c_pil_map)) { - printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); - BUG(); - } - return sun4c_pil_map[sbint]; -} +unsigned char __iomem *interrupt_enable = NULL; static void sun4c_disable_irq(unsigned int irq_nr) { @@ -85,7 +68,7 @@ static void sun4c_disable_irq(unsigned int irq_nr) local_irq_save(flags); irq_nr &= (NR_IRQS - 1); - current_mask = *interrupt_enable; + current_mask = sbus_readb(interrupt_enable); switch(irq_nr) { case 1: new_mask = ((current_mask) & (~(SUN4C_INT_E1))); @@ -103,7 +86,7 @@ static void sun4c_disable_irq(unsigned int irq_nr) local_irq_restore(flags); return; } - *interrupt_enable = new_mask; + sbus_writeb(new_mask, interrupt_enable); local_irq_restore(flags); } @@ -114,7 +97,7 @@ static void sun4c_enable_irq(unsigned int irq_nr) local_irq_save(flags); irq_nr &= (NR_IRQS - 1); - current_mask = *interrupt_enable; + current_mask = sbus_readb(interrupt_enable); switch(irq_nr) { case 1: new_mask = ((current_mask) | SUN4C_INT_E1); @@ -132,37 +115,22 @@ static void sun4c_enable_irq(unsigned int irq_nr) local_irq_restore(flags); return; } - *interrupt_enable = new_mask; + sbus_writeb(new_mask, interrupt_enable); local_irq_restore(flags); } -#define TIMER_IRQ 10 /* Also at level 14, but we ignore that one. */ -#define PROFILE_IRQ 14 /* Level14 ticker.. used by OBP for polling */ - -volatile struct sun4c_timer_info *sun4c_timers; +struct sun4c_timer_info { + u32 l10_count; + u32 l10_limit; + u32 l14_count; + u32 l14_limit; +}; -#ifdef CONFIG_SUN4 -/* This is an ugly hack to work around the - current timer code, and make it work with - the sun4/260 intersil - */ -volatile struct sun4c_timer_info sun4_timer; -#endif +static struct sun4c_timer_info __iomem *sun4c_timers; static void sun4c_clear_clock_irq(void) { - volatile unsigned int clear_intr; -#ifdef CONFIG_SUN4 - if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) - clear_intr = sun4_timer.timer_limit10; - else -#endif - clear_intr = sun4c_timers->timer_limit10; -} - -static void sun4c_clear_profile_irq(int cpu) -{ - /* Errm.. not sure how to do this.. */ + sbus_readl(&sun4c_timers->l10_limit); } static void sun4c_load_profile_irq(int cpu, unsigned int limit) @@ -172,41 +140,48 @@ static void sun4c_load_profile_irq(int cpu, unsigned int limit) static void __init sun4c_init_timers(irq_handler_t counter_fn) { - int irq; + const struct linux_prom_irqs *irq; + struct device_node *dp; + const u32 *addr; + int err; + + dp = of_find_node_by_name(NULL, "counter-timer"); + if (!dp) { + prom_printf("sun4c_init_timers: Unable to find counter-timer\n"); + prom_halt(); + } - /* Map the Timer chip, this is implemented in hardware inside - * the cache chip on the sun4c. - */ -#ifdef CONFIG_SUN4 - if (idprom->id_machtype == (SM_SUN4 | SM_4_260)) - sun4c_timers = &sun4_timer; - else -#endif - sun4c_timers = ioremap(SUN_TIMER_PHYSADDR, - sizeof(struct sun4c_timer_info)); + addr = of_get_property(dp, "address", NULL); + if (!addr) { + prom_printf("sun4c_init_timers: No address property\n"); + prom_halt(); + } + + sun4c_timers = (void __iomem *) (unsigned long) addr[0]; + + irq = of_get_property(dp, "intr", NULL); + if (!irq) { + prom_printf("sun4c_init_timers: No intr property\n"); + prom_halt(); + } /* Have the level 10 timer tick at 100HZ. We don't touch the * level 14 timer limit since we are letting the prom handle * them until we have a real console driver so L1-A works. */ - sun4c_timers->timer_limit10 = (((1000000/HZ) + 1) << 10); - master_l10_counter = &sun4c_timers->cur_count10; - master_l10_limit = &sun4c_timers->timer_limit10; + sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit); - irq = request_irq(TIMER_IRQ, - counter_fn, + master_l10_counter = &sun4c_timers->l10_count; + + err = request_irq(irq[0].pri, counter_fn, (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); - if (irq) { - prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); + if (err) { + prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); prom_halt(); } -#if 0 - /* This does not work on 4/330 */ - sun4c_enable_irq(10); -#endif - claim_ticker14(NULL, PROFILE_IRQ, 0); + sun4c_disable_irq(irq[1].pri); } #ifdef CONFIG_SMP @@ -215,41 +190,28 @@ static void sun4c_nop(void) {} void __init sun4c_init_IRQ(void) { - struct linux_prom_registers int_regs[2]; - int ie_node; + struct device_node *dp; + const u32 *addr; - if (ARCH_SUN4) { - interrupt_enable = (char *) - ioremap(sun4_ie_physaddr, PAGE_SIZE); - } else { - struct resource phyres; - - ie_node = prom_searchsiblings (prom_getchild(prom_root_node), - "interrupt-enable"); - if(ie_node == 0) - panic("Cannot find /interrupt-enable node"); + dp = of_find_node_by_name(NULL, "interrupt-enable"); + if (!dp) { + prom_printf("sun4c_init_IRQ: Unable to find interrupt-enable\n"); + prom_halt(); + } - /* Depending on the "address" property is bad news... */ - interrupt_enable = NULL; - if (prom_getproperty(ie_node, "reg", (char *) int_regs, - sizeof(int_regs)) != -1) { - memset(&phyres, 0, sizeof(struct resource)); - phyres.flags = int_regs[0].which_io; - phyres.start = int_regs[0].phys_addr; - interrupt_enable = (char *) sbus_ioremap(&phyres, 0, - int_regs[0].reg_size, "sun4c_intr"); - } + addr = of_get_property(dp, "address", NULL); + if (!addr) { + prom_printf("sun4c_init_IRQ: No address property\n"); + prom_halt(); } - if (!interrupt_enable) - panic("Cannot map interrupt_enable"); - BTFIXUPSET_CALL(sbint_to_irq, sun4c_sbint_to_irq, BTFIXUPCALL_NORM); + interrupt_enable = (void __iomem *) (unsigned long) addr[0]; + BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, sun4c_clear_profile_irq, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP); sparc_init_timers = sun4c_init_timers; #ifdef CONFIG_SMP @@ -257,6 +219,6 @@ void __init sun4c_init_IRQ(void) BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP); BTFIXUPSET_CALL(set_irq_udt, sun4c_nop, BTFIXUPCALL_NOP); #endif - *interrupt_enable = (SUN4C_INT_ENABLE); + sbus_writeb(SUN4C_INT_ENABLE, interrupt_enable); /* Cannot enable interrupts until OBP ticker is disabled. */ } diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 1290b5998f8..d3cb76ce418 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -19,6 +19,8 @@ #include <linux/smp.h> #include <linux/spinlock.h> #include <linux/seq_file.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -34,7 +36,6 @@ #include <asm/io.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/sbi.h> #include <asm/cacheflush.h> #include <asm/irq_regs.h> @@ -44,16 +45,22 @@ /* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */ /* #define DISTRIBUTE_IRQS */ -struct sun4d_timer_regs *sun4d_timers; +struct sun4d_timer_regs { + u32 l10_timer_limit; + u32 l10_cur_countx; + u32 l10_limit_noclear; + u32 ctrl; + u32 l10_cur_count; +}; + +static struct sun4d_timer_regs __iomem *sun4d_timers; + #define TIMER_IRQ 10 #define MAX_STATIC_ALLOC 4 extern struct irqaction static_irqaction[MAX_STATIC_ALLOC]; extern int static_irq_count; -unsigned char cpu_leds[32]; -#ifdef CONFIG_SMP static unsigned char sbus_tid[32]; -#endif static struct irqaction *irq_action[NR_IRQS]; extern spinlock_t irq_action_lock; @@ -72,9 +79,9 @@ static int sbus_to_pil[] = { }; static int nsbi; -#ifdef CONFIG_SMP + +/* Exported for sun4d_smp.c */ DEFINE_SPINLOCK(sun4d_imsk_lock); -#endif int show_sun4d_interrupts(struct seq_file *p, void *v) { @@ -257,26 +264,6 @@ void sun4d_handler_irq(int irq, struct pt_regs * regs) set_irq_regs(old_regs); } -unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq) -{ - int sbusl = pil_to_sbus[irq]; - - if (sbusl) - return ((sdev->bus->board + 1) << 5) + (sbusl << 2) + sdev->slot; - else - return irq; -} - -static unsigned int sun4d_sbint_to_irq(struct sbus_dev *sdev, - unsigned int sbint) -{ - if (sbint >= sizeof(sbus_to_pil)) { - printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); - BUG(); - } - return sun4d_build_irq(sdev, sbus_to_pil[sbint]); -} - int sun4d_request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void *dev_id) @@ -360,36 +347,28 @@ out: static void sun4d_disable_irq(unsigned int irq) { -#ifdef CONFIG_SMP int tid = sbus_tid[(irq >> 5) - 1]; unsigned long flags; -#endif - if (irq < NR_IRQS) return; -#ifdef CONFIG_SMP + if (irq < NR_IRQS) + return; + spin_lock_irqsave(&sun4d_imsk_lock, flags); cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7])); spin_unlock_irqrestore(&sun4d_imsk_lock, flags); -#else - cc_set_imsk(cc_get_imsk() | (1 << sbus_to_pil[(irq >> 2) & 7])); -#endif } static void sun4d_enable_irq(unsigned int irq) { -#ifdef CONFIG_SMP int tid = sbus_tid[(irq >> 5) - 1]; unsigned long flags; -#endif - if (irq < NR_IRQS) return; -#ifdef CONFIG_SMP + if (irq < NR_IRQS) + return; + spin_lock_irqsave(&sun4d_imsk_lock, flags); cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7])); spin_unlock_irqrestore(&sun4d_imsk_lock, flags); -#else - cc_set_imsk(cc_get_imsk() & ~(1 << sbus_to_pil[(irq >> 2) & 7])); -#endif } #ifdef CONFIG_SMP @@ -409,47 +388,55 @@ static void sun4d_set_udt(int cpu) /* Setup IRQ distribution scheme. */ void __init sun4d_distribute_irqs(void) { + struct device_node *dp; + #ifdef DISTRIBUTE_IRQS - struct sbus_bus *sbus; - unsigned long sbus_serving_map; + cpumask_t sbus_serving_map; sbus_serving_map = cpu_present_map; - for_each_sbus(sbus) { - if ((sbus->board * 2) == boot_cpu_id && (cpu_present_map & (1 << (sbus->board * 2 + 1)))) - sbus_tid[sbus->board] = (sbus->board * 2 + 1); - else if (cpu_present_map & (1 << (sbus->board * 2))) - sbus_tid[sbus->board] = (sbus->board * 2); - else if (cpu_present_map & (1 << (sbus->board * 2 + 1))) - sbus_tid[sbus->board] = (sbus->board * 2 + 1); + for_each_node_by_name(dp, "sbi") { + int board = of_getintprop_default(dp, "board#", 0); + + if ((board * 2) == boot_cpu_id && cpu_isset(board * 2 + 1, cpu_present_map)) + sbus_tid[board] = (board * 2 + 1); + else if (cpu_isset(board * 2, cpu_present_map)) + sbus_tid[board] = (board * 2); + else if (cpu_isset(board * 2 + 1, cpu_present_map)) + sbus_tid[board] = (board * 2 + 1); else - sbus_tid[sbus->board] = 0xff; - if (sbus_tid[sbus->board] != 0xff) - sbus_serving_map &= ~(1 << sbus_tid[sbus->board]); + sbus_tid[board] = 0xff; + if (sbus_tid[board] != 0xff) + cpu_clear(sbus_tid[board], sbus_serving_map); } - for_each_sbus(sbus) - if (sbus_tid[sbus->board] == 0xff) { + for_each_node_by_name(dp, "sbi") { + int board = of_getintprop_default(dp, "board#", 0); + if (sbus_tid[board] == 0xff) { int i = 31; - if (!sbus_serving_map) + if (cpus_empty(sbus_serving_map)) sbus_serving_map = cpu_present_map; - while (!(sbus_serving_map & (1 << i))) + while (cpu_isset(i, sbus_serving_map)) i--; - sbus_tid[sbus->board] = i; - sbus_serving_map &= ~(1 << i); + sbus_tid[board] = i; + cpu_clear(i, sbus_serving_map); } - for_each_sbus(sbus) { - printk("sbus%d IRQs directed to CPU%d\n", sbus->board, sbus_tid[sbus->board]); - set_sbi_tid(sbus->devid, sbus_tid[sbus->board] << 3); + } + for_each_node_by_name(dp, "sbi") { + int devid = of_getintprop_default(dp, "device-id", 0); + int board = of_getintprop_default(dp, "board#", 0); + printk("sbus%d IRQs directed to CPU%d\n", board, sbus_tid[board]); + set_sbi_tid(devid, sbus_tid[board] << 3); } #else - struct sbus_bus *sbus; int cpuid = cpu_logical_map(1); if (cpuid == -1) cpuid = cpu_logical_map(0); - for_each_sbus(sbus) { - sbus_tid[sbus->board] = cpuid; - set_sbi_tid(sbus->devid, cpuid << 3); + for_each_node_by_name(dp, "sbi") { + int devid = of_getintprop_default(dp, "device-id", 0); + int board = of_getintprop_default(dp, "board#", 0); + sbus_tid[board] = cpuid; + set_sbi_tid(devid, cpuid << 3); } printk("All sbus IRQs directed to CPU%d\n", cpuid); #endif @@ -458,13 +445,7 @@ void __init sun4d_distribute_irqs(void) static void sun4d_clear_clock_irq(void) { - volatile unsigned int clear_intr; - clear_intr = sun4d_timers->l10_timer_limit; -} - -static void sun4d_clear_profile_irq(int cpu) -{ - bw_get_prof_limit(cpu); + sbus_readl(&sun4d_timers->l10_timer_limit); } static void sun4d_load_profile_irq(int cpu, unsigned int limit) @@ -472,98 +453,121 @@ static void sun4d_load_profile_irq(int cpu, unsigned int limit) bw_set_prof_limit(cpu, limit); } -static void __init sun4d_init_timers(irq_handler_t counter_fn) +static void __init sun4d_load_profile_irqs(void) { - int irq; - int cpu; - struct resource r; - int mid; + int cpu = 0, mid; - /* Map the User Timer registers. */ - memset(&r, 0, sizeof(r)); + while (!cpu_find_by_instance(cpu, NULL, &mid)) { + sun4d_load_profile_irq(mid >> 3, 0); + cpu++; + } +} + +static void __init sun4d_fixup_trap_table(void) +{ #ifdef CONFIG_SMP - r.start = CSR_BASE(boot_cpu_id)+BW_TIMER_LIMIT; -#else - r.start = CSR_BASE(0)+BW_TIMER_LIMIT; + unsigned long flags; + extern unsigned long lvl14_save[4]; + struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; + extern unsigned int real_irq_entry[], smp4d_ticker[]; + extern unsigned int patchme_maybe_smp_msg[]; + + /* Adjust so that we jump directly to smp4d_ticker */ + lvl14_save[2] += smp4d_ticker - real_irq_entry; + + /* For SMP we use the level 14 ticker, however the bootup code + * has copied the firmware's level 14 vector into the boot cpu's + * trap table, we must fix this now or we get squashed. + */ + local_irq_save(flags); + patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */ + trap_table->inst_one = lvl14_save[0]; + trap_table->inst_two = lvl14_save[1]; + trap_table->inst_three = lvl14_save[2]; + trap_table->inst_four = lvl14_save[3]; + local_flush_cache_all(); + local_irq_restore(flags); #endif - r.flags = 0xf; - sun4d_timers = (struct sun4d_timer_regs *) sbus_ioremap(&r, 0, - PAGE_SIZE, "user timer"); +} - sun4d_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10); - master_l10_counter = &sun4d_timers->l10_cur_count; - master_l10_limit = &sun4d_timers->l10_timer_limit; +static void __init sun4d_init_timers(irq_handler_t counter_fn) +{ + struct device_node *dp; + struct resource res; + const u32 *reg; + int err; + + dp = of_find_node_by_name(NULL, "cpu-unit"); + if (!dp) { + prom_printf("sun4d_init_timers: Unable to find cpu-unit\n"); + prom_halt(); + } - irq = request_irq(TIMER_IRQ, - counter_fn, - (IRQF_DISABLED | SA_STATIC_ALLOC), - "timer", NULL); - if (irq) { - prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); + /* Which cpu-unit we use is arbitrary, we can view the bootbus timer + * registers via any cpu's mapping. The first 'reg' property is the + * bootbus. + */ + reg = of_get_property(dp, "reg", NULL); + if (!reg) { + prom_printf("sun4d_init_timers: No reg property\n"); prom_halt(); } - - /* Enable user timer free run for CPU 0 in BW */ - /* bw_set_ctrl(0, bw_get_ctrl(0) | BW_CTRL_USER_TIMER); */ - cpu = 0; - while (!cpu_find_by_instance(cpu, NULL, &mid)) { - sun4d_load_profile_irq(mid >> 3, 0); - cpu++; + res.start = reg[1]; + res.end = reg[2] - 1; + res.flags = reg[0] & 0xff; + sun4d_timers = of_ioremap(&res, BW_TIMER_LIMIT, + sizeof(struct sun4d_timer_regs), "user timer"); + if (!sun4d_timers) { + prom_printf("sun4d_init_timers: Can't map timer regs\n"); + prom_halt(); } - -#ifdef CONFIG_SMP - { - unsigned long flags; - extern unsigned long lvl14_save[4]; - struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; - extern unsigned int real_irq_entry[], smp4d_ticker[]; - extern unsigned int patchme_maybe_smp_msg[]; - - /* Adjust so that we jump directly to smp4d_ticker */ - lvl14_save[2] += smp4d_ticker - real_irq_entry; - - /* For SMP we use the level 14 ticker, however the bootup code - * has copied the firmware's level 14 vector into the boot cpu's - * trap table, we must fix this now or we get squashed. - */ - local_irq_save(flags); - patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */ - trap_table->inst_one = lvl14_save[0]; - trap_table->inst_two = lvl14_save[1]; - trap_table->inst_three = lvl14_save[2]; - trap_table->inst_four = lvl14_save[3]; - local_flush_cache_all(); - local_irq_restore(flags); + + sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit); + + master_l10_counter = &sun4d_timers->l10_cur_count; + + err = request_irq(TIMER_IRQ, counter_fn, + (IRQF_DISABLED | SA_STATIC_ALLOC), + "timer", NULL); + if (err) { + prom_printf("sun4d_init_timers: request_irq() failed with %d\n", err); + prom_halt(); } -#endif + sun4d_load_profile_irqs(); + sun4d_fixup_trap_table(); } void __init sun4d_init_sbi_irq(void) { - struct sbus_bus *sbus; - unsigned mask; + struct device_node *dp; + int target_cpu = 0; + +#ifdef CONFIG_SMP + target_cpu = boot_cpu_id; +#endif nsbi = 0; - for_each_sbus(sbus) + for_each_node_by_name(dp, "sbi") nsbi++; sbus_actions = kzalloc (nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC); if (!sbus_actions) { prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n"); prom_halt(); } - for_each_sbus(sbus) { -#ifdef CONFIG_SMP - extern unsigned char boot_cpu_id; - - set_sbi_tid(sbus->devid, boot_cpu_id << 3); - sbus_tid[sbus->board] = boot_cpu_id; -#endif + for_each_node_by_name(dp, "sbi") { + int devid = of_getintprop_default(dp, "device-id", 0); + int board = of_getintprop_default(dp, "board#", 0); + unsigned int mask; + + set_sbi_tid(devid, target_cpu << 3); + sbus_tid[board] = target_cpu; + /* Get rid of pending irqs from PROM */ - mask = acquire_sbi(sbus->devid, 0xffffffff); + mask = acquire_sbi(devid, 0xffffffff); if (mask) { - printk ("Clearing pending IRQs %08x on SBI %d\n", mask, sbus->board); - release_sbi(sbus->devid, mask); + printk ("Clearing pending IRQs %08x on SBI %d\n", mask, board); + release_sbi(devid, mask); } } } @@ -572,11 +576,9 @@ void __init sun4d_init_IRQ(void) { local_irq_disable(); - BTFIXUPSET_CALL(sbint_to_irq, sun4d_sbint_to_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM); sparc_init_timers = sun4d_init_timers; #ifdef CONFIG_SMP diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 446767e8f56..7a6a5e79592 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -20,6 +20,7 @@ #include <linux/swap.h> #include <linux/profile.h> #include <linux/delay.h> +#include <linux/cpu.h> #include <asm/ptrace.h> #include <asm/atomic.h> @@ -30,7 +31,6 @@ #include <asm/pgalloc.h> #include <asm/pgtable.h> #include <asm/oplib.h> -#include <asm/sbus.h> #include <asm/sbi.h> #include <asm/tlbflush.h> #include <asm/cacheflush.h> @@ -72,6 +72,17 @@ static void smp_setup_percpu_timer(void); extern void cpu_probe(void); extern void sun4d_distribute_irqs(void); +static unsigned char cpu_leds[32]; + +static inline void show_leds(int cpuid) +{ + cpuid &= 0x1e; + __asm__ __volatile__ ("stba %0, [%1] %2" : : + "r" ((cpu_leds[cpuid] << 4) | cpu_leds[cpuid+1]), + "r" (ECSR_BASE(cpuid) | BB_LEDS), + "i" (ASI_M_CTL)); +} + void __init smp4d_callin(void) { int cpuid = hard_smp4d_processor_id(); diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 94e02de960e..f10317179ee 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -20,6 +20,8 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/ioport.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/ptrace.h> #include <asm/processor.h> @@ -35,59 +37,27 @@ #include <asm/smp.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/cacheflush.h> #include "irq.h" -/* On the sun4m, just like the timers, we have both per-cpu and master - * interrupt registers. - */ - -/* These registers are used for sending/receiving irqs from/to - * different cpu's. - */ -struct sun4m_intreg_percpu { - unsigned int tbt; /* Interrupts still pending for this cpu. */ - - /* These next two registers are WRITE-ONLY and are only - * "on bit" sensitive, "off bits" written have NO affect. - */ - unsigned int clear; /* Clear this cpus irqs here. */ - unsigned int set; /* Set this cpus irqs here. */ - unsigned char space[PAGE_SIZE - 12]; +struct sun4m_irq_percpu { + u32 pending; + u32 clear; + u32 set; }; -/* - * djhr - * Actually the clear and set fields in this struct are misleading.. - * according to the SLAVIO manual (and the same applies for the SEC) - * the clear field clears bits in the mask which will ENABLE that IRQ - * the set field sets bits in the mask to DISABLE the IRQ. - * - * Also the undirected_xx address in the SLAVIO is defined as - * RESERVED and write only.. - * - * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor - * sun4m machines, for MP the layout makes more sense. - */ -struct sun4m_intregs { - struct sun4m_intreg_percpu cpu_intregs[SUN4M_NCPUS]; - unsigned int tbt; /* IRQ's that are still pending. */ - unsigned int irqs; /* Master IRQ bits. */ - - /* Again, like the above, two these registers are WRITE-ONLY. */ - unsigned int clear; /* Clear master IRQ's by setting bits here. */ - unsigned int set; /* Set master IRQ's by setting bits here. */ - - /* This register is both READ and WRITE. */ - unsigned int undirected_target; /* Which cpu gets undirected irqs. */ +struct sun4m_irq_global { + u32 pending; + u32 mask; + u32 mask_clear; + u32 mask_set; + u32 interrupt_target; }; -static unsigned long dummy; - -struct sun4m_intregs *sun4m_interrupts; -unsigned long *irq_rcvreg = &dummy; +/* Code in entry.S needs to get at these register mappings. */ +struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; +struct sun4m_irq_global __iomem *sun4m_irq_global; /* Dave Redman (djhr@tadpole.co.uk) * The sun4m interrupt registers. @@ -101,8 +71,9 @@ unsigned long *irq_rcvreg = &dummy; #define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */ #define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */ -#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */ -#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */ +#define SUN4M_INT_M2S_WRITE_ERR 0x20000000 /* write buffer error */ +#define SUN4M_INT_ECC_ERR 0x10000000 /* ecc memory error */ +#define SUN4M_INT_VME_ERR 0x08000000 /* vme async error */ #define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */ #define SUN4M_INT_MODULE 0x00200000 /* module interrupt */ #define SUN4M_INT_VIDEO 0x00100000 /* onboard video */ @@ -113,75 +84,126 @@ unsigned long *irq_rcvreg = &dummy; #define SUN4M_INT_SERIAL 0x00008000 /* serial ports */ #define SUN4M_INT_KBDMS 0x00004000 /* keyboard/mouse */ #define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */ +#define SUN4M_INT_VMEBITS 0x0000007F /* vme int bits */ + +#define SUN4M_INT_ERROR (SUN4M_INT_MODULE_ERR | \ + SUN4M_INT_M2S_WRITE_ERR | \ + SUN4M_INT_ECC_ERR | \ + SUN4M_INT_VME_ERR) #define SUN4M_INT_SBUS(x) (1 << (x+7)) #define SUN4M_INT_VME(x) (1 << (x)) -/* These tables only apply for interrupts greater than 15.. - * - * any intr value below 0x10 is considered to be a soft-int - * this may be useful or it may not.. but that's how I've done it. - * and it won't clash with what OBP is telling us about devices. +/* Interrupt levels used by OBP */ +#define OBP_INT_LEVEL_SOFT 0x10 +#define OBP_INT_LEVEL_ONBOARD 0x20 +#define OBP_INT_LEVEL_SBUS 0x30 +#define OBP_INT_LEVEL_VME 0x40 + +/* Interrupt level assignment on sun4m: + * + * level source + * ------------------------------------------------------------ + * 1 softint-1 + * 2 softint-2, VME/SBUS level 1 + * 3 softint-3, VME/SBUS level 2 + * 4 softint-4, onboard SCSI + * 5 softint-5, VME/SBUS level 3 + * 6 softint-6, onboard ETHERNET + * 7 softint-7, VME/SBUS level 4 + * 8 softint-8, onboard VIDEO + * 9 softint-9, VME/SBUS level 5, Module Interrupt + * 10 softint-10, system counter/timer + * 11 softint-11, VME/SBUS level 6, Floppy + * 12 softint-12, Keyboard/Mouse, Serial + * 13 softint-13, VME/SBUS level 7, ISDN Audio + * 14 softint-14, per-processor counter/timer + * 15 softint-15, Asynchronous Errors (broadcast) * - * take an encoded intr value and lookup if it's valid - * then get the mask bits that match from irq_mask + * Each interrupt source is masked distinctly in the sun4m interrupt + * registers. The PIL level alone is therefore ambiguous, since multiple + * interrupt sources map to a single PIL. * - * P3: Translation from irq 0x0d to mask 0x2000 is for MrCoffee. + * This ambiguity is resolved in the 'intr' property for device nodes + * in the OF device tree. Each 'intr' property entry is composed of + * two 32-bit words. The first word is the IRQ priority value, which + * is what we're intersted in. The second word is the IRQ vector, which + * is unused. + * + * The low 4 bits of the IRQ priority indicate the PIL, and the upper + * 4 bits indicate onboard vs. SBUS leveled vs. VME leveled. 0x20 + * means onboard, 0x30 means SBUS leveled, and 0x40 means VME leveled. + * + * For example, an 'intr' IRQ priority value of 0x24 is onboard SCSI + * whereas a value of 0x33 is SBUS level 2. Here are some sample + * 'intr' property IRQ priority values from ss4, ss5, ss10, ss20, and + * Tadpole S3 GX systems. + * + * esp: 0x24 onboard ESP SCSI + * le: 0x26 onboard Lance ETHERNET + * p9100: 0x32 SBUS level 1 P9100 video + * bpp: 0x33 SBUS level 2 BPP parallel port device + * DBRI: 0x39 SBUS level 5 DBRI ISDN audio + * SUNW,leo: 0x39 SBUS level 5 LEO video + * pcmcia: 0x3b SBUS level 6 PCMCIA controller + * uctrl: 0x3b SBUS level 6 UCTRL device + * modem: 0x3d SBUS level 7 MODEM + * zs: 0x2c onboard keyboard/mouse/serial + * floppy: 0x2b onboard Floppy + * power: 0x22 onboard power device (XXX unknown mask bit XXX) */ -static unsigned char irq_xlate[32] = { - /* 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f */ - 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 5, 6, 14, 0, 7, - 0, 0, 8, 9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 0 -}; -static unsigned long irq_mask[] = { - 0, /* illegal index */ - SUN4M_INT_SCSI, /* 1 irq 4 */ - SUN4M_INT_ETHERNET, /* 2 irq 6 */ - SUN4M_INT_VIDEO, /* 3 irq 8 */ - SUN4M_INT_REALTIME, /* 4 irq 10 */ - SUN4M_INT_FLOPPY, /* 5 irq 11 */ - (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), /* 6 irq 12 */ - SUN4M_INT_MODULE_ERR, /* 7 irq 15 */ - SUN4M_INT_SBUS(0), /* 8 irq 2 */ - SUN4M_INT_SBUS(1), /* 9 irq 3 */ - SUN4M_INT_SBUS(2), /* 10 irq 5 */ - SUN4M_INT_SBUS(3), /* 11 irq 7 */ - SUN4M_INT_SBUS(4), /* 12 irq 9 */ - SUN4M_INT_SBUS(5), /* 13 irq 11 */ - SUN4M_INT_SBUS(6) /* 14 irq 13 */ +static unsigned long irq_mask[0x50] = { + /* SMP */ + 0, SUN4M_SOFT_INT(1), + SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), + SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5), + SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7), + SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9), + SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11), + SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13), + SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15), + /* soft */ + 0, SUN4M_SOFT_INT(1), + SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3), + SUN4M_SOFT_INT(4), SUN4M_SOFT_INT(5), + SUN4M_SOFT_INT(6), SUN4M_SOFT_INT(7), + SUN4M_SOFT_INT(8), SUN4M_SOFT_INT(9), + SUN4M_SOFT_INT(10), SUN4M_SOFT_INT(11), + SUN4M_SOFT_INT(12), SUN4M_SOFT_INT(13), + SUN4M_SOFT_INT(14), SUN4M_SOFT_INT(15), + /* onboard */ + 0, 0, 0, 0, + SUN4M_INT_SCSI, 0, SUN4M_INT_ETHERNET, 0, + SUN4M_INT_VIDEO, SUN4M_INT_MODULE, + SUN4M_INT_REALTIME, SUN4M_INT_FLOPPY, + (SUN4M_INT_SERIAL | SUN4M_INT_KBDMS), + SUN4M_INT_AUDIO, 0, SUN4M_INT_MODULE_ERR, + /* sbus */ + 0, 0, SUN4M_INT_SBUS(0), SUN4M_INT_SBUS(1), + 0, SUN4M_INT_SBUS(2), 0, SUN4M_INT_SBUS(3), + 0, SUN4M_INT_SBUS(4), 0, SUN4M_INT_SBUS(5), + 0, SUN4M_INT_SBUS(6), 0, 0, + /* vme */ + 0, 0, SUN4M_INT_VME(0), SUN4M_INT_VME(1), + 0, SUN4M_INT_VME(2), 0, SUN4M_INT_VME(3), + 0, SUN4M_INT_VME(4), 0, SUN4M_INT_VME(5), + 0, SUN4M_INT_VME(6), 0, 0 }; -static int sun4m_pil_map[] = { 0, 2, 3, 5, 7, 9, 11, 13 }; - -static unsigned int sun4m_sbint_to_irq(struct sbus_dev *sdev, - unsigned int sbint) -{ - if (sbint >= sizeof(sun4m_pil_map)) { - printk(KERN_ERR "%s: bogus SBINT %d\n", sdev->prom_name, sbint); - BUG(); - } - return sun4m_pil_map[sbint] | 0x30; -} - static unsigned long sun4m_get_irqmask(unsigned int irq) { unsigned long mask; - if (irq > 0x20) { - /* OBIO/SBUS interrupts */ - irq &= 0x1f; - mask = irq_mask[irq_xlate[irq]]; - if (!mask) - printk("sun4m_get_irqmask: IRQ%d has no valid mask!\n",irq); - } else { - /* Soft Interrupts will come here. - * Currently there is no way to trigger them but I'm sure - * something could be cooked up. - */ - irq &= 0xf; - mask = SUN4M_SOFT_INT(irq); - } + if (irq < 0x50) + mask = irq_mask[irq]; + else + mask = 0; + + if (!mask) + printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n", + irq); + return mask; } @@ -193,9 +215,9 @@ static void sun4m_disable_irq(unsigned int irq_nr) mask = sun4m_get_irqmask(irq_nr); local_irq_save(flags); if (irq_nr > 15) - sun4m_interrupts->set = mask; + sbus_writel(mask, &sun4m_irq_global->mask_set); else - sun4m_interrupts->cpu_intregs[cpu].set = mask; + sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); local_irq_restore(flags); } @@ -212,13 +234,13 @@ static void sun4m_enable_irq(unsigned int irq_nr) mask = sun4m_get_irqmask(irq_nr); local_irq_save(flags); if (irq_nr > 15) - sun4m_interrupts->clear = mask; + sbus_writel(mask, &sun4m_irq_global->mask_clear); else - sun4m_interrupts->cpu_intregs[cpu].clear = mask; + sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); local_irq_restore(flags); } else { local_irq_save(flags); - sun4m_interrupts->clear = SUN4M_INT_FLOPPY; + sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear); local_irq_restore(flags); } } @@ -236,10 +258,10 @@ static unsigned long cpu_pil_to_imask[16] = { /*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR, /*10*/ SUN4M_INT_REALTIME, /*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY, -/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, -/*13*/ SUN4M_INT_AUDIO, +/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS, +/*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO, /*14*/ SUN4M_INT_E14, -/*15*/ 0x00000000 +/*15*/ SUN4M_INT_ERROR }; /* We assume the caller has disabled local interrupts when these are called, @@ -247,126 +269,141 @@ static unsigned long cpu_pil_to_imask[16] = { */ static void sun4m_disable_pil_irq(unsigned int pil) { - sun4m_interrupts->set = cpu_pil_to_imask[pil]; + sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set); } static void sun4m_enable_pil_irq(unsigned int pil) { - sun4m_interrupts->clear = cpu_pil_to_imask[pil]; + sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear); } #ifdef CONFIG_SMP static void sun4m_send_ipi(int cpu, int level) { - unsigned long mask; - - mask = sun4m_get_irqmask(level); - sun4m_interrupts->cpu_intregs[cpu].set = mask; + unsigned long mask = sun4m_get_irqmask(level); + sbus_writel(mask, &sun4m_irq_percpu[cpu]->set); } static void sun4m_clear_ipi(int cpu, int level) { - unsigned long mask; - - mask = sun4m_get_irqmask(level); - sun4m_interrupts->cpu_intregs[cpu].clear = mask; + unsigned long mask = sun4m_get_irqmask(level); + sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear); } static void sun4m_set_udt(int cpu) { - sun4m_interrupts->undirected_target = cpu; + sbus_writel(cpu, &sun4m_irq_global->interrupt_target); } #endif -#define OBIO_INTR 0x20 -#define TIMER_IRQ (OBIO_INTR | 10) -#define PROFILE_IRQ (OBIO_INTR | 14) +struct sun4m_timer_percpu { + u32 l14_limit; + u32 l14_count; + u32 l14_limit_noclear; + u32 user_timer_start_stop; +}; + +static struct sun4m_timer_percpu __iomem *timers_percpu[SUN4M_NCPUS]; + +struct sun4m_timer_global { + u32 l10_limit; + u32 l10_count; + u32 l10_limit_noclear; + u32 reserved; + u32 timer_config; +}; + +static struct sun4m_timer_global __iomem *timers_global; + +#define TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10) -static struct sun4m_timer_regs *sun4m_timers; unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10); static void sun4m_clear_clock_irq(void) { - volatile unsigned int clear_intr; - clear_intr = sun4m_timers->l10_timer_limit; + sbus_readl(&timers_global->l10_limit); } -static void sun4m_clear_profile_irq(int cpu) +void sun4m_nmi(struct pt_regs *regs) { - volatile unsigned int clear; - - clear = sun4m_timers->cpu_timers[cpu].l14_timer_limit; + unsigned long afsr, afar, si; + + printk(KERN_ERR "Aieee: sun4m NMI received!\n"); + /* XXX HyperSparc hack XXX */ + __asm__ __volatile__("mov 0x500, %%g1\n\t" + "lda [%%g1] 0x4, %0\n\t" + "mov 0x600, %%g1\n\t" + "lda [%%g1] 0x4, %1\n\t" : + "=r" (afsr), "=r" (afar)); + printk(KERN_ERR "afsr=%08lx afar=%08lx\n", afsr, afar); + si = sbus_readl(&sun4m_irq_global->pending); + printk(KERN_ERR "si=%08lx\n", si); + if (si & SUN4M_INT_MODULE_ERR) + printk(KERN_ERR "Module async error\n"); + if (si & SUN4M_INT_M2S_WRITE_ERR) + printk(KERN_ERR "MBus/SBus async error\n"); + if (si & SUN4M_INT_ECC_ERR) + printk(KERN_ERR "ECC memory error\n"); + if (si & SUN4M_INT_VME_ERR) + printk(KERN_ERR "VME async error\n"); + printk(KERN_ERR "you lose buddy boy...\n"); + show_regs(regs); + prom_halt(); +} + +/* Exported for sun4m_smp.c */ +void sun4m_clear_profile_irq(int cpu) +{ + sbus_readl(&timers_percpu[cpu]->l14_limit); } static void sun4m_load_profile_irq(int cpu, unsigned int limit) { - sun4m_timers->cpu_timers[cpu].l14_timer_limit = limit; + sbus_writel(limit, &timers_percpu[cpu]->l14_limit); } static void __init sun4m_init_timers(irq_handler_t counter_fn) { - int reg_count, irq, cpu; - struct linux_prom_registers cnt_regs[PROMREG_MAX]; - int obio_node, cnt_node; - struct resource r; - - cnt_node = 0; - if((obio_node = - prom_searchsiblings (prom_getchild(prom_root_node), "obio")) == 0 || - (obio_node = prom_getchild (obio_node)) == 0 || - (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) { - prom_printf("Cannot find /obio/counter node\n"); - prom_halt(); + struct device_node *dp = of_find_node_by_name(NULL, "counter"); + int i, err, len, num_cpu_timers; + const u32 *addr; + + if (!dp) { + printk(KERN_ERR "sun4m_init_timers: No 'counter' node.\n"); + return; } - reg_count = prom_getproperty(cnt_node, "reg", - (void *) cnt_regs, sizeof(cnt_regs)); - reg_count = (reg_count/sizeof(struct linux_prom_registers)); - - /* Apply the obio ranges to the timer registers. */ - prom_apply_obio_ranges(cnt_regs, reg_count); - - cnt_regs[4].phys_addr = cnt_regs[reg_count-1].phys_addr; - cnt_regs[4].reg_size = cnt_regs[reg_count-1].reg_size; - cnt_regs[4].which_io = cnt_regs[reg_count-1].which_io; - for(obio_node = 1; obio_node < 4; obio_node++) { - cnt_regs[obio_node].phys_addr = - cnt_regs[obio_node-1].phys_addr + PAGE_SIZE; - cnt_regs[obio_node].reg_size = cnt_regs[obio_node-1].reg_size; - cnt_regs[obio_node].which_io = cnt_regs[obio_node-1].which_io; + + addr = of_get_property(dp, "address", &len); + if (!addr) { + printk(KERN_ERR "sun4m_init_timers: No 'address' prop.\n"); + return; } - memset((char*)&r, 0, sizeof(struct resource)); - /* Map the per-cpu Counter registers. */ - r.flags = cnt_regs[0].which_io; - r.start = cnt_regs[0].phys_addr; - sun4m_timers = (struct sun4m_timer_regs *) sbus_ioremap(&r, 0, - PAGE_SIZE*SUN4M_NCPUS, "sun4m_cpu_cnt"); - /* Map the system Counter register. */ - /* XXX Here we expect consequent calls to yeld adjusent maps. */ - r.flags = cnt_regs[4].which_io; - r.start = cnt_regs[4].phys_addr; - sbus_ioremap(&r, 0, cnt_regs[4].reg_size, "sun4m_sys_cnt"); - - sun4m_timers->l10_timer_limit = (((1000000/HZ) + 1) << 10); - master_l10_counter = &sun4m_timers->l10_cur_count; - master_l10_limit = &sun4m_timers->l10_timer_limit; - - irq = request_irq(TIMER_IRQ, - counter_fn, - (IRQF_DISABLED | SA_STATIC_ALLOC), - "timer", NULL); - if (irq) { - prom_printf("time_init: unable to attach IRQ%d\n",TIMER_IRQ); - prom_halt(); + num_cpu_timers = (len / sizeof(u32)) - 1; + for (i = 0; i < num_cpu_timers; i++) { + timers_percpu[i] = (void __iomem *) + (unsigned long) addr[i]; } - - if (!cpu_find_by_instance(1, NULL, NULL)) { - for(cpu = 0; cpu < 4; cpu++) - sun4m_timers->cpu_timers[cpu].l14_timer_limit = 0; - sun4m_interrupts->set = SUN4M_INT_E14; - } else { - sun4m_timers->cpu_timers[0].l14_timer_limit = 0; + timers_global = (void __iomem *) + (unsigned long) addr[num_cpu_timers]; + + sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); + + master_l10_counter = &timers_global->l10_count; + + err = request_irq(TIMER_IRQ, counter_fn, + (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL); + if (err) { + printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", + err); + return; } + + for (i = 0; i < num_cpu_timers; i++) + sbus_writel(0, &timers_percpu[i]->l14_limit); + if (num_cpu_timers == 4) + sbus_writel(SUN4M_INT_E14, &sun4m_irq_global->mask_set); + #ifdef CONFIG_SMP { unsigned long flags; @@ -390,70 +427,43 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) void __init sun4m_init_IRQ(void) { - int ie_node,i; - struct linux_prom_registers int_regs[PROMREG_MAX]; - int num_regs; - struct resource r; - int mid; - - local_irq_disable(); - if((ie_node = prom_searchsiblings(prom_getchild(prom_root_node), "obio")) == 0 || - (ie_node = prom_getchild (ie_node)) == 0 || - (ie_node = prom_searchsiblings (ie_node, "interrupt")) == 0) { - prom_printf("Cannot find /obio/interrupt node\n"); - prom_halt(); + struct device_node *dp = of_find_node_by_name(NULL, "interrupt"); + int len, i, mid, num_cpu_iregs; + const u32 *addr; + + if (!dp) { + printk(KERN_ERR "sun4m_init_IRQ: No 'interrupt' node.\n"); + return; } - num_regs = prom_getproperty(ie_node, "reg", (char *) int_regs, - sizeof(int_regs)); - num_regs = (num_regs/sizeof(struct linux_prom_registers)); - - /* Apply the obio ranges to these registers. */ - prom_apply_obio_ranges(int_regs, num_regs); - - int_regs[4].phys_addr = int_regs[num_regs-1].phys_addr; - int_regs[4].reg_size = int_regs[num_regs-1].reg_size; - int_regs[4].which_io = int_regs[num_regs-1].which_io; - for(ie_node = 1; ie_node < 4; ie_node++) { - int_regs[ie_node].phys_addr = int_regs[ie_node-1].phys_addr + PAGE_SIZE; - int_regs[ie_node].reg_size = int_regs[ie_node-1].reg_size; - int_regs[ie_node].which_io = int_regs[ie_node-1].which_io; + + addr = of_get_property(dp, "address", &len); + if (!addr) { + printk(KERN_ERR "sun4m_init_IRQ: No 'address' prop.\n"); + return; } - memset((char *)&r, 0, sizeof(struct resource)); - /* Map the interrupt registers for all possible cpus. */ - r.flags = int_regs[0].which_io; - r.start = int_regs[0].phys_addr; - sun4m_interrupts = (struct sun4m_intregs *) sbus_ioremap(&r, 0, - PAGE_SIZE*SUN4M_NCPUS, "interrupts_percpu"); + num_cpu_iregs = (len / sizeof(u32)) - 1; + for (i = 0; i < num_cpu_iregs; i++) { + sun4m_irq_percpu[i] = (void __iomem *) + (unsigned long) addr[i]; + } + sun4m_irq_global = (void __iomem *) + (unsigned long) addr[num_cpu_iregs]; - /* Map the system interrupt control registers. */ - r.flags = int_regs[4].which_io; - r.start = int_regs[4].phys_addr; - sbus_ioremap(&r, 0, int_regs[4].reg_size, "interrupts_system"); + local_irq_disable(); - sun4m_interrupts->set = ~SUN4M_INT_MASKALL; + sbus_writel(~SUN4M_INT_MASKALL, &sun4m_irq_global->mask_set); for (i = 0; !cpu_find_by_instance(i, NULL, &mid); i++) - sun4m_interrupts->cpu_intregs[mid].clear = ~0x17fff; - - if (!cpu_find_by_instance(1, NULL, NULL)) { - /* system wide interrupts go to cpu 0, this should always - * be safe because it is guaranteed to be fitted or OBP doesn't - * come up - * - * Not sure, but writing here on SLAVIO systems may puke - * so I don't do it unless there is more than 1 cpu. - */ - irq_rcvreg = (unsigned long *) - &sun4m_interrupts->undirected_target; - sun4m_interrupts->undirected_target = 0; - } - BTFIXUPSET_CALL(sbint_to_irq, sun4m_sbint_to_irq, BTFIXUPCALL_NORM); + sbus_writel(~0x17fff, &sun4m_irq_percpu[mid]->clear); + + if (num_cpu_iregs == 4) + sbus_writel(0, &sun4m_irq_global->interrupt_target); + BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(clear_profile_irq, sun4m_clear_profile_irq, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM); sparc_init_timers = sun4m_init_timers; #ifdef CONFIG_SMP @@ -461,5 +471,6 @@ void __init sun4m_init_IRQ(void) BTFIXUPSET_CALL(clear_cpu_int, sun4m_clear_ipi, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(set_irq_udt, sun4m_set_udt, BTFIXUPCALL_NORM); #endif + /* Cannot enable interrupts until OBP ticker is disabled. */ } diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 9964890dc1d..5fc386d08c4 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -17,6 +17,7 @@ #include <linux/swap.h> #include <linux/profile.h> #include <linux/delay.h> +#include <linux/cpu.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> @@ -315,6 +316,8 @@ void smp4m_cross_call_irq(void) ccall_info.processors_out[i] = 1; } +extern void sun4m_clear_profile_irq(int cpu); + void smp4m_percpu_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs; @@ -322,7 +325,7 @@ void smp4m_percpu_timer_interrupt(struct pt_regs *regs) old_regs = set_irq_regs(regs); - clear_profile_irq(cpu); + sun4m_clear_profile_irq(cpu); profile_tick(CPU_PROFILING); diff --git a/arch/sparc/kernel/sun4setup.c b/arch/sparc/kernel/sun4setup.c deleted file mode 100644 index 229a52f55f1..00000000000 --- a/arch/sparc/kernel/sun4setup.c +++ /dev/null @@ -1,75 +0,0 @@ -/* sun4setup.c: Setup the hardware address of various items in the sun4 - * architecture. Called from idprom_init - * - * Copyright (C) 1998 Chris G. Davis (cdavis@cois.on.ca) - */ - -#include <asm/page.h> -#include <asm/oplib.h> -#include <asm/idprom.h> -#include <asm/sun4paddr.h> -#include <asm/machines.h> - -int sun4_memreg_physaddr; -int sun4_ie_physaddr; -int sun4_clock_physaddr; -int sun4_timer_physaddr; -int sun4_eth_physaddr; -int sun4_si_physaddr; -int sun4_bwtwo_physaddr; -int sun4_zs0_physaddr; -int sun4_zs1_physaddr; -int sun4_dma_physaddr; -int sun4_esp_physaddr; -int sun4_ie_physaddr; - -void __init sun4setup(void) -{ - printk("Sun4 Hardware Setup v1.0 18/May/98 Chris Davis (cdavis@cois.on.ca). "); - /* - setup standard sun4 info - */ - sun4_ie_physaddr=SUN4_IE_PHYSADDR; - - /* - setup model specific info - */ - switch(idprom->id_machtype) { - case (SM_SUN4 | SM_4_260 ): - printk("Setup for a SUN4/260\n"); - sun4_memreg_physaddr=SUN4_200_MEMREG_PHYSADDR; - sun4_clock_physaddr=SUN4_200_CLOCK_PHYSADDR; - sun4_timer_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_eth_physaddr=SUN4_200_ETH_PHYSADDR; - sun4_si_physaddr=SUN4_200_SI_PHYSADDR; - sun4_bwtwo_physaddr=SUN4_200_BWTWO_PHYSADDR; - sun4_dma_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_esp_physaddr=SUN4_UNUSED_PHYSADDR; - break; - case (SM_SUN4 | SM_4_330 ): - printk("Setup for a SUN4/330\n"); - sun4_memreg_physaddr=SUN4_300_MEMREG_PHYSADDR; - sun4_clock_physaddr=SUN4_300_CLOCK_PHYSADDR; - sun4_timer_physaddr=SUN4_300_TIMER_PHYSADDR; - sun4_eth_physaddr=SUN4_300_ETH_PHYSADDR; - sun4_si_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_bwtwo_physaddr=SUN4_300_BWTWO_PHYSADDR; - sun4_dma_physaddr=SUN4_300_DMA_PHYSADDR; - sun4_esp_physaddr=SUN4_300_ESP_PHYSADDR; - break; - case (SM_SUN4 | SM_4_470 ): - printk("Setup for a SUN4/470\n"); - sun4_memreg_physaddr=SUN4_400_MEMREG_PHYSADDR; - sun4_clock_physaddr=SUN4_400_CLOCK_PHYSADDR; - sun4_timer_physaddr=SUN4_400_TIMER_PHYSADDR; - sun4_eth_physaddr=SUN4_400_ETH_PHYSADDR; - sun4_si_physaddr=SUN4_UNUSED_PHYSADDR; - sun4_bwtwo_physaddr=SUN4_400_BWTWO_PHYSADDR; - sun4_dma_physaddr=SUN4_400_DMA_PHYSADDR; - sun4_esp_physaddr=SUN4_400_ESP_PHYSADDR; - break; - default: - ; - } -} - diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index 4d73421559c..03035c852a4 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c @@ -53,7 +53,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi /* See asm-sparc/uaccess.h */ if (len > TASK_SIZE - PAGE_SIZE) return -ENOMEM; - if (ARCH_SUN4C_SUN4 && len > 0x20000000) + if (ARCH_SUN4C && len > 0x20000000) return -ENOMEM; if (!addr) addr = TASK_UNMAPPED_BASE; @@ -65,7 +65,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) { /* At this point: (!vmm || addr < vmm->vm_end). */ - if (ARCH_SUN4C_SUN4 && addr < 0xe0000000 && 0x20000000 - len < addr) { + if (ARCH_SUN4C && addr < 0xe0000000 && 0x20000000 - len < addr) { addr = PAGE_OFFSET; vmm = find_vma(current->mm, PAGE_OFFSET); } @@ -81,7 +81,7 @@ unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsi asmlinkage unsigned long sparc_brk(unsigned long brk) { - if(ARCH_SUN4C_SUN4) { + if(ARCH_SUN4C) { if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000)) return current->mm->brk; } @@ -221,7 +221,7 @@ out: int sparc_mmap_check(unsigned long addr, unsigned long len) { - if (ARCH_SUN4C_SUN4 && + if (ARCH_SUN4C && (len > 0x20000000 || (addr < 0xe0000000 && addr + len > 0x20000000))) return -EINVAL; diff --git a/arch/sparc/kernel/tick14.c b/arch/sparc/kernel/tick14.c index 707bfda8657..138bbf5f872 100644 --- a/arch/sparc/kernel/tick14.c +++ b/arch/sparc/kernel/tick14.c @@ -1,31 +1,12 @@ /* tick14.c - * linux/arch/sparc/kernel/tick14.c * * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk) * * This file handles the Sparc specific level14 ticker * This is really useful for profiling OBP uses it for keyboard * aborts and other stuff. - * - * */ -#include <linux/errno.h> -#include <linux/sched.h> #include <linux/kernel.h> -#include <linux/param.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/timex.h> -#include <linux/interrupt.h> - -#include <asm/oplib.h> -#include <asm/timer.h> -#include <asm/mostek.h> -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/io.h> - -#include "irq.h" extern unsigned long lvl14_save[5]; static unsigned long *linux_lvl14 = NULL; @@ -56,31 +37,3 @@ void install_obp_ticker(void) linux_lvl14[2] = obp_lvl14[2]; linux_lvl14[3] = obp_lvl14[3]; } - -void claim_ticker14(irq_handler_t handler, - int irq_nr, unsigned int timeout ) -{ - int cpu = smp_processor_id(); - - /* first we copy the obp handler instructions - */ - __disable_irq(irq_nr); - if (!handler) - return; - - linux_lvl14 = (unsigned long *)lvl14_save[4]; - obp_lvl14[0] = linux_lvl14[0]; - obp_lvl14[1] = linux_lvl14[1]; - obp_lvl14[2] = linux_lvl14[2]; - obp_lvl14[3] = linux_lvl14[3]; - - if (!request_irq(irq_nr, - handler, - (IRQF_DISABLED | SA_STATIC_ALLOC), - "counter14", - NULL)) { - install_linux_ticker(); - load_profile_irq(cpu, timeout); - __enable_irq(irq_nr); - } -} diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c index 0762f5db192..62c1d94cb43 100644 --- a/arch/sparc/kernel/time.c +++ b/arch/sparc/kernel/time.c @@ -23,22 +23,24 @@ #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/time.h> +#include <linux/rtc.h> +#include <linux/rtc/m48t59.h> #include <linux/timex.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/ioport.h> #include <linux/profile.h> +#include <linux/of.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <asm/oplib.h> #include <asm/timer.h> -#include <asm/mostek.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/idprom.h> #include <asm/machines.h> -#include <asm/sun4paddr.h> #include <asm/page.h> #include <asm/pcic.h> #include <asm/irq_regs.h> @@ -46,34 +48,9 @@ #include "irq.h" DEFINE_SPINLOCK(rtc_lock); -static enum sparc_clock_type sp_clock_typ; -DEFINE_SPINLOCK(mostek_lock); -void __iomem *mstk48t02_regs = NULL; -static struct mostek48t08 __iomem *mstk48t08_regs = NULL; static int set_rtc_mmss(unsigned long); static int sbus_do_settimeofday(struct timespec *tv); -#ifdef CONFIG_SUN4 -struct intersil *intersil_clock; -#define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \ - (intsil_cmd) - -#define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \ - (intsil_cmd) - -#define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \ - ( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ - INTERSIL_INTR_ENABLE)) - -#define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \ - ( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ - INTERSIL_INTR_ENABLE)) - -#define intersil_read_intr(intersil_reg, towhere) towhere = \ - intersil_reg->int_intr_reg - -#endif - unsigned long profile_pc(struct pt_regs *regs) { extern char __copy_user_begin[], __copy_user_end[]; @@ -96,7 +73,6 @@ unsigned long profile_pc(struct pt_regs *regs) EXPORT_SYMBOL(profile_pc); __volatile__ unsigned int *master_l10_counter; -__volatile__ unsigned int *master_l10_limit; /* * timer_interrupt() needs to keep up the real-time clock, @@ -116,15 +92,7 @@ static irqreturn_t timer_interrupt(int dummy, void *dev_id) /* Protect counter clear so that do_gettimeoffset works */ write_seqlock(&xtime_lock); -#ifdef CONFIG_SUN4 - if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { - int temp; - intersil_read_intr(intersil_clock, temp); - /* re-enable the irq */ - enable_pil_irq(10); - } -#endif + clear_clock_irq(); do_timer(1); @@ -147,157 +115,56 @@ static irqreturn_t timer_interrupt(int dummy, void *dev_id) return IRQ_HANDLED; } -/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ -static void __devinit kick_start_clock(void) +static unsigned char mostek_read_byte(struct device *dev, u32 ofs) { - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned char sec; - int i, count; - - prom_printf("CLOCK: Clock was stopped. Kick start "); - - spin_lock_irq(&mostek_lock); - - /* Turn on the kick start bit to start the oscillator. */ - regs->creg |= MSTK_CREG_WRITE; - regs->sec &= ~MSTK_STOP; - regs->hour |= MSTK_KICK_START; - regs->creg &= ~MSTK_CREG_WRITE; - - spin_unlock_irq(&mostek_lock); - - /* Delay to allow the clock oscillator to start. */ - sec = MSTK_REG_SEC(regs); - for (i = 0; i < 3; i++) { - while (sec == MSTK_REG_SEC(regs)) - for (count = 0; count < 100000; count++) - /* nothing */ ; - prom_printf("."); - sec = regs->sec; - } - prom_printf("\n"); - - spin_lock_irq(&mostek_lock); - - /* Turn off kick start and set a "valid" time and date. */ - regs->creg |= MSTK_CREG_WRITE; - regs->hour &= ~MSTK_KICK_START; - MSTK_SET_REG_SEC(regs,0); - MSTK_SET_REG_MIN(regs,0); - MSTK_SET_REG_HOUR(regs,0); - MSTK_SET_REG_DOW(regs,5); - MSTK_SET_REG_DOM(regs,1); - MSTK_SET_REG_MONTH(regs,8); - MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); - regs->creg &= ~MSTK_CREG_WRITE; - - spin_unlock_irq(&mostek_lock); - - /* Ensure the kick start bit is off. If it isn't, turn it off. */ - while (regs->hour & MSTK_KICK_START) { - prom_printf("CLOCK: Kick start still on!\n"); - - spin_lock_irq(&mostek_lock); - regs->creg |= MSTK_CREG_WRITE; - regs->hour &= ~MSTK_KICK_START; - regs->creg &= ~MSTK_CREG_WRITE; - spin_unlock_irq(&mostek_lock); + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs = pdata->ioaddr; + unsigned char val = readb(regs + ofs); + + /* the year 0 is 1968 */ + if (ofs == pdata->offset + M48T59_YEAR) { + val += 0x68; + if ((val & 0xf) > 9) + val += 6; } - - prom_printf("CLOCK: Kick start procedure successful.\n"); -} - -/* Return nonzero if the clock chip battery is low. */ -static inline int has_low_battery(void) -{ - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned char data1, data2; - - spin_lock_irq(&mostek_lock); - data1 = regs->eeprom[0]; /* Read some data. */ - regs->eeprom[0] = ~data1; /* Write back the complement. */ - data2 = regs->eeprom[0]; /* Read back the complement. */ - regs->eeprom[0] = data1; /* Restore the original value. */ - spin_unlock_irq(&mostek_lock); - - return (data1 == data2); /* Was the write blocked? */ + return val; } -static void __devinit mostek_set_system_time(void) +static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) { - unsigned int year, mon, day, hour, min, sec; - struct mostek48t02 *mregs; - - mregs = (struct mostek48t02 *)mstk48t02_regs; - if(!mregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - spin_lock_irq(&mostek_lock); - mregs->creg |= MSTK_CREG_READ; - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - mregs->creg &= ~MSTK_CREG_READ; - spin_unlock_irq(&mostek_lock); + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs = pdata->ioaddr; + + if (ofs == pdata->offset + M48T59_YEAR) { + if (val < 0x68) + val += 0x32; + else + val -= 0x68; + if ((val & 0xf) > 9) + val += 6; + if ((val & 0xf0) > 0x9A) + val += 0x60; + } + writeb(val, regs + ofs); } -/* Probe for the real time clock chip on Sun4 */ -static inline void sun4_clock_probe(void) -{ -#ifdef CONFIG_SUN4 - int temp; - struct resource r; - - memset(&r, 0, sizeof(r)); - if( idprom->id_machtype == (SM_SUN4 | SM_4_330) ) { - sp_clock_typ = MSTK48T02; - r.start = sun4_clock_physaddr; - mstk48t02_regs = sbus_ioremap(&r, 0, - sizeof(struct mostek48t02), NULL); - mstk48t08_regs = NULL; /* To catch weirdness */ - intersil_clock = NULL; /* just in case */ - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - } else if( idprom->id_machtype == (SM_SUN4 | SM_4_260)) { - /* intersil setup code */ - printk("Clock: INTERSIL at %8x ",sun4_clock_physaddr); - sp_clock_typ = INTERSIL; - r.start = sun4_clock_physaddr; - intersil_clock = (struct intersil *) - sbus_ioremap(&r, 0, sizeof(*intersil_clock), "intersil"); - mstk48t02_regs = 0; /* just be sure */ - mstk48t08_regs = NULL; /* ditto */ - /* initialise the clock */ - - intersil_intr(intersil_clock,INTERSIL_INT_100HZ); - - intersil_start(intersil_clock); - - intersil_read_intr(intersil_clock, temp); - while (!(temp & 0x80)) - intersil_read_intr(intersil_clock, temp); - - intersil_read_intr(intersil_clock, temp); - while (!(temp & 0x80)) - intersil_read_intr(intersil_clock, temp); - - intersil_stop(intersil_clock); +static struct m48t59_plat_data m48t59_data = { + .read_byte = mostek_read_byte, + .write_byte = mostek_write_byte, +}; - } -#endif -} +/* resource is set at runtime */ +static struct platform_device m48t59_rtc = { + .name = "rtc-m48t59", + .id = 0, + .num_resources = 1, + .dev = { + .platform_data = &m48t59_data, + }, +}; -#ifndef CONFIG_SUN4 static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dp = op->node; @@ -306,38 +173,26 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id if (!model) return -ENODEV; + m48t59_rtc.resource = &op->resource[0]; if (!strcmp(model, "mk48t02")) { - sp_clock_typ = MSTK48T02; - /* Map the clock register io area read-only */ - mstk48t02_regs = of_ioremap(&op->resource[0], 0, - sizeof(struct mostek48t02), - "mk48t02"); - mstk48t08_regs = NULL; /* To catch weirdness */ + m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0, + 2048, "rtc-m48t59"); + m48t59_data.type = M48T59RTC_TYPE_M48T02; } else if (!strcmp(model, "mk48t08")) { - sp_clock_typ = MSTK48T08; - mstk48t08_regs = of_ioremap(&op->resource[0], 0, - sizeof(struct mostek48t08), - "mk48t08"); - - mstk48t02_regs = &mstk48t08_regs->regs; + m48t59_data.ioaddr = of_ioremap(&op->resource[0], 0, + 8192, "rtc-m48t59"); + m48t59_data.type = M48T59RTC_TYPE_M48T08; } else return -ENODEV; - /* Report a low battery voltage condition. */ - if (has_low_battery()) - printk(KERN_CRIT "NVRAM: Low battery voltage!\n"); - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - - mostek_set_system_time(); + if (platform_device_register(&m48t59_rtc) < 0) + printk(KERN_ERR "Registering RTC device failed\n"); return 0; } -static struct of_device_id clock_match[] = { +static struct of_device_id __initdata clock_match[] = { { .name = "eeprom", }, @@ -348,7 +203,7 @@ static struct of_platform_driver clock_driver = { .match_table = clock_match, .probe = clock_probe, .driver = { - .name = "clock", + .name = "rtc", }, }; @@ -364,7 +219,6 @@ static int __init clock_init(void) * need to see the clock registers. */ fs_initcall(clock_init); -#endif /* !CONFIG_SUN4 */ static void __init sbus_time_init(void) { @@ -372,51 +226,8 @@ static void __init sbus_time_init(void) BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM); btfixup(); - if (ARCH_SUN4) - sun4_clock_probe(); - sparc_init_timers(timer_interrupt); -#ifdef CONFIG_SUN4 - if(idprom->id_machtype == (SM_SUN4 | SM_4_330)) { - mostek_set_system_time(); - } else if(idprom->id_machtype == (SM_SUN4 | SM_4_260) ) { - /* initialise the intersil on sun4 */ - unsigned int year, mon, day, hour, min, sec; - int temp; - struct intersil *iregs; - - iregs=intersil_clock; - if(!iregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - - intersil_intr(intersil_clock,INTERSIL_INT_100HZ); - disable_pil_irq(10); - intersil_stop(iregs); - intersil_read_intr(intersil_clock, temp); - - temp = iregs->clk.int_csec; - - sec = iregs->clk.int_sec; - min = iregs->clk.int_min; - hour = iregs->clk.int_hour; - day = iregs->clk.int_day; - mon = iregs->clk.int_month; - year = MSTK_CVT_YEAR(iregs->clk.int_year); - - enable_pil_irq(10); - intersil_start(iregs); - - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec); - } -#endif - /* Now that OBP ticker has been silenced, it is safe to enable IRQ. */ local_irq_enable(); } @@ -522,80 +333,15 @@ static int sbus_do_settimeofday(struct timespec *tv) return 0; } -/* - * BUG: This routine does not handle hour overflow properly; it just - * sets the minutes. Usually you won't notice until after reboot! - */ -static int set_rtc_mmss(unsigned long nowtime) +static int set_rtc_mmss(unsigned long secs) { - int real_seconds, real_minutes, mostek_minutes; - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned long flags; -#ifdef CONFIG_SUN4 - struct intersil *iregs = intersil_clock; - int temp; -#endif + struct rtc_device *rtc = rtc_class_open("rtc0"); + int err = -1; - /* Not having a register set can lead to trouble. */ - if (!regs) { -#ifdef CONFIG_SUN4 - if(!iregs) - return -1; - else { - temp = iregs->clk.int_csec; - - mostek_minutes = iregs->clk.int_min; - - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - mostek_minutes) < 30) { - intersil_stop(iregs); - iregs->clk.int_sec=real_seconds; - iregs->clk.int_min=real_minutes; - intersil_start(iregs); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - mostek_minutes, real_minutes); - return -1; - } - - return 0; - } -#endif + if (rtc) { + err = rtc_set_mmss(rtc, secs); + rtc_class_close(rtc); } - spin_lock_irqsave(&mostek_lock, flags); - /* Read the current RTC minutes. */ - regs->creg |= MSTK_CREG_READ; - mostek_minutes = MSTK_REG_MIN(regs); - regs->creg &= ~MSTK_CREG_READ; - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - mostek_minutes) < 30) { - regs->creg |= MSTK_CREG_WRITE; - MSTK_SET_REG_SEC(regs,real_seconds); - MSTK_SET_REG_MIN(regs,real_minutes); - regs->creg &= ~MSTK_CREG_WRITE; - spin_unlock_irqrestore(&mostek_lock, flags); - return 0; - } else { - spin_unlock_irqrestore(&mostek_lock, flags); - return -1; - } + return err; } diff --git a/arch/sparc/kernel/traps.c b/arch/sparc/kernel/traps.c index 5d45d5fd8c9..2b7d5065903 100644 --- a/arch/sparc/kernel/traps.c +++ b/arch/sparc/kernel/traps.c @@ -43,23 +43,6 @@ void syscall_trace_exit(struct pt_regs *regs) { } -void sun4m_nmi(struct pt_regs *regs) -{ - unsigned long afsr, afar; - - printk("Aieee: sun4m NMI received!\n"); - /* XXX HyperSparc hack XXX */ - __asm__ __volatile__("mov 0x500, %%g1\n\t" - "lda [%%g1] 0x4, %0\n\t" - "mov 0x600, %%g1\n\t" - "lda [%%g1] 0x4, %1\n\t" : - "=r" (afsr), "=r" (afar)); - printk("afsr=%08lx afar=%08lx\n", afsr, afar); - printk("you lose buddy boy...\n"); - show_regs(regs); - prom_halt(); -} - void sun4d_nmi(struct pt_regs *regs) { printk("Aieee: sun4d NMI received!\n"); diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index 109c8b22cb3..ea88955d97f 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -3,13 +3,8 @@ EXTRA_AFLAGS := -ansi -obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o - -ifeq ($(CONFIG_SUN4),y) -obj-y += nosrmmu.o -else -obj-y += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o -endif +obj-y := fault.o init.o loadmmu.o generic.o extable.o btfixup.o \ + srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o ifdef CONFIG_HIGHMEM obj-y += highmem.o diff --git a/arch/sparc/mm/btfixup.c b/arch/sparc/mm/btfixup.c index a312d127d47..5175ac2f482 100644 --- a/arch/sparc/mm/btfixup.c +++ b/arch/sparc/mm/btfixup.c @@ -20,11 +20,7 @@ extern char *srmmu_name; static char version[] __initdata = "Boot time fixup v1.6. 4/Mar/98 Jakub Jelinek (jj@ultra.linux.cz). Patching kernel for "; -#ifdef CONFIG_SUN4 -static char str_sun4c[] __initdata = "sun4\n"; -#else static char str_sun4c[] __initdata = "sun4c\n"; -#endif static char str_srmmu[] __initdata = "srmmu[%s]/"; static char str_iommu[] __initdata = "iommu\n"; static char str_iounit[] __initdata = "io-unit\n"; @@ -86,7 +82,7 @@ void __init btfixup(void) if (!visited) { visited++; printk(version); - if (ARCH_SUN4C_SUN4) + if (ARCH_SUN4C) printk(str_sun4c); else { printk(str_srmmu, srmmu_name); diff --git a/arch/sparc/mm/fault.c b/arch/sparc/mm/fault.c index 3604c2e8670..a507e117466 100644 --- a/arch/sparc/mm/fault.c +++ b/arch/sparc/mm/fault.c @@ -191,7 +191,7 @@ asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, * only copy the information from the master page table, * nothing more. */ - if (!ARCH_SUN4C_SUN4 && address >= TASK_SIZE) + if (!ARCH_SUN4C && address >= TASK_SIZE) goto vmalloc_fault; info.si_code = SEGV_MAPERR; diff --git a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c index e103f1bb377..677c1e187a2 100644 --- a/arch/sparc/mm/init.c +++ b/arch/sparc/mm/init.c @@ -23,6 +23,7 @@ #include <linux/highmem.h> #include <linux/bootmem.h> #include <linux/pagemap.h> +#include <linux/poison.h> #include <asm/system.h> #include <asm/vac-ops.h> @@ -480,6 +481,7 @@ void free_initmem (void) for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { struct page *p; + memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); p = virt_to_page(addr); ClearPageReserved(p); @@ -488,20 +490,26 @@ void free_initmem (void) totalram_pages++; num_physpages++; } - printk (KERN_INFO "Freeing unused kernel memory: %dk freed\n", (&__init_end - &__init_begin) >> 10); + printk(KERN_INFO "Freeing unused kernel memory: %dk freed\n", + (&__init_end - &__init_begin) >> 10); } #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { if (start < end) - printk (KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", + (end - start) >> 10); for (; start < end; start += PAGE_SIZE) { - struct page *p = virt_to_page(start); + struct page *p; + + memset((void *)start, POISON_FREE_INITMEM, PAGE_SIZE); + p = virt_to_page(start); ClearPageReserved(p); init_page_count(p); __free_page(p); + totalram_pages++; num_physpages++; } } diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c index f167835db3d..daadf5f8805 100644 --- a/arch/sparc/mm/io-unit.c +++ b/arch/sparc/mm/io-unit.c @@ -12,10 +12,11 @@ #include <linux/highmem.h> /* pte_offset_map => kmap_atomic */ #include <linux/bitops.h> #include <linux/scatterlist.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/io.h> #include <asm/io-unit.h> #include <asm/mxcc.h> @@ -34,13 +35,10 @@ #define IOPERM (IOUPTE_CACHE | IOUPTE_WRITE | IOUPTE_VALID) #define MKIOPTE(phys) __iopte((((phys)>>4) & IOUPTE_PAGE) | IOPERM) -void __init -iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus) +static void __init iounit_iommu_init(struct of_device *op) { - iopte_t *xpt, *xptend; struct iounit_struct *iounit; - struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - struct resource r; + iopte_t *xpt, *xptend; iounit = kzalloc(sizeof(struct iounit_struct), GFP_ATOMIC); if (!iounit) { @@ -55,18 +53,13 @@ iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus) iounit->rotor[1] = IOUNIT_BMAP2_START; iounit->rotor[2] = IOUNIT_BMAPM_START; - xpt = NULL; - if(prom_getproperty(sbi_node, "reg", (void *) iommu_promregs, - sizeof(iommu_promregs)) != -1) { - prom_apply_generic_ranges(io_node, 0, iommu_promregs, 3); - memset(&r, 0, sizeof(r)); - r.flags = iommu_promregs[2].which_io; - r.start = iommu_promregs[2].phys_addr; - xpt = (iopte_t *) sbus_ioremap(&r, 0, PAGE_SIZE * 16, "XPT"); + xpt = of_ioremap(&op->resource[2], 0, PAGE_SIZE * 16, "XPT"); + if (!xpt) { + prom_printf("SUN4D: Cannot map External Page Table."); + prom_halt(); } - if(!xpt) panic("Cannot map External Page Table."); - sbus->ofdev.dev.archdata.iommu = iounit; + op->dev.archdata.iommu = iounit; iounit->page_table = xpt; spin_lock_init(&iounit->lock); @@ -75,6 +68,25 @@ iounit_init(int sbi_node, int io_node, struct sbus_bus *sbus) iopte_val(*xpt++) = 0; } +static int __init iounit_init(void) +{ + extern void sun4d_init_sbi_irq(void); + struct device_node *dp; + + for_each_node_by_name(dp, "sbi") { + struct of_device *op = of_find_device_by_node(dp); + + iounit_iommu_init(op); + of_propagate_archdata(op); + } + + sun4d_init_sbi_irq(); + + return 0; +} + +subsys_initcall(iounit_init); + /* One has to hold iounit->lock to call this */ static unsigned long iounit_get_area(struct iounit_struct *iounit, unsigned long vaddr, int size) { @@ -124,10 +136,10 @@ nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan); return vaddr; } -static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iounit_get_scsi_one(struct device *dev, char *vaddr, unsigned long len) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long ret, flags; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; spin_lock_irqsave(&iounit->lock, flags); ret = iounit_get_area(iounit, (unsigned long)vaddr, len); @@ -135,10 +147,10 @@ static __u32 iounit_get_scsi_one(char *vaddr, unsigned long len, struct sbus_bus return ret; } -static void iounit_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iounit_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long flags; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; /* FIXME: Cache some resolved pages - often several sg entries are to the same page */ spin_lock_irqsave(&iounit->lock, flags); @@ -151,10 +163,10 @@ static void iounit_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus spin_unlock_irqrestore(&iounit->lock, flags); } -static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus) +static void iounit_release_scsi_one(struct device *dev, __u32 vaddr, unsigned long len) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long flags; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; spin_lock_irqsave(&iounit->lock, flags); len = ((vaddr & ~PAGE_MASK) + len + (PAGE_SIZE-1)) >> PAGE_SHIFT; @@ -165,11 +177,11 @@ static void iounit_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_ spin_unlock_irqrestore(&iounit->lock, flags); } -static void iounit_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iounit_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long flags; unsigned long vaddr, len; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; spin_lock_irqsave(&iounit->lock, flags); while (sz != 0) { @@ -185,12 +197,12 @@ static void iounit_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_ } #ifdef CONFIG_SBUS -static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, int len) +static int iounit_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, __u32 addr, int len) { + struct iounit_struct *iounit = dev->archdata.iommu; unsigned long page, end; pgprot_t dvma_prot; iopte_t *iopte; - struct sbus_bus *sbus; *pba = addr; @@ -212,12 +224,8 @@ static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, in i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT); - for_each_sbus(sbus) { - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - - iopte = (iopte_t *)(iounit->page_table + i); - *iopte = MKIOPTE(__pa(page)); - } + iopte = (iopte_t *)(iounit->page_table + i); + *iopte = MKIOPTE(__pa(page)); } addr += PAGE_SIZE; va += PAGE_SIZE; @@ -228,23 +236,10 @@ static int iounit_map_dma_area(dma_addr_t *pba, unsigned long va, __u32 addr, in return 0; } -static void iounit_unmap_dma_area(unsigned long addr, int len) +static void iounit_unmap_dma_area(struct device *dev, unsigned long addr, int len) { /* XXX Somebody please fill this in */ } - -/* XXX We do not pass sbus device here, bad. */ -static struct page *iounit_translate_dvma(unsigned long addr) -{ - struct sbus_bus *sbus = sbus_root; /* They are all the same */ - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - int i; - iopte_t *iopte; - - i = ((addr - IOUNIT_DMA_BASE) >> PAGE_SHIFT); - iopte = (iopte_t *)(iounit->page_table + i); - return pfn_to_page(iopte_val(*iopte) >> (PAGE_SHIFT-4)); /* XXX sun4d guru, help */ -} #endif static char *iounit_lockarea(char *vaddr, unsigned long len) @@ -271,54 +266,5 @@ void __init ld_mmu_iounit(void) #ifdef CONFIG_SBUS BTFIXUPSET_CALL(mmu_map_dma_area, iounit_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, iounit_unmap_dma_area, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(mmu_translate_dvma, iounit_translate_dvma, BTFIXUPCALL_NORM); #endif } - -__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size) -{ - int i, j, k, npages; - unsigned long rotor, scan, limit; - unsigned long flags; - __u32 ret; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - - npages = (size + (PAGE_SIZE-1)) >> PAGE_SHIFT; - i = 0x0213; - spin_lock_irqsave(&iounit->lock, flags); -next: j = (i & 15); - rotor = iounit->rotor[j - 1]; - limit = iounit->limit[j]; - scan = rotor; -nexti: scan = find_next_zero_bit(iounit->bmap, limit, scan); - if (scan + npages > limit) { - if (limit != rotor) { - limit = rotor; - scan = iounit->limit[j - 1]; - goto nexti; - } - i >>= 4; - if (!(i & 15)) - panic("iounit_map_dma_init: Couldn't find free iopte slots for %d bytes\n", size); - goto next; - } - for (k = 1, scan++; k < npages; k++) - if (test_bit(scan++, iounit->bmap)) - goto nexti; - iounit->rotor[j - 1] = (scan < limit) ? scan : iounit->limit[j - 1]; - scan -= npages; - ret = IOUNIT_DMA_BASE + (scan << PAGE_SHIFT); - for (k = 0; k < npages; k++, scan++) - set_bit(scan, iounit->bmap); - spin_unlock_irqrestore(&iounit->lock, flags); - return ret; -} - -__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus) -{ - int scan = (vaddr - IOUNIT_DMA_BASE) >> PAGE_SHIFT; - struct iounit_struct *iounit = sbus->ofdev.dev.archdata.iommu; - - iounit->page_table[scan] = MKIOPTE(__pa(((unsigned long)addr) & PAGE_MASK)); - return vaddr + (((unsigned long)addr) & ~PAGE_MASK); -} diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index 4b934270f05..e7a499e3aa3 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -13,10 +13,11 @@ #include <linux/slab.h> #include <linux/highmem.h> /* pte_offset_map => kmap_atomic */ #include <linux/scatterlist.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/io.h> #include <asm/mxcc.h> #include <asm/mbus.h> @@ -55,30 +56,21 @@ static pgprot_t dvma_prot; /* Consistent mapping pte flags */ #define IOPERM (IOPTE_CACHE | IOPTE_WRITE | IOPTE_VALID) #define MKIOPTE(pfn, perm) (((((pfn)<<8) & IOPTE_PAGE) | (perm)) & ~IOPTE_WAZ) -void __init -iommu_init(int iommund, struct sbus_bus *sbus) +static void __init sbus_iommu_init(struct of_device *op) { - unsigned int impl, vers; - unsigned long tmp; struct iommu_struct *iommu; - struct linux_prom_registers iommu_promregs[PROMREG_MAX]; - struct resource r; + unsigned int impl, vers; unsigned long *bitmap; + unsigned long tmp; iommu = kmalloc(sizeof(struct iommu_struct), GFP_ATOMIC); if (!iommu) { prom_printf("Unable to allocate iommu structure\n"); prom_halt(); } - iommu->regs = NULL; - if (prom_getproperty(iommund, "reg", (void *) iommu_promregs, - sizeof(iommu_promregs)) != -1) { - memset(&r, 0, sizeof(r)); - r.flags = iommu_promregs[0].which_io; - r.start = iommu_promregs[0].phys_addr; - iommu->regs = (struct iommu_regs *) - sbus_ioremap(&r, 0, PAGE_SIZE * 3, "iommu_regs"); - } + + iommu->regs = of_ioremap(&op->resource[0], 0, PAGE_SIZE * 3, + "iommu_regs"); if (!iommu->regs) { prom_printf("Cannot map IOMMU registers\n"); prom_halt(); @@ -128,13 +120,29 @@ iommu_init(int iommund, struct sbus_bus *sbus) else iommu->usemap.num_colors = 1; - printk("IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n", - impl, vers, iommu->page_table, - (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES); + printk(KERN_INFO "IOMMU: impl %d vers %d table 0x%p[%d B] map [%d b]\n", + impl, vers, iommu->page_table, + (int)(IOMMU_NPTES*sizeof(iopte_t)), (int)IOMMU_NPTES); + + op->dev.archdata.iommu = iommu; +} + +static int __init iommu_init(void) +{ + struct device_node *dp; + + for_each_node_by_name(dp, "iommu") { + struct of_device *op = of_find_device_by_node(dp); + + sbus_iommu_init(op); + of_propagate_archdata(op); + } - sbus->ofdev.dev.archdata.iommu = iommu; + return 0; } +subsys_initcall(iommu_init); + /* This begs to be btfixup-ed by srmmu. */ /* Flush the iotlb entries to ram. */ /* This could be better if we didn't have to flush whole pages. */ @@ -164,9 +172,9 @@ static void iommu_flush_iotlb(iopte_t *iopte, unsigned int niopte) } } -static u32 iommu_get_one(struct page *page, int npages, struct sbus_bus *sbus) +static u32 iommu_get_one(struct device *dev, struct page *page, int npages) { - struct iommu_struct *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu_struct *iommu = dev->archdata.iommu; int ioptex; iopte_t *iopte, *iopte0; unsigned int busa, busa0; @@ -194,8 +202,7 @@ static u32 iommu_get_one(struct page *page, int npages, struct sbus_bus *sbus) return busa0; } -static u32 iommu_get_scsi_one(char *vaddr, unsigned int len, - struct sbus_bus *sbus) +static u32 iommu_get_scsi_one(struct device *dev, char *vaddr, unsigned int len) { unsigned long off; int npages; @@ -205,22 +212,22 @@ static u32 iommu_get_scsi_one(char *vaddr, unsigned int len, off = (unsigned long)vaddr & ~PAGE_MASK; npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT; page = virt_to_page((unsigned long)vaddr & PAGE_MASK); - busa = iommu_get_one(page, npages, sbus); + busa = iommu_get_one(dev, page, npages); return busa + off; } -static __u32 iommu_get_scsi_one_noflush(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iommu_get_scsi_one_noflush(struct device *dev, char *vaddr, unsigned long len) { - return iommu_get_scsi_one(vaddr, len, sbus); + return iommu_get_scsi_one(dev, vaddr, len); } -static __u32 iommu_get_scsi_one_gflush(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iommu_get_scsi_one_gflush(struct device *dev, char *vaddr, unsigned long len) { flush_page_for_dma(0); - return iommu_get_scsi_one(vaddr, len, sbus); + return iommu_get_scsi_one(dev, vaddr, len); } -static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct sbus_bus *sbus) +static __u32 iommu_get_scsi_one_pflush(struct device *dev, char *vaddr, unsigned long len) { unsigned long page = ((unsigned long) vaddr) & PAGE_MASK; @@ -228,23 +235,23 @@ static __u32 iommu_get_scsi_one_pflush(char *vaddr, unsigned long len, struct sb flush_page_for_dma(page); page += PAGE_SIZE; } - return iommu_get_scsi_one(vaddr, len, sbus); + return iommu_get_scsi_one(dev, vaddr, len); } -static void iommu_get_scsi_sgl_noflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_get_scsi_sgl_noflush(struct device *dev, struct scatterlist *sg, int sz) { int n; while (sz != 0) { --sz; n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT; - sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset; + sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset; sg->dvma_length = (__u32) sg->length; sg = sg_next(sg); } } -static void iommu_get_scsi_sgl_gflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_get_scsi_sgl_gflush(struct device *dev, struct scatterlist *sg, int sz) { int n; @@ -252,13 +259,13 @@ static void iommu_get_scsi_sgl_gflush(struct scatterlist *sg, int sz, struct sbu while (sz != 0) { --sz; n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT; - sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset; + sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset; sg->dvma_length = (__u32) sg->length; sg = sg_next(sg); } } -static void iommu_get_scsi_sgl_pflush(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_get_scsi_sgl_pflush(struct device *dev, struct scatterlist *sg, int sz) { unsigned long page, oldpage = 0; int n, i; @@ -283,15 +290,15 @@ static void iommu_get_scsi_sgl_pflush(struct scatterlist *sg, int sz, struct sbu } } - sg->dvma_address = iommu_get_one(sg_page(sg), n, sbus) + sg->offset; + sg->dvma_address = iommu_get_one(dev, sg_page(sg), n) + sg->offset; sg->dvma_length = (__u32) sg->length; sg = sg_next(sg); } } -static void iommu_release_one(u32 busa, int npages, struct sbus_bus *sbus) +static void iommu_release_one(struct device *dev, u32 busa, int npages) { - struct iommu_struct *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu_struct *iommu = dev->archdata.iommu; int ioptex; int i; @@ -305,17 +312,17 @@ static void iommu_release_one(u32 busa, int npages, struct sbus_bus *sbus) bit_map_clear(&iommu->usemap, ioptex, npages); } -static void iommu_release_scsi_one(__u32 vaddr, unsigned long len, struct sbus_bus *sbus) +static void iommu_release_scsi_one(struct device *dev, __u32 vaddr, unsigned long len) { unsigned long off; int npages; off = vaddr & ~PAGE_MASK; npages = (off + len + PAGE_SIZE-1) >> PAGE_SHIFT; - iommu_release_one(vaddr & PAGE_MASK, npages, sbus); + iommu_release_one(dev, vaddr & PAGE_MASK, npages); } -static void iommu_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void iommu_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { int n; @@ -323,18 +330,18 @@ static void iommu_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_b --sz; n = (sg->length + sg->offset + PAGE_SIZE-1) >> PAGE_SHIFT; - iommu_release_one(sg->dvma_address & PAGE_MASK, n, sbus); + iommu_release_one(dev, sg->dvma_address & PAGE_MASK, n); sg->dvma_address = 0x21212121; sg = sg_next(sg); } } #ifdef CONFIG_SBUS -static int iommu_map_dma_area(dma_addr_t *pba, unsigned long va, - unsigned long addr, int len) +static int iommu_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, + unsigned long addr, int len) { + struct iommu_struct *iommu = dev->archdata.iommu; unsigned long page, end; - struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu; iopte_t *iopte = iommu->page_table; iopte_t *first; int ioptex; @@ -397,9 +404,9 @@ static int iommu_map_dma_area(dma_addr_t *pba, unsigned long va, return 0; } -static void iommu_unmap_dma_area(unsigned long busa, int len) +static void iommu_unmap_dma_area(struct device *dev, unsigned long busa, int len) { - struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu; + struct iommu_struct *iommu = dev->archdata.iommu; iopte_t *iopte = iommu->page_table; unsigned long end; int ioptex = (busa - iommu->start) >> PAGE_SHIFT; @@ -417,15 +424,6 @@ static void iommu_unmap_dma_area(unsigned long busa, int len) iommu_invalidate(iommu->regs); bit_map_clear(&iommu->usemap, ioptex, len >> PAGE_SHIFT); } - -static struct page *iommu_translate_dvma(unsigned long busa) -{ - struct iommu_struct *iommu = sbus_root->ofdev.dev.archdata.iommu; - iopte_t *iopte = iommu->page_table; - - iopte += ((busa - iommu->start) >> PAGE_SHIFT); - return pfn_to_page((iopte_val(*iopte) & IOPTE_PAGE) >> (PAGE_SHIFT-4)); -} #endif static char *iommu_lockarea(char *vaddr, unsigned long len) @@ -461,7 +459,6 @@ void __init ld_mmu_iommu(void) #ifdef CONFIG_SBUS BTFIXUPSET_CALL(mmu_map_dma_area, iommu_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, iommu_unmap_dma_area, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(mmu_translate_dvma, iommu_translate_dvma, BTFIXUPCALL_NORM); #endif if (viking_mxcc_present || srmmu_modtype == HyperSparc) { diff --git a/arch/sparc/mm/nosrmmu.c b/arch/sparc/mm/nosrmmu.c deleted file mode 100644 index 3701f70fc30..00000000000 --- a/arch/sparc/mm/nosrmmu.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * nosrmmu.c: This file is a bunch of dummies for sun4 compiles, - * so that it does not need srmmu and avoid ifdefs. - * - * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/init.h> -#include <asm/mbus.h> -#include <asm/sbus.h> - -static char shouldnothappen[] __initdata = "SUN4 kernel can only run on SUN4\n"; - -enum mbus_module srmmu_modtype; -void *srmmu_nocache_pool; - -int vac_cache_size = 0; - -static void __init should_not_happen(void) -{ - prom_printf(shouldnothappen); - prom_halt(); -} - -void __init srmmu_frob_mem_map(unsigned long start_mem) -{ - should_not_happen(); -} - -unsigned long __init srmmu_paging_init(unsigned long start_mem, unsigned long end_mem) -{ - should_not_happen(); - return 0; -} - -void __init ld_mmu_srmmu(void) -{ - should_not_happen(); -} - -void srmmu_mapioaddr(unsigned long physaddr, unsigned long virt_addr, int bus_type, int rdonly) -{ -} - -void srmmu_unmapioaddr(unsigned long virt_addr) -{ -} - -__u32 iounit_map_dma_init(struct sbus_bus *sbus, int size) -{ - return 0; -} - -__u32 iounit_map_dma_page(__u32 vaddr, void *addr, struct sbus_bus *sbus) -{ - return 0; -} diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index ee30462598f..6a5d7cabc04 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -31,7 +31,6 @@ #include <asm/mbus.h> #include <asm/cache.h> #include <asm/oplib.h> -#include <asm/sbus.h> #include <asm/asi.h> #include <asm/msi.h> #include <asm/mmu_context.h> diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index d1782f6368b..fe65aeeb394 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -31,7 +31,6 @@ #include <asm/oplib.h> #include <asm/openprom.h> #include <asm/mmu_context.h> -#include <asm/sun4paddr.h> #include <asm/highmem.h> #include <asm/btfixup.h> #include <asm/cacheflush.h> @@ -52,15 +51,11 @@ extern int num_segmaps, num_contexts; extern unsigned long page_kernel; -#ifdef CONFIG_SUN4 -#define SUN4C_VAC_SIZE sun4c_vacinfo.num_bytes -#else /* That's it, we prom_halt() on sun4c if the cache size is something other than 65536. * So let's save some cycles and just use that everywhere except for that bootup * sanity check. */ #define SUN4C_VAC_SIZE 65536 -#endif #define SUN4C_KERNEL_BUCKETS 32 @@ -285,75 +280,32 @@ void __init sun4c_probe_vac(void) { sun4c_disable_vac(); - if (ARCH_SUN4) { - switch (idprom->id_machtype) { - - case (SM_SUN4|SM_4_110): - sun4c_vacinfo.type = VAC_NONE; - sun4c_vacinfo.num_bytes = 0; - sun4c_vacinfo.linesize = 0; - sun4c_vacinfo.do_hwflushes = 0; - prom_printf("No VAC. Get some bucks and buy a real computer."); - prom_halt(); - break; - - case (SM_SUN4|SM_4_260): - sun4c_vacinfo.type = VAC_WRITE_BACK; - sun4c_vacinfo.num_bytes = 128 * 1024; - sun4c_vacinfo.linesize = 16; - sun4c_vacinfo.do_hwflushes = 0; - break; - - case (SM_SUN4|SM_4_330): - sun4c_vacinfo.type = VAC_WRITE_THROUGH; - sun4c_vacinfo.num_bytes = 128 * 1024; - sun4c_vacinfo.linesize = 16; - sun4c_vacinfo.do_hwflushes = 0; - break; - - case (SM_SUN4|SM_4_470): - sun4c_vacinfo.type = VAC_WRITE_BACK; - sun4c_vacinfo.num_bytes = 128 * 1024; - sun4c_vacinfo.linesize = 32; - sun4c_vacinfo.do_hwflushes = 0; - break; - - default: - prom_printf("Cannot initialize VAC - weird sun4 model idprom->id_machtype = %d", idprom->id_machtype); - prom_halt(); - }; + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* PROM on SS1 lacks this info, to be super safe we + * hard code it here since this arch is cast in stone. + */ + sun4c_vacinfo.num_bytes = 65536; + sun4c_vacinfo.linesize = 16; } else { - sun4c_vacinfo.type = VAC_WRITE_THROUGH; + sun4c_vacinfo.num_bytes = + prom_getintdefault(prom_root_node, "vac-size", 65536); + sun4c_vacinfo.linesize = + prom_getintdefault(prom_root_node, "vac-linesize", 16); + } + sun4c_vacinfo.do_hwflushes = + prom_getintdefault(prom_root_node, "vac-hwflush", 0); - if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* PROM on SS1 lacks this info, to be super safe we - * hard code it here since this arch is cast in stone. - */ - sun4c_vacinfo.num_bytes = 65536; - sun4c_vacinfo.linesize = 16; - } else { - sun4c_vacinfo.num_bytes = - prom_getintdefault(prom_root_node, "vac-size", 65536); - sun4c_vacinfo.linesize = - prom_getintdefault(prom_root_node, "vac-linesize", 16); - } + if (sun4c_vacinfo.do_hwflushes == 0) sun4c_vacinfo.do_hwflushes = - prom_getintdefault(prom_root_node, "vac-hwflush", 0); - - if (sun4c_vacinfo.do_hwflushes == 0) - sun4c_vacinfo.do_hwflushes = - prom_getintdefault(prom_root_node, "vac_hwflush", 0); + prom_getintdefault(prom_root_node, "vac_hwflush", 0); - if (sun4c_vacinfo.num_bytes != 65536) { - prom_printf("WEIRD Sun4C VAC cache size, " - "tell sparclinux@vger.kernel.org"); - prom_halt(); - } + if (sun4c_vacinfo.num_bytes != 65536) { + prom_printf("WEIRD Sun4C VAC cache size, " + "tell sparclinux@vger.kernel.org"); + prom_halt(); } - sun4c_vacinfo.num_lines = - (sun4c_vacinfo.num_bytes / sun4c_vacinfo.linesize); switch (sun4c_vacinfo.linesize) { case 16: sun4c_vacinfo.log2lsize = 4; @@ -447,49 +399,18 @@ static void __init patch_kernel_fault_handler(void) static void __init sun4c_probe_mmu(void) { - if (ARCH_SUN4) { - switch (idprom->id_machtype) { - case (SM_SUN4|SM_4_110): - prom_printf("No support for 4100 yet\n"); - prom_halt(); - num_segmaps = 256; - num_contexts = 8; - break; - - case (SM_SUN4|SM_4_260): - /* should be 512 segmaps. when it get fixed */ - num_segmaps = 256; - num_contexts = 16; - break; - - case (SM_SUN4|SM_4_330): - num_segmaps = 256; - num_contexts = 16; - break; - - case (SM_SUN4|SM_4_470): - /* should be 1024 segmaps. when it get fixed */ - num_segmaps = 256; - num_contexts = 64; - break; - default: - prom_printf("Invalid SUN4 model\n"); - prom_halt(); - }; + if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || + (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { + /* Hardcode these just to be safe, PROM on SS1 does + * not have this info available in the root node. + */ + num_segmaps = 128; + num_contexts = 8; } else { - if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* Hardcode these just to be safe, PROM on SS1 does - * not have this info available in the root node. - */ - num_segmaps = 128; - num_contexts = 8; - } else { - num_segmaps = - prom_getintdefault(prom_root_node, "mmu-npmg", 128); - num_contexts = - prom_getintdefault(prom_root_node, "mmu-nctx", 0x8); - } + num_segmaps = + prom_getintdefault(prom_root_node, "mmu-npmg", 128); + num_contexts = + prom_getintdefault(prom_root_node, "mmu-nctx", 0x8); } patch_kernel_fault_handler(); } @@ -501,18 +422,14 @@ void __init sun4c_probe_memerr_reg(void) int node; struct linux_prom_registers regs[1]; - if (ARCH_SUN4) { - sun4c_memerr_reg = ioremap(sun4_memreg_physaddr, PAGE_SIZE); - } else { - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(prom_root_node, "memory-error"); - if (!node) - return; - if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0) - return; - /* hmm I think regs[0].which_io is zero here anyways */ - sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size); - } + node = prom_getchild(prom_root_node); + node = prom_searchsiblings(prom_root_node, "memory-error"); + if (!node) + return; + if (prom_getproperty(node, "reg", (char *)regs, sizeof(regs)) <= 0) + return; + /* hmm I think regs[0].which_io is zero here anyways */ + sun4c_memerr_reg = ioremap(regs[0].phys_addr, regs[0].reg_size); } static inline void sun4c_init_ss2_cache_bug(void) @@ -521,7 +438,6 @@ static inline void sun4c_init_ss2_cache_bug(void) if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS2)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_IPX)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_330)) || (idprom->id_machtype == (SM_SUN4C | SM_4C_ELC))) { /* Whee.. */ printk("SS2 cache bug detected, uncaching trap table page\n"); @@ -532,8 +448,8 @@ static inline void sun4c_init_ss2_cache_bug(void) } /* Addr is always aligned on a page boundary for us already. */ -static int sun4c_map_dma_area(dma_addr_t *pba, unsigned long va, - unsigned long addr, int len) +static int sun4c_map_dma_area(struct device *dev, dma_addr_t *pba, unsigned long va, + unsigned long addr, int len) { unsigned long page, end; @@ -555,14 +471,7 @@ static int sun4c_map_dma_area(dma_addr_t *pba, unsigned long va, return 0; } -static struct page *sun4c_translate_dvma(unsigned long busa) -{ - /* Fortunately for us, bus_addr == uncached_virt in sun4c. */ - unsigned long pte = sun4c_get_pte(busa); - return pfn_to_page(pte & SUN4C_PFN_MASK); -} - -static void sun4c_unmap_dma_area(unsigned long busa, int len) +static void sun4c_unmap_dma_area(struct device *dev, unsigned long busa, int len) { /* Fortunately for us, bus_addr == uncached_virt in sun4c. */ /* XXX Implement this */ @@ -624,11 +533,7 @@ static inline void sun4c_init_map_kernelprom(unsigned long kernel_end) { unsigned long vaddr; unsigned char pseg, ctx; -#ifdef CONFIG_SUN4 - /* sun4/110 and 260 have no kadb. */ - if ((idprom->id_machtype != (SM_SUN4 | SM_4_260)) && - (idprom->id_machtype != (SM_SUN4 | SM_4_110))) { -#endif + for (vaddr = KADB_DEBUGGER_BEGVM; vaddr < LINUX_OPPROM_ENDVM; vaddr += SUN4C_REAL_PGDIR_SIZE) { @@ -640,9 +545,7 @@ static inline void sun4c_init_map_kernelprom(unsigned long kernel_end) fix_permissions(vaddr, _SUN4C_PAGE_PRIV, 0); } } -#ifdef CONFIG_SUN4 - } -#endif + for (vaddr = KERNBASE; vaddr < kernel_end; vaddr += SUN4C_REAL_PGDIR_SIZE) { pseg = sun4c_get_segmap(vaddr); mmu_entry_pool[pseg].locked = 1; @@ -1048,14 +951,10 @@ static struct thread_info *sun4c_alloc_thread_info(void) * so we must flush the cache to guarantee consistency. */ sun4c_flush_page(pages); -#ifndef CONFIG_SUN4 sun4c_flush_page(pages + PAGE_SIZE); -#endif sun4c_put_pte(addr, BUCKET_PTE(pages)); -#ifndef CONFIG_SUN4 sun4c_put_pte(addr + PAGE_SIZE, BUCKET_PTE(pages + PAGE_SIZE)); -#endif #ifdef CONFIG_DEBUG_STACK_USAGE memset((void *)addr, 0, PAGE_SIZE << THREAD_INFO_ORDER); @@ -1072,13 +971,11 @@ static void sun4c_free_thread_info(struct thread_info *ti) /* We are deleting a mapping, so the flush here is mandatory. */ sun4c_flush_page(tiaddr); -#ifndef CONFIG_SUN4 sun4c_flush_page(tiaddr + PAGE_SIZE); -#endif + sun4c_put_pte(tiaddr, 0); -#ifndef CONFIG_SUN4 sun4c_put_pte(tiaddr + PAGE_SIZE, 0); -#endif + sun4c_bucket[entry] = BUCKET_EMPTY; if (entry < sun4c_lowbucket_avail) sun4c_lowbucket_avail = entry; @@ -1211,7 +1108,7 @@ static void sun4c_unlockarea(char *vaddr, unsigned long size) * by implication and fool the page locking code above * if passed to by mistake. */ -static __u32 sun4c_get_scsi_one(char *bufptr, unsigned long len, struct sbus_bus *sbus) +static __u32 sun4c_get_scsi_one(struct device *dev, char *bufptr, unsigned long len) { unsigned long page; @@ -1223,7 +1120,7 @@ static __u32 sun4c_get_scsi_one(char *bufptr, unsigned long len, struct sbus_bus return (__u32)sun4c_lockarea(bufptr, len); } -static void sun4c_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void sun4c_get_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { while (sz != 0) { --sz; @@ -1233,14 +1130,14 @@ static void sun4c_get_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus * } } -static void sun4c_release_scsi_one(__u32 bufptr, unsigned long len, struct sbus_bus *sbus) +static void sun4c_release_scsi_one(struct device *dev, __u32 bufptr, unsigned long len) { if (bufptr < sun4c_iobuffer_start) return; /* On kernel stack or similar, see above */ sun4c_unlockarea((char *)bufptr, len); } -static void sun4c_release_scsi_sgl(struct scatterlist *sg, int sz, struct sbus_bus *sbus) +static void sun4c_release_scsi_sgl(struct device *dev, struct scatterlist *sg, int sz) { while (sz != 0) { --sz; @@ -2263,7 +2160,6 @@ void __init ld_mmu_sun4c(void) BTFIXUPSET_CALL(mmu_map_dma_area, sun4c_map_dma_area, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(mmu_unmap_dma_area, sun4c_unmap_dma_area, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(mmu_translate_dvma, sun4c_translate_dvma, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(sparc_mapiorange, sun4c_mapiorange, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(sparc_unmapiorange, sun4c_unmapiorange, BTFIXUPCALL_NORM); diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile index 7f5eacfcfbc..8f7e18546c9 100644 --- a/arch/sparc/prom/Makefile +++ b/arch/sparc/prom/Makefile @@ -4,5 +4,3 @@ lib-y := bootstr.o devmap.o devops.o init.o memory.o misc.o mp.o \ palloc.o ranges.o segment.o console.o printf.o tree.o - -lib-$(CONFIG_SUN4) += sun4prom.o diff --git a/arch/sparc/prom/bootstr.c b/arch/sparc/prom/bootstr.c index 5a35c768ff7..916831da7e6 100644 --- a/arch/sparc/prom/bootstr.c +++ b/arch/sparc/prom/bootstr.c @@ -6,15 +6,12 @@ #include <linux/string.h> #include <asm/oplib.h> -#include <asm/sun4prom.h> #include <linux/init.h> #define BARG_LEN 256 static char barg_buf[BARG_LEN] = { 0 }; static char fetched __initdata = 0; -extern linux_sun4_romvec *sun4_romvec; - char * __init prom_getbootargs(void) { @@ -28,7 +25,6 @@ prom_getbootargs(void) switch(prom_vers) { case PROM_V0: - case PROM_SUN4: cp = barg_buf; /* Start from 1 and go over fd(0,0,0)kernel */ for(iter = 1; iter < 8; iter++) { diff --git a/arch/sparc/prom/console.c b/arch/sparc/prom/console.c index 790057a3461..b3075d73fc1 100644 --- a/arch/sparc/prom/console.c +++ b/arch/sparc/prom/console.c @@ -10,7 +10,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <asm/openprom.h> -#include <asm/sun4prom.h> #include <asm/oplib.h> #include <asm/system.h> #include <linux/string.h> @@ -30,7 +29,6 @@ prom_nbgetchar(void) spin_lock_irqsave(&prom_lock, flags); switch(prom_vers) { case PROM_V0: - case PROM_SUN4: i = (*(romvec->pv_nbgetchar))(); break; case PROM_V2: @@ -63,7 +61,6 @@ prom_nbputchar(char c) spin_lock_irqsave(&prom_lock, flags); switch(prom_vers) { case PROM_V0: - case PROM_SUN4: i = (*(romvec->pv_nbputchar))(c); break; case PROM_V2: diff --git a/arch/sparc/prom/init.c b/arch/sparc/prom/init.c index 729f8706694..873217c6d82 100644 --- a/arch/sparc/prom/init.c +++ b/arch/sparc/prom/init.c @@ -11,12 +11,10 @@ #include <asm/openprom.h> #include <asm/oplib.h> -#include <asm/sun4prom.h> struct linux_romvec *romvec; enum prom_major_version prom_vers; unsigned int prom_rev, prom_prev; -linux_sun4_romvec *sun4_romvec; /* The root node of the prom device tree. */ int prom_root_node; @@ -34,10 +32,6 @@ extern void prom_ranges_init(void); void __init prom_init(struct linux_romvec *rp) { -#ifdef CONFIG_SUN4 - extern struct linux_romvec *sun4_prom_init(void); - rp = sun4_prom_init(); -#endif romvec = rp; switch(romvec->pv_romvers) { @@ -50,9 +44,6 @@ void __init prom_init(struct linux_romvec *rp) case 3: prom_vers = PROM_V3; break; - case 40: - prom_vers = PROM_SUN4; - break; default: prom_printf("PROMLIB: Bad PROM version %d\n", romvec->pv_romvers); @@ -76,11 +67,8 @@ void __init prom_init(struct linux_romvec *rp) prom_ranges_init(); -#ifndef CONFIG_SUN4 - /* SUN4 prints this in sun4_prom_init */ printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n", romvec->pv_romvers, prom_rev); -#endif /* Initialization successful. */ return; diff --git a/arch/sparc/prom/memory.c b/arch/sparc/prom/memory.c index 947f047dc95..fac7899a29c 100644 --- a/arch/sparc/prom/memory.c +++ b/arch/sparc/prom/memory.c @@ -10,7 +10,6 @@ #include <linux/init.h> #include <asm/openprom.h> -#include <asm/sun4prom.h> #include <asm/oplib.h> #include <asm/page.h> @@ -46,15 +45,6 @@ static int __init prom_meminit_v2(void) return num_ents; } -static int __init prom_meminit_sun4(void) -{ -#ifdef CONFIG_SUN4 - sp_banks[0].base_addr = 0; - sp_banks[0].num_bytes = *(sun4_romvec->memoryavail); -#endif - return 1; -} - static int sp_banks_cmp(const void *a, const void *b) { const struct sparc_phys_banks *x = a, *y = b; @@ -81,10 +71,6 @@ void __init prom_meminit(void) num_ents = prom_meminit_v2(); break; - case PROM_SUN4: - num_ents = prom_meminit_sun4(); - break; - default: break; } diff --git a/arch/sparc/prom/ranges.c b/arch/sparc/prom/ranges.c index f9b7def35f6..64579a37641 100644 --- a/arch/sparc/prom/ranges.c +++ b/arch/sparc/prom/ranges.c @@ -9,7 +9,6 @@ #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/types.h> -#include <asm/sbus.h> #include <asm/system.h> struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX]; diff --git a/arch/sparc/prom/sun4prom.c b/arch/sparc/prom/sun4prom.c deleted file mode 100644 index 00390a2652a..00000000000 --- a/arch/sparc/prom/sun4prom.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 1996 The Australian National University. - * Copyright (C) 1996 Fujitsu Laboratories Limited - * Copyright (C) 1997 Michael A. Griffith (grif@acm.org) - * Copyright (C) 1997 Sun Weenie (ko@ko.reno.nv.us) - * Copyright (C) 1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - * - * This software may be distributed under the terms of the Gnu - * Public License version 2 or later - * - * fake a really simple Sun prom for the SUN4 - */ - -#include <linux/kernel.h> -#include <linux/string.h> -#include <asm/oplib.h> -#include <asm/idprom.h> -#include <asm/machines.h> -#include <asm/sun4prom.h> -#include <asm/asi.h> -#include <asm/contregs.h> -#include <linux/init.h> - -static struct linux_romvec sun4romvec; -static struct idprom sun4_idprom; - -struct property { - char *name; - char *value; - int length; -}; - -struct node { - int level; - struct property *properties; -}; - -struct property null_properties = { NULL, NULL, -1 }; - -struct property root_properties[] = { - {"device_type", "cpu", 4}, - {"idprom", (char *)&sun4_idprom, sizeof(struct idprom)}, - {NULL, NULL, -1} -}; - -struct node nodes[] = { - { 0, &null_properties }, - { 0, root_properties }, - { -1,&null_properties } -}; - - -static int no_nextnode(int node) -{ - if (nodes[node].level == nodes[node+1].level) - return node+1; - return -1; -} - -static int no_child(int node) -{ - if (nodes[node].level == nodes[node+1].level-1) - return node+1; - return -1; -} - -static struct property *find_property(int node,char *name) -{ - struct property *prop = &nodes[node].properties[0]; - while (prop && prop->name) { - if (strcmp(prop->name,name) == 0) return prop; - prop++; - } - return NULL; -} - -static int no_proplen(int node,char *name) -{ - struct property *prop = find_property(node,name); - if (prop) return prop->length; - return -1; -} - -static int no_getprop(int node,char *name,char *value) -{ - struct property *prop = find_property(node,name); - if (prop) { - memcpy(value,prop->value,prop->length); - return 1; - } - return -1; -} - -static int no_setprop(int node,char *name,char *value,int len) -{ - return -1; -} - -static char *no_nextprop(int node,char *name) -{ - struct property *prop = find_property(node,name); - if (prop) return prop[1].name; - return NULL; -} - -static struct linux_nodeops sun4_nodeops = { - no_nextnode, - no_child, - no_proplen, - no_getprop, - no_setprop, - no_nextprop -}; - -static int synch_hook; - -struct linux_romvec * __init sun4_prom_init(void) -{ - int i; - unsigned char x; - char *p; - - p = (char *)&sun4_idprom; - for (i = 0; i < sizeof(sun4_idprom); i++) { - __asm__ __volatile__ ("lduba [%1] %2, %0" : "=r" (x) : - "r" (AC_IDPROM + i), "i" (ASI_CONTROL)); - *p++ = x; - } - - memset(&sun4romvec,0,sizeof(sun4romvec)); - - sun4_romvec = (linux_sun4_romvec *) SUN4_PROM_VECTOR; - - sun4romvec.pv_romvers = 40; - sun4romvec.pv_nodeops = &sun4_nodeops; - sun4romvec.pv_reboot = sun4_romvec->reboot; - sun4romvec.pv_abort = sun4_romvec->abortentry; - sun4romvec.pv_halt = sun4_romvec->exittomon; - sun4romvec.pv_synchook = (void (**)(void))&synch_hook; - sun4romvec.pv_setctxt = sun4_romvec->setcxsegmap; - sun4romvec.pv_v0bootargs = sun4_romvec->bootParam; - sun4romvec.pv_nbgetchar = sun4_romvec->mayget; - sun4romvec.pv_nbputchar = sun4_romvec->mayput; - sun4romvec.pv_stdin = sun4_romvec->insource; - sun4romvec.pv_stdout = sun4_romvec->outsink; - - /* - * We turn on the LEDs to let folks without monitors or - * terminals know we booted. Nothing too fancy now. They - * are all on, except for LED 5, which blinks. When we - * have more time, we can teach the penguin to say "By your - * command" or "Activating turbo boost, Michael". :-) - */ - sun4_romvec->setLEDs(NULL); - - printk("PROMLIB: Old Sun4 boot PROM monitor %s, romvec version %d\n", - sun4_romvec->monid, - sun4_romvec->romvecversion); - - return &sun4romvec; -} diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 36b4b7ab9cf..5446e2a499b 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -18,6 +18,13 @@ config SPARC64 select HAVE_ARCH_KGDB select USE_GENERIC_SMP_HELPERS if SMP select HAVE_ARCH_TRACEHOOK + select ARCH_WANT_OPTIONAL_GPIOLIB + select RTC_CLASS + select RTC_DRV_M48T59 + select RTC_DRV_CMOS + select RTC_DRV_BQ4802 + select RTC_DRV_SUN4V + select RTC_DRV_STARFIRE config GENERIC_TIME bool @@ -31,6 +38,11 @@ config GENERIC_CLOCKEVENTS bool default y +config GENERIC_GPIO + bool + help + Generic GPIO API support + config 64BIT def_bool y @@ -185,6 +197,17 @@ config US2E_FREQ If in doubt, say N. +config US3_MC + tristate "UltraSPARC-III Memory Controller driver" + default y + help + This adds a driver for the UltraSPARC-III memory controller. + Loading this driver allows exact mnemonic strings to be + printed in the event of a memory error, so that the faulty DIMM + on the motherboard can be matched to the error. + + If in doubt, say Y, as this information can be very useful. + # Global things across all Sun machines. config GENERIC_LOCKBREAK bool diff --git a/arch/sparc64/Makefile b/arch/sparc64/Makefile index b785a395b12..c7214abc0d8 100644 --- a/arch/sparc64/Makefile +++ b/arch/sparc64/Makefile @@ -7,7 +7,7 @@ # Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) # -CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -m64 +CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64 # Undefine sparc when processing vmlinux.lds - it is used # And teach CPP we are doing 64 bit builds (for this case) diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 418b5782096..c0b8009ab19 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -7,16 +7,16 @@ EXTRA_CFLAGS := -Werror extra-y := head.o init_task.o vmlinux.lds -obj-y := process.o setup.o cpu.o idprom.o \ +obj-y := process.o setup.o cpu.o idprom.o reboot.o \ traps.o auxio.o una_asm.o sysfs.o iommu.o \ irq.o ptrace.o time.o sys_sparc.o signal.o \ - unaligned.o central.o pci.o starfire.o \ - power.o sbus.o sparc64_ksyms.o chmc.o \ + unaligned.o central.o starfire.o \ + power.o sbus.o sparc64_ksyms.o ebus.o \ visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_STACKTRACE) += stacktrace.o -obj-$(CONFIG_PCI) += ebus.o pci_common.o \ +obj-$(CONFIG_PCI) += pci.o pci_common.o psycho_common.o \ pci_psycho.o pci_sabre.o pci_schizo.o \ pci_sun4v.o pci_sun4v_asm.o pci_fire.o obj-$(CONFIG_PCI_MSI) += pci_msi.o @@ -25,6 +25,7 @@ obj-$(CONFIG_COMPAT) += sys32.o sys_sparc32.o signal32.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_US3_FREQ) += us3_cpufreq.o obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o +obj-$(CONFIG_US3_MC) += chmc.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_SUN_LDOMS) += ldc.o vio.o viohs.o ds.o obj-$(CONFIG_AUDIT) += audit.o diff --git a/arch/sparc64/kernel/auxio.c b/arch/sparc64/kernel/auxio.c index dd5c7bf8761..858beda8652 100644 --- a/arch/sparc64/kernel/auxio.c +++ b/arch/sparc64/kernel/auxio.c @@ -109,7 +109,7 @@ void auxio_set_lte(int on) } } -static struct of_device_id auxio_match[] = { +static struct of_device_id __initdata auxio_match[] = { { .name = "auxio", }, diff --git a/arch/sparc64/kernel/central.c b/arch/sparc64/kernel/central.c index f2e87d0d7e1..05f1c916db0 100644 --- a/arch/sparc64/kernel/central.c +++ b/arch/sparc64/kernel/central.c @@ -1,461 +1,268 @@ /* central.c: Central FHC driver for Sunfire/Starfire/Wildfire. * - * Copyright (C) 1997, 1999 David S. Miller (davem@davemloft.net) + * Copyright (C) 1997, 1999, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> #include <linux/types.h> #include <linux/string.h> -#include <linux/timer.h> -#include <linux/sched.h> -#include <linux/delay.h> #include <linux/init.h> -#include <linux/bootmem.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> -#include <asm/page.h> #include <asm/fhc.h> -#include <asm/starfire.h> - -static struct linux_central *central_bus = NULL; -static struct linux_fhc *fhc_list = NULL; +#include <asm/upa.h> + +struct clock_board { + void __iomem *clock_freq_regs; + void __iomem *clock_regs; + void __iomem *clock_ver_reg; + int num_slots; + struct resource leds_resource; + struct platform_device leds_pdev; +}; + +struct fhc { + void __iomem *pregs; + bool central; + bool jtag_master; + int board_num; + struct resource leds_resource; + struct platform_device leds_pdev; +}; + +static int __devinit clock_board_calc_nslots(struct clock_board *p) +{ + u8 reg = upa_readb(p->clock_regs + CLOCK_STAT1) & 0xc0; -#define IS_CENTRAL_FHC(__fhc) ((__fhc) == central_bus->child) + switch (reg) { + case 0x40: + return 16; -static void central_probe_failure(int line) -{ - prom_printf("CENTRAL: Critical device probe failure at central.c:%d\n", - line); - prom_halt(); -} + case 0xc0: + return 8; -static void central_ranges_init(struct linux_central *central) -{ - struct device_node *dp = central->prom_node; - const void *pval; - int len; - - central->num_central_ranges = 0; - pval = of_get_property(dp, "ranges", &len); - if (pval) { - memcpy(central->central_ranges, pval, len); - central->num_central_ranges = - (len / sizeof(struct linux_prom_ranges)); + case 0x80: + reg = 0; + if (p->clock_ver_reg) + reg = upa_readb(p->clock_ver_reg); + if (reg) { + if (reg & 0x80) + return 4; + else + return 5; + } + /* Fallthrough */ + default: + return 4; } } -static void fhc_ranges_init(struct linux_fhc *fhc) +static int __devinit clock_board_probe(struct of_device *op, + const struct of_device_id *match) { - struct device_node *dp = fhc->prom_node; - const void *pval; - int len; - - fhc->num_fhc_ranges = 0; - pval = of_get_property(dp, "ranges", &len); - if (pval) { - memcpy(fhc->fhc_ranges, pval, len); - fhc->num_fhc_ranges = - (len / sizeof(struct linux_prom_ranges)); - } -} + struct clock_board *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; -/* Range application routines are exported to various drivers, - * so do not __init this. - */ -static void adjust_regs(struct linux_prom_registers *regp, int nregs, - struct linux_prom_ranges *rangep, int nranges) -{ - int regc, rngc; - - for (regc = 0; regc < nregs; regc++) { - for (rngc = 0; rngc < nranges; rngc++) - if (regp[regc].which_io == rangep[rngc].ot_child_space) - break; /* Fount it */ - if (rngc == nranges) /* oops */ - central_probe_failure(__LINE__); - regp[regc].which_io = rangep[rngc].ot_parent_space; - regp[regc].phys_addr -= rangep[rngc].ot_child_base; - regp[regc].phys_addr += rangep[rngc].ot_parent_base; + if (!p) { + printk(KERN_ERR "clock_board: Cannot allocate struct clock_board\n"); + goto out; } -} -/* Apply probed fhc ranges to registers passed, if no ranges return. */ -static void apply_fhc_ranges(struct linux_fhc *fhc, - struct linux_prom_registers *regs, - int nregs) -{ - if (fhc->num_fhc_ranges) - adjust_regs(regs, nregs, fhc->fhc_ranges, - fhc->num_fhc_ranges); -} + p->clock_freq_regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "clock_board_freq"); + if (!p->clock_freq_regs) { + printk(KERN_ERR "clock_board: Cannot map clock_freq_regs\n"); + goto out_free; + } -/* Apply probed central ranges to registers passed, if no ranges return. */ -static void apply_central_ranges(struct linux_central *central, - struct linux_prom_registers *regs, int nregs) -{ - if (central->num_central_ranges) - adjust_regs(regs, nregs, central->central_ranges, - central->num_central_ranges); -} + p->clock_regs = of_ioremap(&op->resource[1], 0, + resource_size(&op->resource[1]), + "clock_board_regs"); + if (!p->clock_regs) { + printk(KERN_ERR "clock_board: Cannot map clock_regs\n"); + goto out_unmap_clock_freq_regs; + } -static void * __init central_alloc_bootmem(unsigned long size) -{ - void *ret; + if (op->resource[2].flags) { + p->clock_ver_reg = of_ioremap(&op->resource[2], 0, + resource_size(&op->resource[2]), + "clock_ver_reg"); + if (!p->clock_ver_reg) { + printk(KERN_ERR "clock_board: Cannot map clock_ver_reg\n"); + goto out_unmap_clock_regs; + } + } - ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL); - if (ret != NULL) - memset(ret, 0, size); + p->num_slots = clock_board_calc_nslots(p); - return ret; -} + p->leds_resource.start = (unsigned long) + (p->clock_regs + CLOCK_CTRL); + p->leds_resource.end = p->leds_resource.end; + p->leds_resource.name = "leds"; -static unsigned long prom_reg_to_paddr(struct linux_prom_registers *r) -{ - unsigned long ret = ((unsigned long) r->which_io) << 32; + p->leds_pdev.name = "sunfire-clockboard-leds"; + p->leds_pdev.resource = &p->leds_resource; + p->leds_pdev.num_resources = 1; + p->leds_pdev.dev.parent = &op->dev; - return ret | (unsigned long) r->phys_addr; -} - -static void __init probe_other_fhcs(void) -{ - struct device_node *dp; - const struct linux_prom64_registers *fpregs; - - for_each_node_by_name(dp, "fhc") { - struct linux_fhc *fhc; - int board; - u32 tmp; - - if (dp->parent && - dp->parent->parent != NULL) - continue; - - fhc = (struct linux_fhc *) - central_alloc_bootmem(sizeof(struct linux_fhc)); - if (fhc == NULL) - central_probe_failure(__LINE__); - - /* Link it into the FHC chain. */ - fhc->next = fhc_list; - fhc_list = fhc; - - /* Toplevel FHCs have no parent. */ - fhc->parent = NULL; - - fhc->prom_node = dp; - fhc_ranges_init(fhc); - - /* Non-central FHC's have 64-bit OBP format registers. */ - fpregs = of_get_property(dp, "reg", NULL); - if (!fpregs) - central_probe_failure(__LINE__); - - /* Only central FHC needs special ranges applied. */ - fhc->fhc_regs.pregs = fpregs[0].phys_addr; - fhc->fhc_regs.ireg = fpregs[1].phys_addr; - fhc->fhc_regs.ffregs = fpregs[2].phys_addr; - fhc->fhc_regs.sregs = fpregs[3].phys_addr; - fhc->fhc_regs.uregs = fpregs[4].phys_addr; - fhc->fhc_regs.tregs = fpregs[5].phys_addr; - - board = of_getintprop_default(dp, "board#", -1); - fhc->board = board; - - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_JCTRL); - if ((tmp & FHC_JTAG_CTRL_MENAB) != 0) - fhc->jtag_master = 1; - else - fhc->jtag_master = 0; - - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); - printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] %s\n", - board, - (tmp & FHC_ID_VERS) >> 28, - (tmp & FHC_ID_PARTID) >> 12, - (tmp & FHC_ID_MANUF) >> 1, - (fhc->jtag_master ? "(JTAG Master)" : "")); - - /* This bit must be set in all non-central FHC's in - * the system. When it is clear, this identifies - * the central board. - */ - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - tmp |= FHC_CONTROL_IXIST; - upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); + err = platform_device_register(&p->leds_pdev); + if (err) { + printk(KERN_ERR "clock_board: Could not register LEDS " + "platform device\n"); + goto out_unmap_clock_ver_reg; } -} -static void probe_clock_board(struct linux_central *central, - struct linux_fhc *fhc, - struct device_node *fp) -{ - struct device_node *dp; - struct linux_prom_registers cregs[3]; - const struct linux_prom_registers *pr; - int nslots, tmp, nregs; - - dp = fp->child; - while (dp) { - if (!strcmp(dp->name, "clock-board")) - break; - dp = dp->sibling; - } - if (!dp) - central_probe_failure(__LINE__); + printk(KERN_INFO "clock_board: Detected %d slot Enterprise system.\n", + p->num_slots); - pr = of_get_property(dp, "reg", &nregs); - if (!pr) - central_probe_failure(__LINE__); + err = 0; +out: + return err; - memcpy(cregs, pr, nregs); - nregs /= sizeof(struct linux_prom_registers); +out_unmap_clock_ver_reg: + if (p->clock_ver_reg) + of_iounmap(&op->resource[2], p->clock_ver_reg, + resource_size(&op->resource[2])); - apply_fhc_ranges(fhc, &cregs[0], nregs); - apply_central_ranges(central, &cregs[0], nregs); - central->cfreg = prom_reg_to_paddr(&cregs[0]); - central->clkregs = prom_reg_to_paddr(&cregs[1]); +out_unmap_clock_regs: + of_iounmap(&op->resource[1], p->clock_regs, + resource_size(&op->resource[1])); - if (nregs == 2) - central->clkver = 0UL; - else - central->clkver = prom_reg_to_paddr(&cregs[2]); +out_unmap_clock_freq_regs: + of_iounmap(&op->resource[0], p->clock_freq_regs, + resource_size(&op->resource[0])); - tmp = upa_readb(central->clkregs + CLOCK_STAT1); - tmp &= 0xc0; - switch(tmp) { - case 0x40: - nslots = 16; - break; - case 0xc0: - nslots = 8; - break; - case 0x80: - if (central->clkver != 0UL && - upa_readb(central->clkver) != 0) { - if ((upa_readb(central->clkver) & 0x80) != 0) - nslots = 4; - else - nslots = 5; - break; - } - default: - nslots = 4; - break; - }; - central->slots = nslots; - printk("CENTRAL: Detected %d slot Enterprise system. cfreg[%02x] cver[%02x]\n", - central->slots, upa_readb(central->cfreg), - (central->clkver ? upa_readb(central->clkver) : 0x00)); +out_free: + kfree(p); + goto out; } -static void ZAP(unsigned long iclr, unsigned long imap) +static struct of_device_id __initdata clock_board_match[] = { + { + .name = "clock-board", + }, + {}, +}; + +static struct of_platform_driver clock_board_driver = { + .match_table = clock_board_match, + .probe = clock_board_probe, + .driver = { + .name = "clock_board", + }, +}; + +static int __devinit fhc_probe(struct of_device *op, + const struct of_device_id *match) { - u32 imap_tmp; - - upa_writel(0, iclr); - upa_readl(iclr); - imap_tmp = upa_readl(imap); - imap_tmp &= ~(0x80000000); - upa_writel(imap_tmp, imap); - upa_readl(imap); -} + struct fhc *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + u32 reg; -static void init_all_fhc_hw(void) -{ - struct linux_fhc *fhc; - - for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) { - u32 tmp; - - /* Clear all of the interrupt mapping registers - * just in case OBP left them in a foul state. - */ - ZAP(fhc->fhc_regs.ffregs + FHC_FFREGS_ICLR, - fhc->fhc_regs.ffregs + FHC_FFREGS_IMAP); - ZAP(fhc->fhc_regs.sregs + FHC_SREGS_ICLR, - fhc->fhc_regs.sregs + FHC_SREGS_IMAP); - ZAP(fhc->fhc_regs.uregs + FHC_UREGS_ICLR, - fhc->fhc_regs.uregs + FHC_UREGS_IMAP); - ZAP(fhc->fhc_regs.tregs + FHC_TREGS_ICLR, - fhc->fhc_regs.tregs + FHC_TREGS_IMAP); - - /* Setup FHC control register. */ - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - - /* All non-central boards have this bit set. */ - if (! IS_CENTRAL_FHC(fhc)) - tmp |= FHC_CONTROL_IXIST; - - /* For all FHCs, clear the firmware synchronization - * line and both low power mode enables. - */ - tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | - FHC_CONTROL_SLINE); - - upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); + if (!p) { + printk(KERN_ERR "fhc: Cannot allocate struct fhc\n"); + goto out; } -} + if (!strcmp(op->node->parent->name, "central")) + p->central = true; -void __init central_probe(void) -{ - struct linux_prom_registers fpregs[6]; - const struct linux_prom_registers *pr; - struct linux_fhc *fhc; - struct device_node *dp, *fp; - int err; - - dp = of_find_node_by_name(NULL, "central"); - if (!dp) { - if (this_is_starfire) - starfire_cpu_setup(); - return; + p->pregs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "fhc_pregs"); + if (!p->pregs) { + printk(KERN_ERR "fhc: Cannot map pregs\n"); + goto out_free; } - /* Ok we got one, grab some memory for software state. */ - central_bus = (struct linux_central *) - central_alloc_bootmem(sizeof(struct linux_central)); - if (central_bus == NULL) - central_probe_failure(__LINE__); - - fhc = (struct linux_fhc *) - central_alloc_bootmem(sizeof(struct linux_fhc)); - if (fhc == NULL) - central_probe_failure(__LINE__); - - /* First init central. */ - central_bus->child = fhc; - central_bus->prom_node = dp; - central_ranges_init(central_bus); - - /* And then central's FHC. */ - fhc->next = fhc_list; - fhc_list = fhc; - - fhc->parent = central_bus; - fp = dp->child; - while (fp) { - if (!strcmp(fp->name, "fhc")) - break; - fp = fp->sibling; + if (p->central) { + reg = upa_readl(p->pregs + FHC_PREGS_BSR); + p->board_num = ((reg >> 16) & 1) | ((reg >> 12) & 0x0e); + } else { + p->board_num = of_getintprop_default(op->node, "board#", -1); + if (p->board_num == -1) { + printk(KERN_ERR "fhc: No board# property\n"); + goto out_unmap_pregs; + } + if (upa_readl(p->pregs + FHC_PREGS_JCTRL) & FHC_JTAG_CTRL_MENAB) + p->jtag_master = true; } - if (!fp) - central_probe_failure(__LINE__); - - fhc->prom_node = fp; - fhc_ranges_init(fhc); - - /* Now, map in FHC register set. */ - pr = of_get_property(fp, "reg", NULL); - if (!pr) - central_probe_failure(__LINE__); - memcpy(fpregs, pr, sizeof(fpregs)); - - apply_central_ranges(central_bus, &fpregs[0], 6); - - fhc->fhc_regs.pregs = prom_reg_to_paddr(&fpregs[0]); - fhc->fhc_regs.ireg = prom_reg_to_paddr(&fpregs[1]); - fhc->fhc_regs.ffregs = prom_reg_to_paddr(&fpregs[2]); - fhc->fhc_regs.sregs = prom_reg_to_paddr(&fpregs[3]); - fhc->fhc_regs.uregs = prom_reg_to_paddr(&fpregs[4]); - fhc->fhc_regs.tregs = prom_reg_to_paddr(&fpregs[5]); - - /* Obtain board number from board status register, Central's - * FHC lacks "board#" property. - */ - err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_BSR); - fhc->board = (((err >> 16) & 0x01) | - ((err >> 12) & 0x0e)); - - fhc->jtag_master = 0; - - /* Attach the clock board registers for CENTRAL. */ - probe_clock_board(central_bus, fhc, fp); - - err = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_ID); - printk("FHC(board %d): Version[%x] PartID[%x] Manuf[%x] (CENTRAL)\n", - fhc->board, - ((err & FHC_ID_VERS) >> 28), - ((err & FHC_ID_PARTID) >> 12), - ((err & FHC_ID_MANUF) >> 1)); - - probe_other_fhcs(); - - init_all_fhc_hw(); -} -static inline void fhc_ledblink(struct linux_fhc *fhc, int on) -{ - u32 tmp; + if (!p->central) { + p->leds_resource.start = (unsigned long) + (p->pregs + FHC_PREGS_CTRL); + p->leds_resource.end = p->leds_resource.end; + p->leds_resource.name = "leds"; + + p->leds_pdev.name = "sunfire-fhc-leds"; + p->leds_pdev.resource = &p->leds_resource; + p->leds_pdev.num_resources = 1; + p->leds_pdev.dev.parent = &op->dev; + + err = platform_device_register(&p->leds_pdev); + if (err) { + printk(KERN_ERR "fhc: Could not register LEDS " + "platform device\n"); + goto out_unmap_pregs; + } + } + reg = upa_readl(p->pregs + FHC_PREGS_CTRL); - tmp = upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); + if (!p->central) + reg |= FHC_CONTROL_IXIST; - /* NOTE: reverse logic on this bit */ - if (on) - tmp &= ~(FHC_CONTROL_RLED); - else - tmp |= FHC_CONTROL_RLED; - tmp &= ~(FHC_CONTROL_AOFF | FHC_CONTROL_BOFF | FHC_CONTROL_SLINE); + reg &= ~(FHC_CONTROL_AOFF | + FHC_CONTROL_BOFF | + FHC_CONTROL_SLINE); - upa_writel(tmp, fhc->fhc_regs.pregs + FHC_PREGS_CTRL); - upa_readl(fhc->fhc_regs.pregs + FHC_PREGS_CTRL); -} + upa_writel(reg, p->pregs + FHC_PREGS_CTRL); + upa_readl(p->pregs + FHC_PREGS_CTRL); -static inline void central_ledblink(struct linux_central *central, int on) -{ - u8 tmp; - - tmp = upa_readb(central->clkregs + CLOCK_CTRL); + reg = upa_readl(p->pregs + FHC_PREGS_ID); + printk(KERN_INFO "fhc: Board #%d, Version[%x] PartID[%x] Manuf[%x] %s\n", + p->board_num, + (reg & FHC_ID_VERS) >> 28, + (reg & FHC_ID_PARTID) >> 12, + (reg & FHC_ID_MANUF) >> 1, + (p->jtag_master ? + "(JTAG Master)" : + (p->central ? "(Central)" : ""))); - /* NOTE: reverse logic on this bit */ - if (on) - tmp &= ~(CLOCK_CTRL_RLED); - else - tmp |= CLOCK_CTRL_RLED; + err = 0; - upa_writeb(tmp, central->clkregs + CLOCK_CTRL); - upa_readb(central->clkregs + CLOCK_CTRL); -} +out: + return err; -static struct timer_list sftimer; -static int led_state; +out_unmap_pregs: + of_iounmap(&op->resource[0], p->pregs, resource_size(&op->resource[0])); -static void sunfire_timer(unsigned long __ignored) -{ - struct linux_fhc *fhc; - - central_ledblink(central_bus, led_state); - for (fhc = fhc_list; fhc != NULL; fhc = fhc->next) - if (! IS_CENTRAL_FHC(fhc)) - fhc_ledblink(fhc, led_state); - led_state = ! led_state; - sftimer.expires = jiffies + (HZ >> 1); - add_timer(&sftimer); +out_free: + kfree(p); + goto out; } -/* After PCI/SBUS busses have been probed, this is called to perform - * final initialization of all FireHose Controllers in the system. - */ -void firetruck_init(void) +static struct of_device_id __initdata fhc_match[] = { + { + .name = "fhc", + }, + {}, +}; + +static struct of_platform_driver fhc_driver = { + .match_table = fhc_match, + .probe = fhc_probe, + .driver = { + .name = "fhc", + }, +}; + +static int __init sunfire_init(void) { - struct linux_central *central = central_bus; - u8 ctrl; - - /* No central bus, nothing to do. */ - if (central == NULL) - return; - - /* OBP leaves it on, turn it off so clock board timer LED - * is in sync with FHC ones. - */ - ctrl = upa_readb(central->clkregs + CLOCK_CTRL); - ctrl &= ~(CLOCK_CTRL_RLED); - upa_writeb(ctrl, central->clkregs + CLOCK_CTRL); - - led_state = 0; - init_timer(&sftimer); - sftimer.data = 0; - sftimer.function = &sunfire_timer; - sftimer.expires = jiffies + (HZ >> 1); - add_timer(&sftimer); + (void) of_register_driver(&fhc_driver, &of_platform_bus_type); + (void) of_register_driver(&clock_board_driver, &of_platform_bus_type); + return 0; } + +subsys_initcall(sunfire_init); diff --git a/arch/sparc64/kernel/chmc.c b/arch/sparc64/kernel/chmc.c index 6d4f02e8a4c..3b9f4d6e14a 100644 --- a/arch/sparc64/kernel/chmc.c +++ b/arch/sparc64/kernel/chmc.c @@ -1,6 +1,6 @@ -/* memctrlr.c: Driver for UltraSPARC-III memory controller. +/* chmc.c: Driver for UltraSPARC-III memory controller. * - * Copyright (C) 2001, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 2001, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> @@ -13,45 +13,64 @@ #include <linux/smp.h> #include <linux/errno.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/spitfire.h> #include <asm/chmctrl.h> #include <asm/cpudata.h> #include <asm/oplib.h> #include <asm/prom.h> +#include <asm/head.h> #include <asm/io.h> +#include <asm/memctrl.h> + +#define DRV_MODULE_NAME "chmc" +#define PFX DRV_MODULE_NAME ": " +#define DRV_MODULE_VERSION "0.2" + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("UltraSPARC-III memory controller driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +static int mc_type; +#define MC_TYPE_SAFARI 1 +#define MC_TYPE_JBUS 2 + +static dimm_printer_t us3mc_dimm_printer; #define CHMCTRL_NDGRPS 2 #define CHMCTRL_NDIMMS 4 -#define DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS) +#define CHMC_DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS) /* OBP memory-layout property format. */ -struct obp_map { +struct chmc_obp_map { unsigned char dimm_map[144]; unsigned char pin_map[576]; }; #define DIMM_LABEL_SZ 8 -struct obp_mem_layout { +struct chmc_obp_mem_layout { /* One max 8-byte string label per DIMM. Usually * this matches the label on the motherboard where * that DIMM resides. */ - char dimm_labels[DIMMS_PER_MC][DIMM_LABEL_SZ]; + char dimm_labels[CHMC_DIMMS_PER_MC][DIMM_LABEL_SZ]; /* If symmetric use map[0], else it is * asymmetric and map[1] should be used. */ - char symmetric; + char symmetric; - struct obp_map map[2]; + struct chmc_obp_map map[2]; }; #define CHMCTRL_NBANKS 4 -struct bank_info { - struct mctrl_info *mp; +struct chmc_bank_info { + struct chmc *p; int bank_id; u64 raw_reg; @@ -65,28 +84,406 @@ struct bank_info { unsigned long size; }; -struct mctrl_info { - struct list_head list; - int portid; +struct chmc { + struct list_head list; + int portid; + + struct chmc_obp_mem_layout layout_prop; + int layout_size; + + void __iomem *regs; - struct obp_mem_layout layout_prop; - int layout_size; + u64 timing_control1; + u64 timing_control2; + u64 timing_control3; + u64 timing_control4; + u64 memaddr_control; - void __iomem *regs; + struct chmc_bank_info logical_banks[CHMCTRL_NBANKS]; +}; + +#define JBUSMC_REGS_SIZE 8 + +#define JB_MC_REG1_DIMM2_BANK3 0x8000000000000000UL +#define JB_MC_REG1_DIMM1_BANK1 0x4000000000000000UL +#define JB_MC_REG1_DIMM2_BANK2 0x2000000000000000UL +#define JB_MC_REG1_DIMM1_BANK0 0x1000000000000000UL +#define JB_MC_REG1_XOR 0x0000010000000000UL +#define JB_MC_REG1_ADDR_GEN_2 0x000000e000000000UL +#define JB_MC_REG1_ADDR_GEN_2_SHIFT 37 +#define JB_MC_REG1_ADDR_GEN_1 0x0000001c00000000UL +#define JB_MC_REG1_ADDR_GEN_1_SHIFT 34 +#define JB_MC_REG1_INTERLEAVE 0x0000000001800000UL +#define JB_MC_REG1_INTERLEAVE_SHIFT 23 +#define JB_MC_REG1_DIMM2_PTYPE 0x0000000000200000UL +#define JB_MC_REG1_DIMM2_PTYPE_SHIFT 21 +#define JB_MC_REG1_DIMM1_PTYPE 0x0000000000100000UL +#define JB_MC_REG1_DIMM1_PTYPE_SHIFT 20 + +#define PART_TYPE_X8 0 +#define PART_TYPE_X4 1 + +#define INTERLEAVE_NONE 0 +#define INTERLEAVE_SAME 1 +#define INTERLEAVE_INTERNAL 2 +#define INTERLEAVE_BOTH 3 + +#define ADDR_GEN_128MB 0 +#define ADDR_GEN_256MB 1 +#define ADDR_GEN_512MB 2 +#define ADDR_GEN_1GB 3 + +#define JB_NUM_DIMM_GROUPS 2 +#define JB_NUM_DIMMS_PER_GROUP 2 +#define JB_NUM_DIMMS (JB_NUM_DIMM_GROUPS * JB_NUM_DIMMS_PER_GROUP) + +struct jbusmc_obp_map { + unsigned char dimm_map[18]; + unsigned char pin_map[144]; +}; + +struct jbusmc_obp_mem_layout { + /* One max 8-byte string label per DIMM. Usually + * this matches the label on the motherboard where + * that DIMM resides. + */ + char dimm_labels[JB_NUM_DIMMS][DIMM_LABEL_SZ]; + + /* If symmetric use map[0], else it is + * asymmetric and map[1] should be used. + */ + char symmetric; + + struct jbusmc_obp_map map; + + char _pad; +}; - u64 timing_control1; - u64 timing_control2; - u64 timing_control3; - u64 timing_control4; - u64 memaddr_control; +struct jbusmc_dimm_group { + struct jbusmc *controller; + int index; + u64 base_addr; + u64 size; +}; - struct bank_info logical_banks[CHMCTRL_NBANKS]; +struct jbusmc { + void __iomem *regs; + u64 mc_reg_1; + u32 portid; + struct jbusmc_obp_mem_layout layout; + int layout_len; + int num_dimm_groups; + struct jbusmc_dimm_group dimm_groups[JB_NUM_DIMM_GROUPS]; + struct list_head list; }; +static DEFINE_SPINLOCK(mctrl_list_lock); static LIST_HEAD(mctrl_list); +static void mc_list_add(struct list_head *list) +{ + spin_lock(&mctrl_list_lock); + list_add(list, &mctrl_list); + spin_unlock(&mctrl_list_lock); +} + +static void mc_list_del(struct list_head *list) +{ + spin_lock(&mctrl_list_lock); + list_del_init(list); + spin_unlock(&mctrl_list_lock); +} + +#define SYNDROME_MIN -1 +#define SYNDROME_MAX 144 + +/* Covert syndrome code into the way the bits are positioned + * on the bus. + */ +static int syndrome_to_qword_code(int syndrome_code) +{ + if (syndrome_code < 128) + syndrome_code += 16; + else if (syndrome_code < 128 + 9) + syndrome_code -= (128 - 7); + else if (syndrome_code < (128 + 9 + 3)) + syndrome_code -= (128 + 9 - 4); + else + syndrome_code -= (128 + 9 + 3); + return syndrome_code; +} + +/* All this magic has to do with how a cache line comes over the wire + * on Safari and JBUS. A 64-bit line comes over in 1 or more quadword + * cycles, each of which transmit ECC/MTAG info as well as the actual + * data. + */ +#define L2_LINE_SIZE 64 +#define L2_LINE_ADDR_MSK (L2_LINE_SIZE - 1) +#define QW_PER_LINE 4 +#define QW_BYTES (L2_LINE_SIZE / QW_PER_LINE) +#define QW_BITS 144 +#define SAFARI_LAST_BIT (576 - 1) +#define JBUS_LAST_BIT (144 - 1) + +static void get_pin_and_dimm_str(int syndrome_code, unsigned long paddr, + int *pin_p, char **dimm_str_p, void *_prop, + int base_dimm_offset) +{ + int qword_code = syndrome_to_qword_code(syndrome_code); + int cache_line_offset; + int offset_inverse; + int dimm_map_index; + int map_val; + + if (mc_type == MC_TYPE_JBUS) { + struct jbusmc_obp_mem_layout *p = _prop; + + /* JBUS */ + cache_line_offset = qword_code; + offset_inverse = (JBUS_LAST_BIT - cache_line_offset); + dimm_map_index = offset_inverse / 8; + map_val = p->map.dimm_map[dimm_map_index]; + map_val = ((map_val >> ((7 - (offset_inverse & 7)))) & 1); + *dimm_str_p = p->dimm_labels[base_dimm_offset + map_val]; + *pin_p = p->map.pin_map[cache_line_offset]; + } else { + struct chmc_obp_mem_layout *p = _prop; + struct chmc_obp_map *mp; + int qword; + + /* Safari */ + if (p->symmetric) + mp = &p->map[0]; + else + mp = &p->map[1]; + + qword = (paddr & L2_LINE_ADDR_MSK) / QW_BYTES; + cache_line_offset = ((3 - qword) * QW_BITS) + qword_code; + offset_inverse = (SAFARI_LAST_BIT - cache_line_offset); + dimm_map_index = offset_inverse >> 2; + map_val = mp->dimm_map[dimm_map_index]; + map_val = ((map_val >> ((3 - (offset_inverse & 3)) << 1)) & 0x3); + *dimm_str_p = p->dimm_labels[base_dimm_offset + map_val]; + *pin_p = mp->pin_map[cache_line_offset]; + } +} + +static struct jbusmc_dimm_group *jbusmc_find_dimm_group(unsigned long phys_addr) +{ + struct jbusmc *p; + + list_for_each_entry(p, &mctrl_list, list) { + int i; + + for (i = 0; i < p->num_dimm_groups; i++) { + struct jbusmc_dimm_group *dp = &p->dimm_groups[i]; + + if (phys_addr < dp->base_addr || + (dp->base_addr + dp->size) <= phys_addr) + continue; + + return dp; + } + } + return NULL; +} + +static int jbusmc_print_dimm(int syndrome_code, + unsigned long phys_addr, + char *buf, int buflen) +{ + struct jbusmc_obp_mem_layout *prop; + struct jbusmc_dimm_group *dp; + struct jbusmc *p; + int first_dimm; + + dp = jbusmc_find_dimm_group(phys_addr); + if (dp == NULL || + syndrome_code < SYNDROME_MIN || + syndrome_code > SYNDROME_MAX) { + buf[0] = '?'; + buf[1] = '?'; + buf[2] = '?'; + buf[3] = '\0'; + } + p = dp->controller; + prop = &p->layout; + + first_dimm = dp->index * JB_NUM_DIMMS_PER_GROUP; + + if (syndrome_code != SYNDROME_MIN) { + char *dimm_str; + int pin; + + get_pin_and_dimm_str(syndrome_code, phys_addr, &pin, + &dimm_str, prop, first_dimm); + sprintf(buf, "%s, pin %3d", dimm_str, pin); + } else { + int dimm; + + /* Multi-bit error, we just dump out all the + * dimm labels associated with this dimm group. + */ + for (dimm = 0; dimm < JB_NUM_DIMMS_PER_GROUP; dimm++) { + sprintf(buf, "%s ", + prop->dimm_labels[first_dimm + dimm]); + buf += strlen(buf); + } + } + + return 0; +} + +static u64 __devinit jbusmc_dimm_group_size(u64 base, + const struct linux_prom64_registers *mem_regs, + int num_mem_regs) +{ + u64 max = base + (8UL * 1024 * 1024 * 1024); + u64 max_seen = base; + int i; + + for (i = 0; i < num_mem_regs; i++) { + const struct linux_prom64_registers *ent; + u64 this_base; + u64 this_end; + + ent = &mem_regs[i]; + this_base = ent->phys_addr; + this_end = this_base + ent->reg_size; + if (base < this_base || base >= this_end) + continue; + if (this_end > max) + this_end = max; + if (this_end > max_seen) + max_seen = this_end; + } + + return max_seen - base; +} + +static void __devinit jbusmc_construct_one_dimm_group(struct jbusmc *p, + unsigned long index, + const struct linux_prom64_registers *mem_regs, + int num_mem_regs) +{ + struct jbusmc_dimm_group *dp = &p->dimm_groups[index]; + + dp->controller = p; + dp->index = index; + + dp->base_addr = (p->portid * (64UL * 1024 * 1024 * 1024)); + dp->base_addr += (index * (8UL * 1024 * 1024 * 1024)); + dp->size = jbusmc_dimm_group_size(dp->base_addr, mem_regs, num_mem_regs); +} + +static void __devinit jbusmc_construct_dimm_groups(struct jbusmc *p, + const struct linux_prom64_registers *mem_regs, + int num_mem_regs) +{ + if (p->mc_reg_1 & JB_MC_REG1_DIMM1_BANK0) { + jbusmc_construct_one_dimm_group(p, 0, mem_regs, num_mem_regs); + p->num_dimm_groups++; + } + if (p->mc_reg_1 & JB_MC_REG1_DIMM2_BANK2) { + jbusmc_construct_one_dimm_group(p, 1, mem_regs, num_mem_regs); + p->num_dimm_groups++; + } +} + +static int __devinit jbusmc_probe(struct of_device *op, + const struct of_device_id *match) +{ + const struct linux_prom64_registers *mem_regs; + struct device_node *mem_node; + int err, len, num_mem_regs; + struct jbusmc *p; + const u32 *prop; + const void *ml; + + err = -ENODEV; + mem_node = of_find_node_by_path("/memory"); + if (!mem_node) { + printk(KERN_ERR PFX "Cannot find /memory node.\n"); + goto out; + } + mem_regs = of_get_property(mem_node, "reg", &len); + if (!mem_regs) { + printk(KERN_ERR PFX "Cannot get reg property of /memory node.\n"); + goto out; + } + num_mem_regs = len / sizeof(*mem_regs); + + err = -ENOMEM; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Cannot allocate struct jbusmc.\n"); + goto out; + } + + INIT_LIST_HEAD(&p->list); + + err = -ENODEV; + prop = of_get_property(op->node, "portid", &len); + if (!prop || len != 4) { + printk(KERN_ERR PFX "Cannot find portid.\n"); + goto out_free; + } + + p->portid = *prop; + + prop = of_get_property(op->node, "memory-control-register-1", &len); + if (!prop || len != 8) { + printk(KERN_ERR PFX "Cannot get memory control register 1.\n"); + goto out_free; + } + + p->mc_reg_1 = ((u64)prop[0] << 32) | (u64) prop[1]; + + err = -ENOMEM; + p->regs = of_ioremap(&op->resource[0], 0, JBUSMC_REGS_SIZE, "jbusmc"); + if (!p->regs) { + printk(KERN_ERR PFX "Cannot map jbusmc regs.\n"); + goto out_free; + } + + err = -ENODEV; + ml = of_get_property(op->node, "memory-layout", &p->layout_len); + if (!ml) { + printk(KERN_ERR PFX "Cannot get memory layout property.\n"); + goto out_iounmap; + } + if (p->layout_len > sizeof(p->layout)) { + printk(KERN_ERR PFX "Unexpected memory-layout size %d\n", + p->layout_len); + goto out_iounmap; + } + memcpy(&p->layout, ml, p->layout_len); + + jbusmc_construct_dimm_groups(p, mem_regs, num_mem_regs); + + mc_list_add(&p->list); + + printk(KERN_INFO PFX "UltraSPARC-IIIi memory controller at %s\n", + op->node->full_name); + + dev_set_drvdata(&op->dev, p); + + err = 0; + +out: + return err; + +out_iounmap: + of_iounmap(&op->resource[0], p->regs, JBUSMC_REGS_SIZE); + +out_free: + kfree(p); + goto out; +} + /* Does BANK decode PHYS_ADDR? */ -static int bank_match(struct bank_info *bp, unsigned long phys_addr) +static int chmc_bank_match(struct chmc_bank_info *bp, unsigned long phys_addr) { unsigned long upper_bits = (phys_addr & PA_UPPER_BITS) >> PA_UPPER_BITS_SHIFT; unsigned long lower_bits = (phys_addr & PA_LOWER_BITS) >> PA_LOWER_BITS_SHIFT; @@ -118,25 +515,18 @@ static int bank_match(struct bank_info *bp, unsigned long phys_addr) } /* Given PHYS_ADDR, search memory controller banks for a match. */ -static struct bank_info *find_bank(unsigned long phys_addr) +static struct chmc_bank_info *chmc_find_bank(unsigned long phys_addr) { - struct list_head *mctrl_head = &mctrl_list; - struct list_head *mctrl_entry = mctrl_head->next; + struct chmc *p; - for (;;) { - struct mctrl_info *mp = - list_entry(mctrl_entry, struct mctrl_info, list); + list_for_each_entry(p, &mctrl_list, list) { int bank_no; - if (mctrl_entry == mctrl_head) - break; - mctrl_entry = mctrl_entry->next; - for (bank_no = 0; bank_no < CHMCTRL_NBANKS; bank_no++) { - struct bank_info *bp; + struct chmc_bank_info *bp; - bp = &mp->logical_banks[bank_no]; - if (bank_match(bp, phys_addr)) + bp = &p->logical_banks[bank_no]; + if (chmc_bank_match(bp, phys_addr)) return bp; } } @@ -145,17 +535,15 @@ static struct bank_info *find_bank(unsigned long phys_addr) } /* This is the main purpose of this driver. */ -#define SYNDROME_MIN -1 -#define SYNDROME_MAX 144 -int chmc_getunumber(int syndrome_code, - unsigned long phys_addr, - char *buf, int buflen) +static int chmc_print_dimm(int syndrome_code, + unsigned long phys_addr, + char *buf, int buflen) { - struct bank_info *bp; - struct obp_mem_layout *prop; + struct chmc_bank_info *bp; + struct chmc_obp_mem_layout *prop; int bank_in_controller, first_dimm; - bp = find_bank(phys_addr); + bp = chmc_find_bank(phys_addr); if (bp == NULL || syndrome_code < SYNDROME_MIN || syndrome_code > SYNDROME_MAX) { @@ -166,60 +554,18 @@ int chmc_getunumber(int syndrome_code, return 0; } - prop = &bp->mp->layout_prop; + prop = &bp->p->layout_prop; bank_in_controller = bp->bank_id & (CHMCTRL_NBANKS - 1); first_dimm = (bank_in_controller & (CHMCTRL_NDGRPS - 1)); first_dimm *= CHMCTRL_NDIMMS; if (syndrome_code != SYNDROME_MIN) { - struct obp_map *map; - int qword, where_in_line, where, map_index, map_offset; - unsigned int map_val; + char *dimm_str; + int pin; - /* Yaay, single bit error so we can figure out - * the exact dimm. - */ - if (prop->symmetric) - map = &prop->map[0]; - else - map = &prop->map[1]; - - /* Covert syndrome code into the way the bits are - * positioned on the bus. - */ - if (syndrome_code < 144 - 16) - syndrome_code += 16; - else if (syndrome_code < 144) - syndrome_code -= (144 - 7); - else if (syndrome_code < (144 + 3)) - syndrome_code -= (144 + 3 - 4); - else - syndrome_code -= 144 + 3; - - /* All this magic has to do with how a cache line - * comes over the wire on Safari. A 64-bit line - * comes over in 4 quadword cycles, each of which - * transmit ECC/MTAG info as well as the actual - * data. 144 bits per quadword, 576 total. - */ -#define LINE_SIZE 64 -#define LINE_ADDR_MSK (LINE_SIZE - 1) -#define QW_PER_LINE 4 -#define QW_BYTES (LINE_SIZE / QW_PER_LINE) -#define QW_BITS 144 -#define LAST_BIT (576 - 1) - - qword = (phys_addr & LINE_ADDR_MSK) / QW_BYTES; - where_in_line = ((3 - qword) * QW_BITS) + syndrome_code; - where = (LAST_BIT - where_in_line); - map_index = where >> 2; - map_offset = where & 0x3; - map_val = map->dimm_map[map_index]; - map_val = ((map_val >> ((3 - map_offset) << 1)) & (2 - 1)); - - sprintf(buf, "%s, pin %3d", - prop->dimm_labels[first_dimm + map_val], - map->pin_map[where_in_line]); + get_pin_and_dimm_str(syndrome_code, phys_addr, &pin, + &dimm_str, prop, first_dimm); + sprintf(buf, "%s, pin %3d", dimm_str, pin); } else { int dimm; @@ -240,7 +586,7 @@ int chmc_getunumber(int syndrome_code, * the code is executing, you must use special ASI load/store else * you go through the global mapping. */ -static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) +static u64 chmc_read_mcreg(struct chmc *p, unsigned long offset) { unsigned long ret, this_cpu; @@ -248,14 +594,14 @@ static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) this_cpu = real_hard_smp_processor_id(); - if (mp->portid == this_cpu) { + if (p->portid == this_cpu) { __asm__ __volatile__("ldxa [%1] %2, %0" : "=r" (ret) : "r" (offset), "i" (ASI_MCU_CTRL_REG)); } else { __asm__ __volatile__("ldxa [%1] %2, %0" : "=r" (ret) - : "r" (mp->regs + offset), + : "r" (p->regs + offset), "i" (ASI_PHYS_BYPASS_EC_E)); } @@ -265,178 +611,253 @@ static u64 read_mcreg(struct mctrl_info *mp, unsigned long offset) } #if 0 /* currently unused */ -static void write_mcreg(struct mctrl_info *mp, unsigned long offset, u64 val) +static void chmc_write_mcreg(struct chmc *p, unsigned long offset, u64 val) { - if (mp->portid == smp_processor_id()) { + if (p->portid == smp_processor_id()) { __asm__ __volatile__("stxa %0, [%1] %2" : : "r" (val), "r" (offset), "i" (ASI_MCU_CTRL_REG)); } else { __asm__ __volatile__("ldxa %0, [%1] %2" : : "r" (val), - "r" (mp->regs + offset), + "r" (p->regs + offset), "i" (ASI_PHYS_BYPASS_EC_E)); } } #endif -static void interpret_one_decode_reg(struct mctrl_info *mp, int which_bank, u64 val) +static void chmc_interpret_one_decode_reg(struct chmc *p, int which_bank, u64 val) { - struct bank_info *p = &mp->logical_banks[which_bank]; - - p->mp = mp; - p->bank_id = (CHMCTRL_NBANKS * mp->portid) + which_bank; - p->raw_reg = val; - p->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT; - p->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT; - p->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT; - p->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT; - p->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT; - - p->base = (p->um); - p->base &= ~(p->uk); - p->base <<= PA_UPPER_BITS_SHIFT; - - switch(p->lk) { + struct chmc_bank_info *bp = &p->logical_banks[which_bank]; + + bp->p = p; + bp->bank_id = (CHMCTRL_NBANKS * p->portid) + which_bank; + bp->raw_reg = val; + bp->valid = (val & MEM_DECODE_VALID) >> MEM_DECODE_VALID_SHIFT; + bp->uk = (val & MEM_DECODE_UK) >> MEM_DECODE_UK_SHIFT; + bp->um = (val & MEM_DECODE_UM) >> MEM_DECODE_UM_SHIFT; + bp->lk = (val & MEM_DECODE_LK) >> MEM_DECODE_LK_SHIFT; + bp->lm = (val & MEM_DECODE_LM) >> MEM_DECODE_LM_SHIFT; + + bp->base = (bp->um); + bp->base &= ~(bp->uk); + bp->base <<= PA_UPPER_BITS_SHIFT; + + switch(bp->lk) { case 0xf: default: - p->interleave = 1; + bp->interleave = 1; break; case 0xe: - p->interleave = 2; + bp->interleave = 2; break; case 0xc: - p->interleave = 4; + bp->interleave = 4; break; case 0x8: - p->interleave = 8; + bp->interleave = 8; break; case 0x0: - p->interleave = 16; + bp->interleave = 16; break; }; /* UK[10] is reserved, and UK[11] is not set for the SDRAM * bank size definition. */ - p->size = (((unsigned long)p->uk & - ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT; - p->size /= p->interleave; + bp->size = (((unsigned long)bp->uk & + ((1UL << 10UL) - 1UL)) + 1UL) << PA_UPPER_BITS_SHIFT; + bp->size /= bp->interleave; } -static void fetch_decode_regs(struct mctrl_info *mp) +static void chmc_fetch_decode_regs(struct chmc *p) { - if (mp->layout_size == 0) + if (p->layout_size == 0) return; - interpret_one_decode_reg(mp, 0, - read_mcreg(mp, CHMCTRL_DECODE1)); - interpret_one_decode_reg(mp, 1, - read_mcreg(mp, CHMCTRL_DECODE2)); - interpret_one_decode_reg(mp, 2, - read_mcreg(mp, CHMCTRL_DECODE3)); - interpret_one_decode_reg(mp, 3, - read_mcreg(mp, CHMCTRL_DECODE4)); + chmc_interpret_one_decode_reg(p, 0, + chmc_read_mcreg(p, CHMCTRL_DECODE1)); + chmc_interpret_one_decode_reg(p, 1, + chmc_read_mcreg(p, CHMCTRL_DECODE2)); + chmc_interpret_one_decode_reg(p, 2, + chmc_read_mcreg(p, CHMCTRL_DECODE3)); + chmc_interpret_one_decode_reg(p, 3, + chmc_read_mcreg(p, CHMCTRL_DECODE4)); } -static int init_one_mctrl(struct device_node *dp) +static int __devinit chmc_probe(struct of_device *op, + const struct of_device_id *match) { - struct mctrl_info *mp = kzalloc(sizeof(*mp), GFP_KERNEL); - int portid = of_getintprop_default(dp, "portid", -1); - const struct linux_prom64_registers *regs; + struct device_node *dp = op->node; + unsigned long ver; const void *pval; - int len; + int len, portid; + struct chmc *p; + int err; + + err = -ENODEV; + __asm__ ("rdpr %%ver, %0" : "=r" (ver)); + if ((ver >> 32UL) == __JALAPENO_ID || + (ver >> 32UL) == __SERRANO_ID) + goto out; - if (!mp) - return -1; + portid = of_getintprop_default(dp, "portid", -1); if (portid == -1) - goto fail; + goto out; - mp->portid = portid; pval = of_get_property(dp, "memory-layout", &len); - mp->layout_size = len; - if (!pval) - mp->layout_size = 0; - else { - if (mp->layout_size > sizeof(mp->layout_prop)) - goto fail; - memcpy(&mp->layout_prop, pval, len); + if (pval && len > sizeof(p->layout_prop)) { + printk(KERN_ERR PFX "Unexpected memory-layout property " + "size %d.\n", len); + goto out; } - regs = of_get_property(dp, "reg", NULL); - if (!regs || regs->reg_size != 0x48) - goto fail; + err = -ENOMEM; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Could not allocate struct chmc.\n"); + goto out; + } - mp->regs = ioremap(regs->phys_addr, regs->reg_size); - if (mp->regs == NULL) - goto fail; + p->portid = portid; + p->layout_size = len; + if (!pval) + p->layout_size = 0; + else + memcpy(&p->layout_prop, pval, len); + + p->regs = of_ioremap(&op->resource[0], 0, 0x48, "chmc"); + if (!p->regs) { + printk(KERN_ERR PFX "Could not map registers.\n"); + goto out_free; + } - if (mp->layout_size != 0UL) { - mp->timing_control1 = read_mcreg(mp, CHMCTRL_TCTRL1); - mp->timing_control2 = read_mcreg(mp, CHMCTRL_TCTRL2); - mp->timing_control3 = read_mcreg(mp, CHMCTRL_TCTRL3); - mp->timing_control4 = read_mcreg(mp, CHMCTRL_TCTRL4); - mp->memaddr_control = read_mcreg(mp, CHMCTRL_MACTRL); + if (p->layout_size != 0UL) { + p->timing_control1 = chmc_read_mcreg(p, CHMCTRL_TCTRL1); + p->timing_control2 = chmc_read_mcreg(p, CHMCTRL_TCTRL2); + p->timing_control3 = chmc_read_mcreg(p, CHMCTRL_TCTRL3); + p->timing_control4 = chmc_read_mcreg(p, CHMCTRL_TCTRL4); + p->memaddr_control = chmc_read_mcreg(p, CHMCTRL_MACTRL); } - fetch_decode_regs(mp); + chmc_fetch_decode_regs(p); - list_add(&mp->list, &mctrl_list); + mc_list_add(&p->list); - /* Report the device. */ - printk(KERN_INFO "%s: US3 memory controller at %p [%s]\n", + printk(KERN_INFO PFX "UltraSPARC-III memory controller at %s [%s]\n", dp->full_name, - mp->regs, (mp->layout_size ? "ACTIVE" : "INACTIVE")); + (p->layout_size ? "ACTIVE" : "INACTIVE")); - return 0; + dev_set_drvdata(&op->dev, p); + + err = 0; + +out: + return err; + +out_free: + kfree(p); + goto out; +} -fail: - if (mp) { - if (mp->regs != NULL) - iounmap(mp->regs); - kfree(mp); +static int __devinit us3mc_probe(struct of_device *op, + const struct of_device_id *match) +{ + if (mc_type == MC_TYPE_SAFARI) + return chmc_probe(op, match); + else if (mc_type == MC_TYPE_JBUS) + return jbusmc_probe(op, match); + return -ENODEV; +} + +static void __devexit chmc_destroy(struct of_device *op, struct chmc *p) +{ + list_del(&p->list); + of_iounmap(&op->resource[0], p->regs, 0x48); + kfree(p); +} + +static void __devexit jbusmc_destroy(struct of_device *op, struct jbusmc *p) +{ + mc_list_del(&p->list); + of_iounmap(&op->resource[0], p->regs, JBUSMC_REGS_SIZE); + kfree(p); +} + +static int __devexit us3mc_remove(struct of_device *op) +{ + void *p = dev_get_drvdata(&op->dev); + + if (p) { + if (mc_type == MC_TYPE_SAFARI) + chmc_destroy(op, p); + else if (mc_type == MC_TYPE_JBUS) + jbusmc_destroy(op, p); } - return -1; + return 0; +} + +static const struct of_device_id us3mc_match[] = { + { + .name = "memory-controller", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, us3mc_match); + +static struct of_platform_driver us3mc_driver = { + .name = "us3mc", + .match_table = us3mc_match, + .probe = us3mc_probe, + .remove = __devexit_p(us3mc_remove), +}; + +static inline bool us3mc_platform(void) +{ + if (tlb_type == cheetah || tlb_type == cheetah_plus) + return true; + return false; } -static int __init chmc_init(void) +static int __init us3mc_init(void) { - struct device_node *dp; + unsigned long ver; + int ret; - /* This driver is only for cheetah platforms. */ - if (tlb_type != cheetah && tlb_type != cheetah_plus) + if (!us3mc_platform()) return -ENODEV; - for_each_node_by_name(dp, "memory-controller") - init_one_mctrl(dp); + __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver)); + if ((ver >> 32UL) == __JALAPENO_ID || + (ver >> 32UL) == __SERRANO_ID) { + mc_type = MC_TYPE_JBUS; + us3mc_dimm_printer = jbusmc_print_dimm; + } else { + mc_type = MC_TYPE_SAFARI; + us3mc_dimm_printer = chmc_print_dimm; + } - for_each_node_by_name(dp, "mc-us3") - init_one_mctrl(dp); + ret = register_dimm_printer(us3mc_dimm_printer); - return 0; + if (!ret) { + ret = of_register_driver(&us3mc_driver, &of_bus_type); + if (ret) + unregister_dimm_printer(us3mc_dimm_printer); + } + return ret; } -static void __exit chmc_cleanup(void) +static void __exit us3mc_cleanup(void) { - struct list_head *head = &mctrl_list; - struct list_head *tmp = head->next; - - for (;;) { - struct mctrl_info *p = - list_entry(tmp, struct mctrl_info, list); - if (tmp == head) - break; - tmp = tmp->next; - - list_del(&p->list); - iounmap(p->regs); - kfree(p); + if (us3mc_platform()) { + unregister_dimm_printer(us3mc_dimm_printer); + of_unregister_driver(&us3mc_driver); } } -module_init(chmc_init); -module_exit(chmc_cleanup); +module_init(us3mc_init); +module_exit(us3mc_cleanup); diff --git a/arch/sparc64/kernel/cpu.c b/arch/sparc64/kernel/cpu.c index 0097c08dc60..0c9ac83ed0a 100644 --- a/arch/sparc64/kernel/cpu.c +++ b/arch/sparc64/kernel/cpu.c @@ -1,7 +1,7 @@ /* cpu.c: Dinky routines to look for the kind of Sparc cpu * we are on. * - * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> @@ -19,53 +19,86 @@ DEFINE_PER_CPU(cpuinfo_sparc, __cpu_data) = { 0 }; -struct cpu_iu_info { - short manuf; - short impl; - char* cpu_name; /* should be enough I hope... */ +struct cpu_chip_info { + unsigned short manuf; + unsigned short impl; + const char *cpu_name; + const char *fp_name; }; -struct cpu_fp_info { - short manuf; - short impl; - char fpu_vers; - char* fp_name; +static const struct cpu_chip_info cpu_chips[] = { + { + .manuf = 0x17, + .impl = 0x10, + .cpu_name = "TI UltraSparc I (SpitFire)", + .fp_name = "UltraSparc I integrated FPU", + }, + { + .manuf = 0x22, + .impl = 0x10, + .cpu_name = "TI UltraSparc I (SpitFire)", + .fp_name = "UltraSparc I integrated FPU", + }, + { + .manuf = 0x17, + .impl = 0x11, + .cpu_name = "TI UltraSparc II (BlackBird)", + .fp_name = "UltraSparc II integrated FPU", + }, + { + .manuf = 0x17, + .impl = 0x12, + .cpu_name = "TI UltraSparc IIi (Sabre)", + .fp_name = "UltraSparc IIi integrated FPU", + }, + { + .manuf = 0x17, + .impl = 0x13, + .cpu_name = "TI UltraSparc IIe (Hummingbird)", + .fp_name = "UltraSparc IIe integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x14, + .cpu_name = "TI UltraSparc III (Cheetah)", + .fp_name = "UltraSparc III integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x15, + .cpu_name = "TI UltraSparc III+ (Cheetah+)", + .fp_name = "UltraSparc III+ integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x16, + .cpu_name = "TI UltraSparc IIIi (Jalapeno)", + .fp_name = "UltraSparc IIIi integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x18, + .cpu_name = "TI UltraSparc IV (Jaguar)", + .fp_name = "UltraSparc IV integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x19, + .cpu_name = "TI UltraSparc IV+ (Panther)", + .fp_name = "UltraSparc IV+ integrated FPU", + }, + { + .manuf = 0x3e, + .impl = 0x22, + .cpu_name = "TI UltraSparc IIIi+ (Serrano)", + .fp_name = "UltraSparc IIIi+ integrated FPU", + }, }; -static struct cpu_fp_info linux_sparc_fpu[] = { - { 0x17, 0x10, 0, "UltraSparc I integrated FPU"}, - { 0x22, 0x10, 0, "UltraSparc I integrated FPU"}, - { 0x17, 0x11, 0, "UltraSparc II integrated FPU"}, - { 0x17, 0x12, 0, "UltraSparc IIi integrated FPU"}, - { 0x17, 0x13, 0, "UltraSparc IIe integrated FPU"}, - { 0x3e, 0x14, 0, "UltraSparc III integrated FPU"}, - { 0x3e, 0x15, 0, "UltraSparc III+ integrated FPU"}, - { 0x3e, 0x16, 0, "UltraSparc IIIi integrated FPU"}, - { 0x3e, 0x18, 0, "UltraSparc IV integrated FPU"}, - { 0x3e, 0x19, 0, "UltraSparc IV+ integrated FPU"}, - { 0x3e, 0x22, 0, "UltraSparc IIIi+ integrated FPU"}, -}; - -#define NSPARCFPU ARRAY_SIZE(linux_sparc_fpu) - -static struct cpu_iu_info linux_sparc_chips[] = { - { 0x17, 0x10, "TI UltraSparc I (SpitFire)"}, - { 0x22, 0x10, "TI UltraSparc I (SpitFire)"}, - { 0x17, 0x11, "TI UltraSparc II (BlackBird)"}, - { 0x17, 0x12, "TI UltraSparc IIi (Sabre)"}, - { 0x17, 0x13, "TI UltraSparc IIe (Hummingbird)"}, - { 0x3e, 0x14, "TI UltraSparc III (Cheetah)"}, - { 0x3e, 0x15, "TI UltraSparc III+ (Cheetah+)"}, - { 0x3e, 0x16, "TI UltraSparc IIIi (Jalapeno)"}, - { 0x3e, 0x18, "TI UltraSparc IV (Jaguar)"}, - { 0x3e, 0x19, "TI UltraSparc IV+ (Panther)"}, - { 0x3e, 0x22, "TI UltraSparc IIIi+ (Serrano)"}, -}; +#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips) -#define NSPARCCHIPS ARRAY_SIZE(linux_sparc_chips) - -char *sparc_cpu_type; -char *sparc_fpu_type; +const char *sparc_cpu_type; +const char *sparc_fpu_type; static void __init sun4v_cpu_probe(void) { @@ -89,68 +122,45 @@ static void __init sun4v_cpu_probe(void) } } -void __init cpu_probe(void) +static const struct cpu_chip_info * __init find_cpu_chip(unsigned short manuf, + unsigned short impl) { - unsigned long ver, fpu_vers, manuf, impl, fprs; int i; - - if (tlb_type == hypervisor) { - sun4v_cpu_probe(); - return; - } - fprs = fprs_read(); - fprs_write(FPRS_FEF); - __asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" - : "=&r" (ver) - : "r" (&fpu_vers)); - fprs_write(fprs); - - manuf = ((ver >> 48) & 0xffff); - impl = ((ver >> 32) & 0xffff); - - fpu_vers = ((fpu_vers >> 17) & 0x7); - -retry: - for (i = 0; i < NSPARCCHIPS; i++) { - if (linux_sparc_chips[i].manuf == manuf) { - if (linux_sparc_chips[i].impl == impl) { - sparc_cpu_type = - linux_sparc_chips[i].cpu_name; - break; - } - } - } + for (i = 0; i < ARRAY_SIZE(cpu_chips); i++) { + const struct cpu_chip_info *p = &cpu_chips[i]; - if (i == NSPARCCHIPS) { - /* Maybe it is a cheetah+ derivative, report it as cheetah+ - * in that case until we learn the real names. - */ - if (manuf == 0x3e && - impl > 0x15) { - impl = 0x15; - goto retry; - } else { - printk("DEBUG: manuf[%lx] impl[%lx]\n", - manuf, impl); - } - sparc_cpu_type = "Unknown CPU"; + if (p->manuf == manuf && p->impl == impl) + return p; } + return NULL; +} - for (i = 0; i < NSPARCFPU; i++) { - if (linux_sparc_fpu[i].manuf == manuf && - linux_sparc_fpu[i].impl == impl) { - if (linux_sparc_fpu[i].fpu_vers == fpu_vers) { - sparc_fpu_type = - linux_sparc_fpu[i].fp_name; - break; - } +static int __init cpu_type_probe(void) +{ + if (tlb_type == hypervisor) { + sun4v_cpu_probe(); + } else { + unsigned long ver, manuf, impl; + const struct cpu_chip_info *p; + + __asm__ __volatile__("rdpr %%ver, %0" : "=r" (ver)); + + manuf = ((ver >> 48) & 0xffff); + impl = ((ver >> 32) & 0xffff); + + p = find_cpu_chip(manuf, impl); + if (p) { + sparc_cpu_type = p->cpu_name; + sparc_fpu_type = p->fp_name; + } else { + printk(KERN_ERR "CPU: Unknown chip, manuf[%lx] impl[%lx]\n", + manuf, impl); + sparc_cpu_type = "Unknown CPU"; + sparc_fpu_type = "Unknown FPU"; } } - - if (i == NSPARCFPU) { - printk("DEBUG: manuf[%lx] impl[%lx] fsr.vers[%lx]\n", - manuf, impl, fpu_vers); - sparc_fpu_type = "Unknown FPU"; - } + return 0; } + +arch_initcall(cpu_type_probe); diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index d0fa5aa3893..f52e0534d91 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c @@ -1,6 +1,6 @@ /* ds.c: Domain Services driver for Logical Domains * - * Copyright (C) 2007 David S. Miller <davem@davemloft.net> + * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> @@ -1217,7 +1217,7 @@ static int ds_remove(struct vio_dev *vdev) return 0; } -static struct vio_device_id ds_match[] = { +static struct vio_device_id __initdata ds_match[] = { { .type = "domain-services-port", }, diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index 60d36d14255..77dbf6d45fa 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,5 +1,4 @@ -/* - * ebus.c: PCI to EBus bridge device. +/* ebus.c: EBUS DMA library code. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -9,23 +8,11 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> -#include <linux/slab.h> -#include <linux/string.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/pci.h> -#include <linux/of_device.h> - -#include <asm/system.h> -#include <asm/page.h> -#include <asm/ebus.h> -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/bpp.h> -#include <asm/irq.h> -#include <asm/io.h> -/* EBUS dma library. */ +#include <asm/ebus_dma.h> +#include <asm/io.h> #define EBDMA_CSR 0x00UL /* Control/Status */ #define EBDMA_ADDR 0x04UL /* DMA Address */ @@ -268,283 +255,3 @@ void ebus_dma_enable(struct ebus_dma_info *p, int on) spin_unlock_irqrestore(&p->lock, flags); } EXPORT_SYMBOL(ebus_dma_enable); - -struct linux_ebus *ebus_chain = NULL; - -static inline void *ebus_alloc(size_t size) -{ - void *mem; - - mem = kzalloc(size, GFP_ATOMIC); - if (!mem) - panic("ebus_alloc: out of memory"); - return mem; -} - -static void __init fill_ebus_child(struct device_node *dp, - struct linux_ebus_child *dev, - int non_standard_regs) -{ - struct of_device *op; - const int *regs; - int i, len; - - dev->prom_node = dp; - printk(" (%s)", dp->name); - - regs = of_get_property(dp, "reg", &len); - if (!regs) - dev->num_addrs = 0; - else - dev->num_addrs = len / sizeof(regs[0]); - - if (non_standard_regs) { - /* This is to handle reg properties which are not - * in the parent relative format. One example are - * children of the i2c device on CompactPCI systems. - * - * So, for such devices we just record the property - * raw in the child resources. - */ - for (i = 0; i < dev->num_addrs; i++) - dev->resource[i].start = regs[i]; - } else { - for (i = 0; i < dev->num_addrs; i++) { - int rnum = regs[i]; - if (rnum >= dev->parent->num_addrs) { - prom_printf("UGH: property for %s was %d, need < %d\n", - dp->name, len, dev->parent->num_addrs); - prom_halt(); - } - dev->resource[i].start = dev->parent->resource[i].start; - dev->resource[i].end = dev->parent->resource[i].end; - dev->resource[i].flags = IORESOURCE_MEM; - dev->resource[i].name = dp->name; - } - } - - op = of_find_device_by_node(dp); - if (!op) { - dev->num_irqs = 0; - } else { - dev->num_irqs = op->num_irqs; - for (i = 0; i < dev->num_irqs; i++) - dev->irqs[i] = op->irqs[i]; - } - - if (!dev->num_irqs) { - /* - * Oh, well, some PROMs don't export interrupts - * property to children of EBus devices... - * - * Be smart about PS/2 keyboard and mouse. - */ - if (!strcmp(dev->parent->prom_node->name, "8042")) { - if (!strcmp(dev->prom_node->name, "kb_ps2")) { - dev->num_irqs = 1; - dev->irqs[0] = dev->parent->irqs[0]; - } else { - dev->num_irqs = 1; - dev->irqs[0] = dev->parent->irqs[1]; - } - } - } -} - -static int __init child_regs_nonstandard(struct linux_ebus_device *dev) -{ - if (!strcmp(dev->prom_node->name, "i2c") || - !strcmp(dev->prom_node->name, "SUNW,lombus")) - return 1; - return 0; -} - -static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *dev) -{ - struct linux_ebus_child *child; - struct dev_archdata *sd; - struct of_device *op; - int i, len; - - dev->prom_node = dp; - - printk(" [%s", dp->name); - - op = of_find_device_by_node(dp); - if (!op) { - dev->num_addrs = 0; - dev->num_irqs = 0; - } else { - const int *regs = of_get_property(dp, "reg", &len); - - if (!regs) - len = 0; - dev->num_addrs = len / sizeof(struct linux_prom_registers); - - for (i = 0; i < dev->num_addrs; i++) - memcpy(&dev->resource[i], - &op->resource[i], - sizeof(struct resource)); - - dev->num_irqs = op->num_irqs; - for (i = 0; i < dev->num_irqs; i++) - dev->irqs[i] = op->irqs[i]; - } - - sd = &dev->ofdev.dev.archdata; - sd->prom_node = dp; - sd->op = &dev->ofdev; - sd->iommu = dev->bus->ofdev.dev.parent->archdata.iommu; - sd->stc = dev->bus->ofdev.dev.parent->archdata.stc; - sd->numa_node = dev->bus->ofdev.dev.parent->archdata.numa_node; - - dev->ofdev.node = dp; - dev->ofdev.dev.parent = &dev->bus->ofdev.dev; - dev->ofdev.dev.bus = &ebus_bus_type; - dev_set_name(&dev->ofdev.dev, "ebus[%08x]", dp->node); - - /* Register with core */ - if (of_device_register(&dev->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - dp = dp->child; - if (dp) { - printk(" ->"); - dev->children = ebus_alloc(sizeof(struct linux_ebus_child)); - - child = dev->children; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child, - child_regs_nonstandard(dev)); - - while ((dp = dp->sibling) != NULL) { - child->next = ebus_alloc(sizeof(struct linux_ebus_child)); - - child = child->next; - child->next = NULL; - child->parent = dev; - child->bus = dev->bus; - fill_ebus_child(dp, child, - child_regs_nonstandard(dev)); - } - } - printk("]"); -} - -static struct pci_dev *find_next_ebus(struct pci_dev *start, int *is_rio_p) -{ - struct pci_dev *pdev = start; - - while ((pdev = pci_get_device(PCI_VENDOR_ID_SUN, PCI_ANY_ID, pdev))) - if (pdev->device == PCI_DEVICE_ID_SUN_EBUS || - pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS) - break; - - *is_rio_p = !!(pdev && (pdev->device == PCI_DEVICE_ID_SUN_RIO_EBUS)); - - return pdev; -} - -void __init ebus_init(void) -{ - struct linux_ebus_device *dev; - struct linux_ebus *ebus; - struct pci_dev *pdev; - struct device_node *dp; - int is_rio; - int num_ebus = 0; - - pdev = find_next_ebus(NULL, &is_rio); - if (!pdev) { - printk("ebus: No EBus's found.\n"); - return; - } - - dp = pci_device_to_OF_node(pdev); - - ebus_chain = ebus = ebus_alloc(sizeof(struct linux_ebus)); - ebus->next = NULL; - ebus->is_rio = is_rio; - - while (dp) { - struct device_node *child; - - /* SUNW,pci-qfe uses four empty ebuses on it. - I think we should not consider them here, - as they have half of the properties this - code expects and once we do PCI hot-plug, - we'd have to tweak with the ebus_chain - in the runtime after initialization. -jj */ - if (!dp->child) { - pdev = find_next_ebus(pdev, &is_rio); - if (!pdev) { - if (ebus == ebus_chain) { - ebus_chain = NULL; - printk("ebus: No EBus's found.\n"); - return; - } - break; - } - ebus->is_rio = is_rio; - dp = pci_device_to_OF_node(pdev); - continue; - } - printk("ebus%d:", num_ebus); - - ebus->index = num_ebus; - ebus->prom_node = dp; - ebus->self = pdev; - - ebus->ofdev.node = dp; - ebus->ofdev.dev.parent = &pdev->dev; - ebus->ofdev.dev.bus = &ebus_bus_type; - dev_set_name(&ebus->ofdev.dev, "ebus%d", num_ebus); - - /* Register with core */ - if (of_device_register(&ebus->ofdev) != 0) - printk(KERN_DEBUG "ebus: device registration error for %s!\n", - dp->path_component_name); - - - child = dp->child; - if (!child) - goto next_ebus; - - ebus->devices = ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = ebus->devices; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(child, dev); - - while ((child = child->sibling) != NULL) { - dev->next = ebus_alloc(sizeof(struct linux_ebus_device)); - - dev = dev->next; - dev->next = NULL; - dev->children = NULL; - dev->bus = ebus; - fill_ebus_device(child, dev); - } - - next_ebus: - printk("\n"); - - pdev = find_next_ebus(pdev, &is_rio); - if (!pdev) - break; - - dp = pci_device_to_OF_node(pdev); - - ebus->next = ebus_alloc(sizeof(struct linux_ebus)); - ebus = ebus->next; - ebus->next = NULL; - ebus->is_rio = is_rio; - ++num_ebus; - } - pci_dev_put(pdev); /* XXX for the case, when ebusnd is 0, is it OK? */ -} diff --git a/arch/sparc64/kernel/entry.h b/arch/sparc64/kernel/entry.h index fc294a29289..34d7ab5e10d 100644 --- a/arch/sparc64/kernel/entry.h +++ b/arch/sparc64/kernel/entry.h @@ -5,8 +5,8 @@ #include <linux/types.h> #include <linux/init.h> -extern char *sparc_cpu_type; -extern char *sparc_fpu_type; +extern const char *sparc_cpu_type; +extern const char *sparc_fpu_type; extern void __init per_cpu_patch(void); extern void __init sun4v_patch(void); @@ -22,7 +22,8 @@ extern void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long thread_info_flags); -extern asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p); +extern asmlinkage int syscall_trace_enter(struct pt_regs *regs); +extern asmlinkage void syscall_trace_leave(struct pt_regs *regs); extern void bad_trap_tl1(struct pt_regs *regs, long lvl); diff --git a/arch/sparc64/kernel/head.S b/arch/sparc64/kernel/head.S index c9afef093d5..353226fa023 100644 --- a/arch/sparc64/kernel/head.S +++ b/arch/sparc64/kernel/head.S @@ -10,6 +10,7 @@ #include <linux/errno.h> #include <linux/threads.h> #include <linux/init.h> +#include <linux/linkage.h> #include <asm/thread_info.h> #include <asm/asi.h> #include <asm/pstate.h> diff --git a/arch/sparc64/kernel/hvapi.c b/arch/sparc64/kernel/hvapi.c index 691760b5b01..1d272c3b574 100644 --- a/arch/sparc64/kernel/hvapi.c +++ b/arch/sparc64/kernel/hvapi.c @@ -9,7 +9,6 @@ #include <asm/hypervisor.h> #include <asm/oplib.h> -#include <asm/sstate.h> /* If the hypervisor indicates that the API setting * calls are unsupported, by returning HV_EBADTRAP or @@ -184,8 +183,6 @@ void __init sun4v_hvapi_init(void) if (sun4v_hvapi_register(group, major, &minor)) goto bad; - sun4v_sstate_init(); - return; bad: diff --git a/arch/sparc64/kernel/hvcalls.S b/arch/sparc64/kernel/hvcalls.S index a2810f3ac70..e066269d159 100644 --- a/arch/sparc64/kernel/hvcalls.S +++ b/arch/sparc64/kernel/hvcalls.S @@ -3,89 +3,75 @@ * * returns %o0: sysino */ - .globl sun4v_devino_to_sysino - .type sun4v_devino_to_sysino,#function -sun4v_devino_to_sysino: +ENTRY(sun4v_devino_to_sysino) mov HV_FAST_INTR_DEVINO2SYSINO, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_devino_to_sysino, .-sun4v_devino_to_sysino +ENDPROC(sun4v_devino_to_sysino) /* %o0: sysino * * returns %o0: intr_enabled (HV_INTR_{DISABLED,ENABLED}) */ - .globl sun4v_intr_getenabled - .type sun4v_intr_getenabled,#function -sun4v_intr_getenabled: +ENTRY(sun4v_intr_getenabled) mov HV_FAST_INTR_GETENABLED, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_intr_getenabled, .-sun4v_intr_getenabled +ENDPROC(sun4v_intr_getenabled) /* %o0: sysino * %o1: intr_enabled (HV_INTR_{DISABLED,ENABLED}) */ - .globl sun4v_intr_setenabled - .type sun4v_intr_setenabled,#function -sun4v_intr_setenabled: +ENTRY(sun4v_intr_setenabled) mov HV_FAST_INTR_SETENABLED, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_intr_setenabled, .-sun4v_intr_setenabled +ENDPROC(sun4v_intr_setenabled) /* %o0: sysino * * returns %o0: intr_state (HV_INTR_STATE_*) */ - .globl sun4v_intr_getstate - .type sun4v_intr_getstate,#function -sun4v_intr_getstate: +ENTRY(sun4v_intr_getstate) mov HV_FAST_INTR_GETSTATE, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_intr_getstate, .-sun4v_intr_getstate +ENDPROC(sun4v_intr_getstate) /* %o0: sysino * %o1: intr_state (HV_INTR_STATE_*) */ - .globl sun4v_intr_setstate - .type sun4v_intr_setstate,#function -sun4v_intr_setstate: +ENTRY(sun4v_intr_setstate) mov HV_FAST_INTR_SETSTATE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_intr_setstate, .-sun4v_intr_setstate +ENDPROC(sun4v_intr_setstate) /* %o0: sysino * * returns %o0: cpuid */ - .globl sun4v_intr_gettarget - .type sun4v_intr_gettarget,#function -sun4v_intr_gettarget: +ENTRY(sun4v_intr_gettarget) mov HV_FAST_INTR_GETTARGET, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 - .size sun4v_intr_gettarget, .-sun4v_intr_gettarget +ENDPROC(sun4v_intr_gettarget) /* %o0: sysino * %o1: cpuid */ - .globl sun4v_intr_settarget - .type sun4v_intr_settarget,#function -sun4v_intr_settarget: +ENTRY(sun4v_intr_settarget) mov HV_FAST_INTR_SETTARGET, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_intr_settarget, .-sun4v_intr_settarget +ENDPROC(sun4v_intr_settarget) /* %o0: cpuid * %o1: pc @@ -94,37 +80,31 @@ sun4v_intr_settarget: * * returns %o0: status */ - .globl sun4v_cpu_start - .type sun4v_cpu_start,#function -sun4v_cpu_start: +ENTRY(sun4v_cpu_start) mov HV_FAST_CPU_START, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_start, .-sun4v_cpu_start +ENDPROC(sun4v_cpu_start) /* %o0: cpuid * * returns %o0: status */ - .globl sun4v_cpu_stop - .type sun4v_cpu_stop,#function -sun4v_cpu_stop: +ENTRY(sun4v_cpu_stop) mov HV_FAST_CPU_STOP, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_stop, .-sun4v_cpu_stop +ENDPROC(sun4v_cpu_stop) /* returns %o0: status */ - .globl sun4v_cpu_yield - .type sun4v_cpu_yield, #function -sun4v_cpu_yield: +ENTRY(sun4v_cpu_yield) mov HV_FAST_CPU_YIELD, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_yield, .-sun4v_cpu_yield +ENDPROC(sun4v_cpu_yield) /* %o0: type * %o1: queue paddr @@ -132,14 +112,12 @@ sun4v_cpu_yield: * * returns %o0: status */ - .globl sun4v_cpu_qconf - .type sun4v_cpu_qconf,#function -sun4v_cpu_qconf: +ENTRY(sun4v_cpu_qconf) mov HV_FAST_CPU_QCONF, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_qconf, .-sun4v_cpu_qconf +ENDPROC(sun4v_cpu_qconf) /* %o0: num cpus in cpu list * %o1: cpu list paddr @@ -147,23 +125,19 @@ sun4v_cpu_qconf: * * returns %o0: status */ - .globl sun4v_cpu_mondo_send - .type sun4v_cpu_mondo_send,#function -sun4v_cpu_mondo_send: +ENTRY(sun4v_cpu_mondo_send) mov HV_FAST_CPU_MONDO_SEND, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_cpu_mondo_send, .-sun4v_cpu_mondo_send +ENDPROC(sun4v_cpu_mondo_send) /* %o0: CPU ID * * returns %o0: -status if status non-zero, else * %o0: cpu state as HV_CPU_STATE_* */ - .globl sun4v_cpu_state - .type sun4v_cpu_state,#function -sun4v_cpu_state: +ENTRY(sun4v_cpu_state) mov HV_FAST_CPU_STATE, %o5 ta HV_FAST_TRAP brnz,pn %o0, 1f @@ -171,7 +145,7 @@ sun4v_cpu_state: mov %o1, %o0 1: retl nop - .size sun4v_cpu_state, .-sun4v_cpu_state +ENDPROC(sun4v_cpu_state) /* %o0: virtual address * %o1: must be zero @@ -180,28 +154,24 @@ sun4v_cpu_state: * * returns %o0: status */ - .globl sun4v_mmu_map_perm_addr - .type sun4v_mmu_map_perm_addr,#function -sun4v_mmu_map_perm_addr: +ENTRY(sun4v_mmu_map_perm_addr) mov HV_FAST_MMU_MAP_PERM_ADDR, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mmu_map_perm_addr, .-sun4v_mmu_map_perm_addr +ENDPROC(sun4v_mmu_map_perm_addr) /* %o0: number of TSB descriptions * %o1: TSB descriptions real address * * returns %o0: status */ - .globl sun4v_mmu_tsb_ctx0 - .type sun4v_mmu_tsb_ctx0,#function -sun4v_mmu_tsb_ctx0: +ENTRY(sun4v_mmu_tsb_ctx0) mov HV_FAST_MMU_TSB_CTX0, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mmu_tsb_ctx0, .-sun4v_mmu_tsb_ctx0 +ENDPROC(sun4v_mmu_tsb_ctx0) /* %o0: API group number * %o1: pointer to unsigned long major number storage @@ -209,9 +179,7 @@ sun4v_mmu_tsb_ctx0: * * returns %o0: status */ - .globl sun4v_get_version - .type sun4v_get_version,#function -sun4v_get_version: +ENTRY(sun4v_get_version) mov HV_CORE_GET_VER, %o5 mov %o1, %o3 mov %o2, %o4 @@ -219,7 +187,7 @@ sun4v_get_version: stx %o1, [%o3] retl stx %o2, [%o4] - .size sun4v_get_version, .-sun4v_get_version +ENDPROC(sun4v_get_version) /* %o0: API group number * %o1: desired major number @@ -228,51 +196,43 @@ sun4v_get_version: * * returns %o0: status */ - .globl sun4v_set_version - .type sun4v_set_version,#function -sun4v_set_version: +ENTRY(sun4v_set_version) mov HV_CORE_SET_VER, %o5 mov %o3, %o4 ta HV_CORE_TRAP retl stx %o1, [%o4] - .size sun4v_set_version, .-sun4v_set_version +ENDPROC(sun4v_set_version) /* %o0: pointer to unsigned long time * * returns %o0: status */ - .globl sun4v_tod_get - .type sun4v_tod_get,#function -sun4v_tod_get: +ENTRY(sun4v_tod_get) mov %o0, %o4 mov HV_FAST_TOD_GET, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_tod_get, .-sun4v_tod_get +ENDPROC(sun4v_tod_get) /* %o0: time * * returns %o0: status */ - .globl sun4v_tod_set - .type sun4v_tod_set,#function -sun4v_tod_set: +ENTRY(sun4v_tod_set) mov HV_FAST_TOD_SET, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_tod_set, .-sun4v_tod_set +ENDPROC(sun4v_tod_set) /* %o0: pointer to unsigned long status * * returns %o0: signed character */ - .globl sun4v_con_getchar - .type sun4v_con_getchar,#function -sun4v_con_getchar: +ENTRY(sun4v_con_getchar) mov %o0, %o4 mov HV_FAST_CONS_GETCHAR, %o5 clr %o0 @@ -281,20 +241,18 @@ sun4v_con_getchar: stx %o0, [%o4] retl sra %o1, 0, %o0 - .size sun4v_con_getchar, .-sun4v_con_getchar +ENDPROC(sun4v_con_getchar) /* %o0: signed long character * * returns %o0: status */ - .globl sun4v_con_putchar - .type sun4v_con_putchar,#function -sun4v_con_putchar: +ENTRY(sun4v_con_putchar) mov HV_FAST_CONS_PUTCHAR, %o5 ta HV_FAST_TRAP retl sra %o0, 0, %o0 - .size sun4v_con_putchar, .-sun4v_con_putchar +ENDPROC(sun4v_con_putchar) /* %o0: buffer real address * %o1: buffer size @@ -302,9 +260,7 @@ sun4v_con_putchar: * * returns %o0: status */ - .globl sun4v_con_read - .type sun4v_con_read,#function -sun4v_con_read: +ENTRY(sun4v_con_read) mov %o2, %o4 mov HV_FAST_CONS_READ, %o5 ta HV_FAST_TRAP @@ -318,7 +274,7 @@ sun4v_con_read: stx %o1, [%o4] 1: retl nop - .size sun4v_con_read, .-sun4v_con_read +ENDPROC(sun4v_con_read) /* %o0: buffer real address * %o1: buffer size @@ -326,43 +282,37 @@ sun4v_con_read: * * returns %o0: status */ - .globl sun4v_con_write - .type sun4v_con_write,#function -sun4v_con_write: +ENTRY(sun4v_con_write) mov %o2, %o4 mov HV_FAST_CONS_WRITE, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_con_write, .-sun4v_con_write +ENDPROC(sun4v_con_write) /* %o0: soft state * %o1: address of description string * * returns %o0: status */ - .globl sun4v_mach_set_soft_state - .type sun4v_mach_set_soft_state,#function -sun4v_mach_set_soft_state: +ENTRY(sun4v_mach_set_soft_state) mov HV_FAST_MACH_SET_SOFT_STATE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mach_set_soft_state, .-sun4v_mach_set_soft_state +ENDPROC(sun4v_mach_set_soft_state) /* %o0: exit code * * Does not return. */ - .globl sun4v_mach_exit - .type sun4v_mach_exit,#function -sun4v_mach_exit: +ENTRY(sun4v_mach_exit) mov HV_FAST_MACH_EXIT, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_mach_exit, .-sun4v_mach_exit +ENDPROC(sun4v_mach_exit) /* %o0: buffer real address * %o1: buffer length @@ -370,44 +320,38 @@ sun4v_mach_exit: * * returns %o0: status */ - .globl sun4v_mach_desc - .type sun4v_mach_desc,#function -sun4v_mach_desc: +ENTRY(sun4v_mach_desc) mov %o2, %o4 mov HV_FAST_MACH_DESC, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mach_desc, .-sun4v_mach_desc +ENDPROC(sun4v_mach_desc) /* %o0: new timeout in milliseconds * %o1: pointer to unsigned long orig_timeout * * returns %o0: status */ - .globl sun4v_mach_set_watchdog - .type sun4v_mach_set_watchdog,#function -sun4v_mach_set_watchdog: +ENTRY(sun4v_mach_set_watchdog) mov %o1, %o4 mov HV_FAST_MACH_SET_WATCHDOG, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mach_set_watchdog, .-sun4v_mach_set_watchdog +ENDPROC(sun4v_mach_set_watchdog) /* No inputs and does not return. */ - .globl sun4v_mach_sir - .type sun4v_mach_sir,#function -sun4v_mach_sir: +ENTRY(sun4v_mach_sir) mov %o1, %o4 mov HV_FAST_MACH_SIR, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mach_sir, .-sun4v_mach_sir +ENDPROC(sun4v_mach_sir) /* %o0: channel * %o1: ra @@ -415,14 +359,12 @@ sun4v_mach_sir: * * returns %o0: status */ - .globl sun4v_ldc_tx_qconf - .type sun4v_ldc_tx_qconf,#function -sun4v_ldc_tx_qconf: +ENTRY(sun4v_ldc_tx_qconf) mov HV_FAST_LDC_TX_QCONF, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_tx_qconf, .-sun4v_ldc_tx_qconf +ENDPROC(sun4v_ldc_tx_qconf) /* %o0: channel * %o1: pointer to unsigned long ra @@ -430,9 +372,7 @@ sun4v_ldc_tx_qconf: * * returns %o0: status */ - .globl sun4v_ldc_tx_qinfo - .type sun4v_ldc_tx_qinfo,#function -sun4v_ldc_tx_qinfo: +ENTRY(sun4v_ldc_tx_qinfo) mov %o1, %g1 mov %o2, %g2 mov HV_FAST_LDC_TX_QINFO, %o5 @@ -441,7 +381,7 @@ sun4v_ldc_tx_qinfo: stx %o2, [%g2] retl nop - .size sun4v_ldc_tx_qinfo, .-sun4v_ldc_tx_qinfo +ENDPROC(sun4v_ldc_tx_qinfo) /* %o0: channel * %o1: pointer to unsigned long head_off @@ -450,9 +390,7 @@ sun4v_ldc_tx_qinfo: * * returns %o0: status */ - .globl sun4v_ldc_tx_get_state - .type sun4v_ldc_tx_get_state,#function -sun4v_ldc_tx_get_state: +ENTRY(sun4v_ldc_tx_get_state) mov %o1, %g1 mov %o2, %g2 mov %o3, %g3 @@ -463,21 +401,19 @@ sun4v_ldc_tx_get_state: stx %o3, [%g3] retl nop - .size sun4v_ldc_tx_get_state, .-sun4v_ldc_tx_get_state +ENDPROC(sun4v_ldc_tx_get_state) /* %o0: channel * %o1: tail_off * * returns %o0: status */ - .globl sun4v_ldc_tx_set_qtail - .type sun4v_ldc_tx_set_qtail,#function -sun4v_ldc_tx_set_qtail: +ENTRY(sun4v_ldc_tx_set_qtail) mov HV_FAST_LDC_TX_SET_QTAIL, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_tx_set_qtail, .-sun4v_ldc_tx_set_qtail +ENDPROC(sun4v_ldc_tx_set_qtail) /* %o0: channel * %o1: ra @@ -485,14 +421,12 @@ sun4v_ldc_tx_set_qtail: * * returns %o0: status */ - .globl sun4v_ldc_rx_qconf - .type sun4v_ldc_rx_qconf,#function -sun4v_ldc_rx_qconf: +ENTRY(sun4v_ldc_rx_qconf) mov HV_FAST_LDC_RX_QCONF, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_rx_qconf, .-sun4v_ldc_rx_qconf +ENDPROC(sun4v_ldc_rx_qconf) /* %o0: channel * %o1: pointer to unsigned long ra @@ -500,9 +434,7 @@ sun4v_ldc_rx_qconf: * * returns %o0: status */ - .globl sun4v_ldc_rx_qinfo - .type sun4v_ldc_rx_qinfo,#function -sun4v_ldc_rx_qinfo: +ENTRY(sun4v_ldc_rx_qinfo) mov %o1, %g1 mov %o2, %g2 mov HV_FAST_LDC_RX_QINFO, %o5 @@ -511,7 +443,7 @@ sun4v_ldc_rx_qinfo: stx %o2, [%g2] retl nop - .size sun4v_ldc_rx_qinfo, .-sun4v_ldc_rx_qinfo +ENDPROC(sun4v_ldc_rx_qinfo) /* %o0: channel * %o1: pointer to unsigned long head_off @@ -520,9 +452,7 @@ sun4v_ldc_rx_qinfo: * * returns %o0: status */ - .globl sun4v_ldc_rx_get_state - .type sun4v_ldc_rx_get_state,#function -sun4v_ldc_rx_get_state: +ENTRY(sun4v_ldc_rx_get_state) mov %o1, %g1 mov %o2, %g2 mov %o3, %g3 @@ -533,21 +463,19 @@ sun4v_ldc_rx_get_state: stx %o3, [%g3] retl nop - .size sun4v_ldc_rx_get_state, .-sun4v_ldc_rx_get_state +ENDPROC(sun4v_ldc_rx_get_state) /* %o0: channel * %o1: head_off * * returns %o0: status */ - .globl sun4v_ldc_rx_set_qhead - .type sun4v_ldc_rx_set_qhead,#function -sun4v_ldc_rx_set_qhead: +ENTRY(sun4v_ldc_rx_set_qhead) mov HV_FAST_LDC_RX_SET_QHEAD, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_rx_set_qhead, .-sun4v_ldc_rx_set_qhead +ENDPROC(sun4v_ldc_rx_set_qhead) /* %o0: channel * %o1: ra @@ -555,14 +483,12 @@ sun4v_ldc_rx_set_qhead: * * returns %o0: status */ - .globl sun4v_ldc_set_map_table - .type sun4v_ldc_set_map_table,#function -sun4v_ldc_set_map_table: +ENTRY(sun4v_ldc_set_map_table) mov HV_FAST_LDC_SET_MAP_TABLE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_set_map_table, .-sun4v_ldc_set_map_table +ENDPROC(sun4v_ldc_set_map_table) /* %o0: channel * %o1: pointer to unsigned long ra @@ -570,9 +496,7 @@ sun4v_ldc_set_map_table: * * returns %o0: status */ - .globl sun4v_ldc_get_map_table - .type sun4v_ldc_get_map_table,#function -sun4v_ldc_get_map_table: +ENTRY(sun4v_ldc_get_map_table) mov %o1, %g1 mov %o2, %g2 mov HV_FAST_LDC_GET_MAP_TABLE, %o5 @@ -581,7 +505,7 @@ sun4v_ldc_get_map_table: stx %o2, [%g2] retl nop - .size sun4v_ldc_get_map_table, .-sun4v_ldc_get_map_table +ENDPROC(sun4v_ldc_get_map_table) /* %o0: channel * %o1: dir_code @@ -592,16 +516,14 @@ sun4v_ldc_get_map_table: * * returns %o0: status */ - .globl sun4v_ldc_copy - .type sun4v_ldc_copy,#function -sun4v_ldc_copy: +ENTRY(sun4v_ldc_copy) mov %o5, %g1 mov HV_FAST_LDC_COPY, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_ldc_copy, .-sun4v_ldc_copy +ENDPROC(sun4v_ldc_copy) /* %o0: channel * %o1: cookie @@ -610,9 +532,7 @@ sun4v_ldc_copy: * * returns %o0: status */ - .globl sun4v_ldc_mapin - .type sun4v_ldc_mapin,#function -sun4v_ldc_mapin: +ENTRY(sun4v_ldc_mapin) mov %o2, %g1 mov %o3, %g2 mov HV_FAST_LDC_MAPIN, %o5 @@ -621,20 +541,18 @@ sun4v_ldc_mapin: stx %o2, [%g2] retl nop - .size sun4v_ldc_mapin, .-sun4v_ldc_mapin +ENDPROC(sun4v_ldc_mapin) /* %o0: ra * * returns %o0: status */ - .globl sun4v_ldc_unmap - .type sun4v_ldc_unmap,#function -sun4v_ldc_unmap: +ENTRY(sun4v_ldc_unmap) mov HV_FAST_LDC_UNMAP, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_unmap, .-sun4v_ldc_unmap +ENDPROC(sun4v_ldc_unmap) /* %o0: channel * %o1: cookie @@ -642,14 +560,12 @@ sun4v_ldc_unmap: * * returns %o0: status */ - .globl sun4v_ldc_revoke - .type sun4v_ldc_revoke,#function -sun4v_ldc_revoke: +ENTRY(sun4v_ldc_revoke) mov HV_FAST_LDC_REVOKE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ldc_revoke, .-sun4v_ldc_revoke +ENDPROC(sun4v_ldc_revoke) /* %o0: device handle * %o1: device INO @@ -657,16 +573,14 @@ sun4v_ldc_revoke: * * returns %o0: status */ - .globl sun4v_vintr_get_cookie - .type sun4v_vintr_get_cookie,#function -sun4v_vintr_get_cookie: +ENTRY(sun4v_vintr_get_cookie) mov %o2, %g1 mov HV_FAST_VINTR_GET_COOKIE, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_cookie, .-sun4v_vintr_get_cookie +ENDPROC(sun4v_vintr_get_cookie) /* %o0: device handle * %o1: device INO @@ -674,14 +588,12 @@ sun4v_vintr_get_cookie: * * returns %o0: status */ - .globl sun4v_vintr_set_cookie - .type sun4v_vintr_set_cookie,#function -sun4v_vintr_set_cookie: +ENTRY(sun4v_vintr_set_cookie) mov HV_FAST_VINTR_SET_COOKIE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_cookie, .-sun4v_vintr_set_cookie +ENDPROC(sun4v_vintr_set_cookie) /* %o0: device handle * %o1: device INO @@ -689,16 +601,14 @@ sun4v_vintr_set_cookie: * * returns %o0: status */ - .globl sun4v_vintr_get_valid - .type sun4v_vintr_get_valid,#function -sun4v_vintr_get_valid: +ENTRY(sun4v_vintr_get_valid) mov %o2, %g1 mov HV_FAST_VINTR_GET_VALID, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_valid, .-sun4v_vintr_get_valid +ENDPROC(sun4v_vintr_get_valid) /* %o0: device handle * %o1: device INO @@ -706,14 +616,12 @@ sun4v_vintr_get_valid: * * returns %o0: status */ - .globl sun4v_vintr_set_valid - .type sun4v_vintr_set_valid,#function -sun4v_vintr_set_valid: +ENTRY(sun4v_vintr_set_valid) mov HV_FAST_VINTR_SET_VALID, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_valid, .-sun4v_vintr_set_valid +ENDPROC(sun4v_vintr_set_valid) /* %o0: device handle * %o1: device INO @@ -721,16 +629,14 @@ sun4v_vintr_set_valid: * * returns %o0: status */ - .globl sun4v_vintr_get_state - .type sun4v_vintr_get_state,#function -sun4v_vintr_get_state: +ENTRY(sun4v_vintr_get_state) mov %o2, %g1 mov HV_FAST_VINTR_GET_STATE, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_state, .-sun4v_vintr_get_state +ENDPROC(sun4v_vintr_get_state) /* %o0: device handle * %o1: device INO @@ -738,14 +644,12 @@ sun4v_vintr_get_state: * * returns %o0: status */ - .globl sun4v_vintr_set_state - .type sun4v_vintr_set_state,#function -sun4v_vintr_set_state: +ENTRY(sun4v_vintr_set_state) mov HV_FAST_VINTR_SET_STATE, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_state, .-sun4v_vintr_set_state +ENDPROC(sun4v_vintr_set_state) /* %o0: device handle * %o1: device INO @@ -753,16 +657,14 @@ sun4v_vintr_set_state: * * returns %o0: status */ - .globl sun4v_vintr_get_target - .type sun4v_vintr_get_target,#function -sun4v_vintr_get_target: +ENTRY(sun4v_vintr_get_target) mov %o2, %g1 mov HV_FAST_VINTR_GET_TARGET, %o5 ta HV_FAST_TRAP stx %o1, [%g1] retl nop - .size sun4v_vintr_get_target, .-sun4v_vintr_get_target +ENDPROC(sun4v_vintr_get_target) /* %o0: device handle * %o1: device INO @@ -770,14 +672,12 @@ sun4v_vintr_get_target: * * returns %o0: status */ - .globl sun4v_vintr_set_target - .type sun4v_vintr_set_target,#function -sun4v_vintr_set_target: +ENTRY(sun4v_vintr_set_target) mov HV_FAST_VINTR_SET_TARGET, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_vintr_set_target, .-sun4v_vintr_set_target +ENDPROC(sun4v_vintr_set_target) /* %o0: NCS sub-function * %o1: sub-function arg real-address @@ -785,18 +685,14 @@ sun4v_vintr_set_target: * * returns %o0: status */ - .globl sun4v_ncs_request - .type sun4v_ncs_request,#function -sun4v_ncs_request: +ENTRY(sun4v_ncs_request) mov HV_FAST_NCS_REQUEST, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_ncs_request, .-sun4v_ncs_request +ENDPROC(sun4v_ncs_request) - .globl sun4v_svc_send - .type sun4v_svc_send,#function -sun4v_svc_send: +ENTRY(sun4v_svc_send) save %sp, -192, %sp mov %i0, %o0 mov %i1, %o1 @@ -806,11 +702,9 @@ sun4v_svc_send: stx %o1, [%i3] ret restore - .size sun4v_svc_send, .-sun4v_svc_send +ENDPROC(sun4v_svc_send) - .globl sun4v_svc_recv - .type sun4v_svc_recv,#function -sun4v_svc_recv: +ENTRY(sun4v_svc_recv) save %sp, -192, %sp mov %i0, %o0 mov %i1, %o1 @@ -820,62 +714,50 @@ sun4v_svc_recv: stx %o1, [%i3] ret restore - .size sun4v_svc_recv, .-sun4v_svc_recv +ENDPROC(sun4v_svc_recv) - .globl sun4v_svc_getstatus - .type sun4v_svc_getstatus,#function -sun4v_svc_getstatus: +ENTRY(sun4v_svc_getstatus) mov HV_FAST_SVC_GETSTATUS, %o5 mov %o1, %o4 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_svc_getstatus, .-sun4v_svc_getstatus +ENDPROC(sun4v_svc_getstatus) - .globl sun4v_svc_setstatus - .type sun4v_svc_setstatus,#function -sun4v_svc_setstatus: +ENTRY(sun4v_svc_setstatus) mov HV_FAST_SVC_SETSTATUS, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_svc_setstatus, .-sun4v_svc_setstatus +ENDPROC(sun4v_svc_setstatus) - .globl sun4v_svc_clrstatus - .type sun4v_svc_clrstatus,#function -sun4v_svc_clrstatus: +ENTRY(sun4v_svc_clrstatus) mov HV_FAST_SVC_CLRSTATUS, %o5 ta HV_FAST_TRAP retl nop - .size sun4v_svc_clrstatus, .-sun4v_svc_clrstatus +ENDPROC(sun4v_svc_clrstatus) - .globl sun4v_mmustat_conf - .type sun4v_mmustat_conf,#function -sun4v_mmustat_conf: +ENTRY(sun4v_mmustat_conf) mov %o1, %o4 mov HV_FAST_MMUSTAT_CONF, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mmustat_conf, .-sun4v_mmustat_conf +ENDPROC(sun4v_mmustat_conf) - .globl sun4v_mmustat_info - .type sun4v_mmustat_info,#function -sun4v_mmustat_info: +ENTRY(sun4v_mmustat_info) mov %o0, %o4 mov HV_FAST_MMUSTAT_INFO, %o5 ta HV_FAST_TRAP stx %o1, [%o4] retl nop - .size sun4v_mmustat_info, .-sun4v_mmustat_info +ENDPROC(sun4v_mmustat_info) - .globl sun4v_mmu_demap_all - .type sun4v_mmu_demap_all,#function -sun4v_mmu_demap_all: +ENTRY(sun4v_mmu_demap_all) clr %o0 clr %o1 mov HV_MMU_ALL, %o2 @@ -883,4 +765,4 @@ sun4v_mmu_demap_all: ta HV_FAST_TRAP retl nop - .size sun4v_mmu_demap_all, .-sun4v_mmu_demap_all +ENDPROC(sun4v_mmu_demap_all) diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 7495bc77468..52fc836f464 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -29,7 +29,6 @@ #include <asm/system.h> #include <asm/irq.h> #include <asm/io.h> -#include <asm/sbus.h> #include <asm/iommu.h> #include <asm/upa.h> #include <asm/oplib.h> diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 100ebd52749..0f616ae3246 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -55,15 +55,38 @@ struct of_device *of_find_device_by_node(struct device_node *dp) } EXPORT_SYMBOL(of_find_device_by_node); -#ifdef CONFIG_PCI -struct bus_type ebus_bus_type; -EXPORT_SYMBOL(ebus_bus_type); -#endif +unsigned int irq_of_parse_and_map(struct device_node *node, int index) +{ + struct of_device *op = of_find_device_by_node(node); + + if (!op || index >= op->num_irqs) + return 0; + + return op->irqs[index]; +} +EXPORT_SYMBOL(irq_of_parse_and_map); + +/* Take the archdata values for IOMMU, STC, and HOSTDATA found in + * BUS and propagate to all child of_device objects. + */ +void of_propagate_archdata(struct of_device *bus) +{ + struct dev_archdata *bus_sd = &bus->dev.archdata; + struct device_node *bus_dp = bus->node; + struct device_node *dp; -#ifdef CONFIG_SBUS -struct bus_type sbus_bus_type; -EXPORT_SYMBOL(sbus_bus_type); -#endif + for (dp = bus_dp->child; dp; dp = dp->sibling) { + struct of_device *op = of_find_device_by_node(dp); + + op->dev.archdata.iommu = bus_sd->iommu; + op->dev.archdata.stc = bus_sd->stc; + op->dev.archdata.host_controller = bus_sd->host_controller; + op->dev.archdata.numa_node = bus_sd->numa_node; + + if (dp->child) + of_propagate_archdata(op); + } +} struct bus_type of_platform_bus_type; EXPORT_SYMBOL(of_platform_bus_type); @@ -378,8 +401,7 @@ static int __init build_one_resource(struct device_node *parent, int na, int ns, int pna) { const u32 *ranges; - unsigned int rlen; - int rone; + int rone, rlen; ranges = of_get_property(parent, "ranges", &rlen); if (ranges == NULL || rlen == 0) { @@ -421,8 +443,17 @@ static int __init use_1to1_mapping(struct device_node *pp) /* If the parent is the dma node of an ISA bus, pass * the translation up to the root. + * + * Some SBUS devices use intermediate nodes to express + * hierarchy within the device itself. These aren't + * real bus nodes, and don't have a 'ranges' property. + * But, we should still pass the translation work up + * to the SBUS itself. */ - if (!strcmp(pp->name, "dma")) + if (!strcmp(pp->name, "dma") || + !strcmp(pp->name, "espdma") || + !strcmp(pp->name, "ledma") || + !strcmp(pp->name, "lebuffer")) return 0; /* Similarly for all PCI bridges, if we get this far @@ -844,15 +875,6 @@ static int __init of_bus_driver_init(void) int err; err = of_bus_type_init(&of_platform_bus_type, "of"); -#ifdef CONFIG_PCI - if (!err) - err = of_bus_type_init(&ebus_bus_type, "ebus"); -#endif -#ifdef CONFIG_SBUS - if (!err) - err = of_bus_type_init(&sbus_bus_type, "sbus"); -#endif - if (!err) scan_of_devices(); diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 80dad76f8b8..242ac1ccae7 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c @@ -18,32 +18,17 @@ #include <linux/msi.h> #include <linux/irq.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/irq.h> -#include <asm/ebus.h> #include <asm/prom.h> #include <asm/apb.h> #include "pci_impl.h" -#ifndef CONFIG_PCI -/* A "nop" PCI implementation. */ -asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, - unsigned long off, unsigned long len, - unsigned char *buf) -{ - return 0; -} -asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, - unsigned long off, unsigned long len, - unsigned char *buf) -{ - return 0; -} -#else - /* List of all PCI controllers found in the system. */ struct pci_pbm_info *pci_pbm_root = NULL; @@ -179,97 +164,6 @@ void pci_config_write32(u32 *addr, u32 val) spin_unlock_irqrestore(&pci_poke_lock, flags); } -/* Probe for all PCI controllers in the system. */ -extern void sabre_init(struct device_node *, const char *); -extern void psycho_init(struct device_node *, const char *); -extern void schizo_init(struct device_node *, const char *); -extern void schizo_plus_init(struct device_node *, const char *); -extern void tomatillo_init(struct device_node *, const char *); -extern void sun4v_pci_init(struct device_node *, const char *); -extern void fire_pci_init(struct device_node *, const char *); - -static struct { - char *model_name; - void (*init)(struct device_node *, const char *); -} pci_controller_table[] __initdata = { - { "SUNW,sabre", sabre_init }, - { "pci108e,a000", sabre_init }, - { "pci108e,a001", sabre_init }, - { "SUNW,psycho", psycho_init }, - { "pci108e,8000", psycho_init }, - { "SUNW,schizo", schizo_init }, - { "pci108e,8001", schizo_init }, - { "SUNW,schizo+", schizo_plus_init }, - { "pci108e,8002", schizo_plus_init }, - { "SUNW,tomatillo", tomatillo_init }, - { "pci108e,a801", tomatillo_init }, - { "SUNW,sun4v-pci", sun4v_pci_init }, - { "pciex108e,80f0", fire_pci_init }, -}; -#define PCI_NUM_CONTROLLER_TYPES ARRAY_SIZE(pci_controller_table) - -static int __init pci_controller_init(const char *model_name, int namelen, struct device_node *dp) -{ - int i; - - for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) { - if (!strncmp(model_name, - pci_controller_table[i].model_name, - namelen)) { - pci_controller_table[i].init(dp, model_name); - return 1; - } - } - - return 0; -} - -static int __init pci_controller_scan(int (*handler)(const char *, int, struct device_node *)) -{ - struct device_node *dp; - int count = 0; - - for_each_node_by_name(dp, "pci") { - struct property *prop; - int len; - - prop = of_find_property(dp, "model", &len); - if (!prop) - prop = of_find_property(dp, "compatible", &len); - - if (prop) { - const char *model = prop->value; - int item_len = 0; - - /* Our value may be a multi-valued string in the - * case of some compatible properties. For sanity, - * only try the first one. - */ - while (model[item_len] && len) { - len--; - item_len++; - } - - if (handler(model, item_len, dp)) - count++; - } - } - - return count; -} - -/* Find each controller in the system, attach and initialize - * software state structure for each and link into the - * pci_pbm_root. Setup the controller enough such - * that bus scanning can be done. - */ -static void __init pci_controller_probe(void) -{ - printk("PCI: Probing for controllers.\n"); - - pci_controller_scan(pci_controller_init); -} - static int ofpci_verbose; static int __init ofpci_debug(char *str) @@ -348,11 +242,12 @@ static void pci_parse_of_addrs(struct of_device *op, } } -struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, - struct device_node *node, - struct pci_bus *bus, int devfn) +static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, + struct device_node *node, + struct pci_bus *bus, int devfn) { struct dev_archdata *sd; + struct of_device *op; struct pci_dev *dev; const char *type; u32 class; @@ -366,14 +261,17 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, sd->stc = &pbm->stc; sd->host_controller = pbm; sd->prom_node = node; - sd->op = of_find_device_by_node(node); + sd->op = op = of_find_device_by_node(node); sd->numa_node = pbm->numa_node; - sd = &sd->op->dev.archdata; + sd = &op->dev.archdata; sd->iommu = pbm->iommu; sd->stc = &pbm->stc; sd->numa_node = pbm->numa_node; + if (!strcmp(node->name, "ebus")) + of_propagate_archdata(op); + type = of_get_property(node, "device_type", NULL); if (type == NULL) type = ""; @@ -775,15 +673,15 @@ static void __devinit pci_bus_register_of_sysfs(struct pci_bus *bus) pci_bus_register_of_sysfs(child_bus); } -struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm) +struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm, + struct device *parent) { - struct device_node *node = pbm->prom_node; + struct device_node *node = pbm->op->node; struct pci_bus *bus; printk("PCI: Scanning PBM %s\n", node->full_name); - /* XXX parent device? XXX */ - bus = pci_create_bus(NULL, pbm->pci_first_busno, pbm->pci_ops, pbm); + bus = pci_create_bus(parent, pbm->pci_first_busno, pbm->pci_ops, pbm); if (!bus) { printk(KERN_ERR "Failed to create bus for %s\n", node->full_name); @@ -802,32 +700,6 @@ struct pci_bus * __devinit pci_scan_one_pbm(struct pci_pbm_info *pbm) return bus; } -static void __init pci_scan_each_controller_bus(void) -{ - struct pci_pbm_info *pbm; - - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) - pbm->scan_bus(pbm); -} - -extern void power_init(void); - -static int __init pcibios_init(void) -{ - pci_controller_probe(); - if (pci_pbm_root == NULL) - return 0; - - pci_scan_each_controller_bus(); - - ebus_init(); - power_init(); - - return 0; -} - -subsys_initcall(pcibios_init); - void __devinit pcibios_fixup_bus(struct pci_bus *pbus) { struct pci_pbm_info *pbm = pbus->sysdata; @@ -1105,14 +977,14 @@ int pcibus_to_node(struct pci_bus *pbus) EXPORT_SYMBOL(pcibus_to_node); #endif -/* Return the domain nuber for this pci bus */ +/* Return the domain number for this pci bus */ int pci_domain_nr(struct pci_bus *pbus) { struct pci_pbm_info *pbm = pbus->sysdata; int ret; - if (pbm == NULL || pbm->parent == NULL) { + if (!pbm) { ret = -ENXIO; } else { ret = pbm->index; @@ -1126,7 +998,7 @@ EXPORT_SYMBOL(pci_domain_nr); int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) { struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; - int virt_irq; + unsigned int virt_irq; if (!pbm->setup_msi_irq) return -EINVAL; @@ -1140,10 +1012,8 @@ void arch_teardown_msi_irq(unsigned int virt_irq) struct pci_dev *pdev = entry->dev; struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller; - if (!pbm->teardown_msi_irq) - return; - - return pbm->teardown_msi_irq(virt_irq, pdev); + if (pbm->teardown_msi_irq) + pbm->teardown_msi_irq(virt_irq, pdev); } #endif /* !(CONFIG_PCI_MSI) */ @@ -1215,5 +1085,3 @@ void pci_resource_to_user(const struct pci_dev *pdev, int bar, *start = rp->start - offset; *end = rp->end - offset; } - -#endif /* !(CONFIG_PCI) */ diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 09a5ec200c6..23b88082d0b 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -314,12 +314,12 @@ struct pci_ops sun4v_pci_ops = { void pci_get_pbm_props(struct pci_pbm_info *pbm) { - const u32 *val = of_get_property(pbm->prom_node, "bus-range", NULL); + const u32 *val = of_get_property(pbm->op->node, "bus-range", NULL); pbm->pci_first_busno = val[0]; pbm->pci_last_busno = val[1]; - val = of_get_property(pbm->prom_node, "ino-bitmap", NULL); + val = of_get_property(pbm->op->node, "ino-bitmap", NULL); if (val) { pbm->ino_bitmap = (((u64)val[1] << 32UL) | ((u64)val[0] << 0UL)); @@ -365,7 +365,7 @@ static void pci_register_legacy_regions(struct resource *io_res, static void pci_register_iommu_region(struct pci_pbm_info *pbm) { - const u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL); + const u32 *vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); if (vdma) { struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL); @@ -389,7 +389,7 @@ void pci_determine_mem_io_space(struct pci_pbm_info *pbm) int num_pbm_ranges; saw_mem = saw_io = 0; - pbm_ranges = of_get_property(pbm->prom_node, "ranges", &i); + pbm_ranges = of_get_property(pbm->op->node, "ranges", &i); if (!pbm_ranges) { prom_printf("PCI: Fatal error, missing PBM ranges property " " for %s\n", diff --git a/arch/sparc64/kernel/pci_fire.c b/arch/sparc64/kernel/pci_fire.c index d23bb6f53cd..9462b68f489 100644 --- a/arch/sparc64/kernel/pci_fire.c +++ b/arch/sparc64/kernel/pci_fire.c @@ -8,34 +8,16 @@ #include <linux/init.h> #include <linux/msi.h> #include <linux/irq.h> +#include <linux/of_device.h> -#include <asm/oplib.h> #include <asm/prom.h> #include <asm/irq.h> +#include <asm/upa.h> #include "pci_impl.h" -#define fire_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define fire_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") - -static void __init pci_fire_scan_bus(struct pci_pbm_info *pbm) -{ - pbm->pci_bus = pci_scan_one_pbm(pbm); - - /* XXX register error interrupt handlers XXX */ -} +#define DRIVER_NAME "fire" +#define PFX DRIVER_NAME ": " #define FIRE_IOMMU_CONTROL 0x40000UL #define FIRE_IOMMU_TSBBASE 0x40008UL @@ -69,21 +51,21 @@ static int pci_fire_pbm_iommu_init(struct pci_pbm_info *pbm) /* * Invalidate TLB Entries. */ - fire_write(iommu->iommu_flushinv, ~(u64)0); + upa_writeq(~(u64)0, iommu->iommu_flushinv); err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask, pbm->numa_node); if (err) return err; - fire_write(iommu->iommu_tsbbase, __pa(iommu->page_table) | 0x7UL); + upa_writeq(__pa(iommu->page_table) | 0x7UL, iommu->iommu_tsbbase); - control = fire_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); control |= (0x00000400 /* TSB cache snoop enable */ | 0x00000300 /* Cache mode */ | 0x00000002 /* Bypass enable */ | 0x00000001 /* Translation enable */); - fire_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); return 0; } @@ -165,7 +147,7 @@ struct pci_msiq_entry { static int pci_fire_get_head(struct pci_pbm_info *pbm, unsigned long msiqid, unsigned long *head) { - *head = fire_read(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid)); + *head = upa_readq(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid)); return 0; } @@ -191,8 +173,7 @@ static int pci_fire_dequeue_msi(struct pci_pbm_info *pbm, unsigned long msiqid, *msi = msi_num = ((ep->word0 & MSIQ_WORD0_DATA0) >> MSIQ_WORD0_DATA0_SHIFT); - fire_write(pbm->pbm_regs + MSI_CLEAR(msi_num), - MSI_CLEAR_EQWR_N); + upa_writeq(MSI_CLEAR_EQWR_N, pbm->pbm_regs + MSI_CLEAR(msi_num)); /* Clear the entry. */ ep->word0 &= ~MSIQ_WORD0_FMT_TYPE; @@ -208,7 +189,7 @@ static int pci_fire_dequeue_msi(struct pci_pbm_info *pbm, unsigned long msiqid, static int pci_fire_set_head(struct pci_pbm_info *pbm, unsigned long msiqid, unsigned long head) { - fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid), head); + upa_writeq(head, pbm->pbm_regs + EVENT_QUEUE_HEAD(msiqid)); return 0; } @@ -217,17 +198,16 @@ static int pci_fire_msi_setup(struct pci_pbm_info *pbm, unsigned long msiqid, { u64 val; - val = fire_read(pbm->pbm_regs + MSI_MAP(msi)); + val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); val &= ~(MSI_MAP_EQNUM); val |= msiqid; - fire_write(pbm->pbm_regs + MSI_MAP(msi), val); + upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi)); - fire_write(pbm->pbm_regs + MSI_CLEAR(msi), - MSI_CLEAR_EQWR_N); + upa_writeq(MSI_CLEAR_EQWR_N, pbm->pbm_regs + MSI_CLEAR(msi)); - val = fire_read(pbm->pbm_regs + MSI_MAP(msi)); + val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); val |= MSI_MAP_VALID; - fire_write(pbm->pbm_regs + MSI_MAP(msi), val); + upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi)); return 0; } @@ -237,12 +217,12 @@ static int pci_fire_msi_teardown(struct pci_pbm_info *pbm, unsigned long msi) unsigned long msiqid; u64 val; - val = fire_read(pbm->pbm_regs + MSI_MAP(msi)); + val = upa_readq(pbm->pbm_regs + MSI_MAP(msi)); msiqid = (val & MSI_MAP_EQNUM); val &= ~MSI_MAP_VALID; - fire_write(pbm->pbm_regs + MSI_MAP(msi), val); + upa_writeq(val, pbm->pbm_regs + MSI_MAP(msi)); return 0; } @@ -261,22 +241,19 @@ static int pci_fire_msiq_alloc(struct pci_pbm_info *pbm) memset((char *)pages, 0, PAGE_SIZE << order); pbm->msi_queues = (void *) pages; - fire_write(pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG, - (EVENT_QUEUE_BASE_ADDR_ALL_ONES | - __pa(pbm->msi_queues))); + upa_writeq((EVENT_QUEUE_BASE_ADDR_ALL_ONES | + __pa(pbm->msi_queues)), + pbm->pbm_regs + EVENT_QUEUE_BASE_ADDR_REG); - fire_write(pbm->pbm_regs + IMONDO_DATA0, - pbm->portid << 6); - fire_write(pbm->pbm_regs + IMONDO_DATA1, 0); + upa_writeq(pbm->portid << 6, pbm->pbm_regs + IMONDO_DATA0); + upa_writeq(0, pbm->pbm_regs + IMONDO_DATA1); - fire_write(pbm->pbm_regs + MSI_32BIT_ADDR, - pbm->msi32_start); - fire_write(pbm->pbm_regs + MSI_64BIT_ADDR, - pbm->msi64_start); + upa_writeq(pbm->msi32_start, pbm->pbm_regs + MSI_32BIT_ADDR); + upa_writeq(pbm->msi64_start, pbm->pbm_regs + MSI_64BIT_ADDR); for (i = 0; i < pbm->msiq_num; i++) { - fire_write(pbm->pbm_regs + EVENT_QUEUE_HEAD(i), 0); - fire_write(pbm->pbm_regs + EVENT_QUEUE_TAIL(i), 0); + upa_writeq(0, pbm->pbm_regs + EVENT_QUEUE_HEAD(i)); + upa_writeq(0, pbm->pbm_regs + EVENT_QUEUE_TAIL(i)); } return 0; @@ -310,9 +287,9 @@ static int pci_fire_msiq_build_irq(struct pci_pbm_info *pbm, /* XXX iterate amongst the 4 IRQ controllers XXX */ int_ctrlr = (1UL << 6); - val = fire_read(imap_reg); + val = upa_readq(imap_reg); val |= (1UL << 63) | int_ctrlr; - fire_write(imap_reg, val); + upa_writeq(val, imap_reg); fixup = ((pbm->portid << 6) | devino) - int_ctrlr; @@ -320,9 +297,8 @@ static int pci_fire_msiq_build_irq(struct pci_pbm_info *pbm, if (!virt_irq) return -ENOMEM; - fire_write(pbm->pbm_regs + - EVENT_QUEUE_CONTROL_SET(msiqid), - EVENT_QUEUE_CONTROL_SET_EN); + upa_writeq(EVENT_QUEUE_CONTROL_SET_EN, + pbm->pbm_regs + EVENT_QUEUE_CONTROL_SET(msiqid)); return virt_irq; } @@ -390,77 +366,65 @@ static void pci_fire_hw_init(struct pci_pbm_info *pbm) { u64 val; - fire_write(pbm->controller_regs + FIRE_PARITY_CONTROL, - FIRE_PARITY_ENAB); + upa_writeq(FIRE_PARITY_ENAB, + pbm->controller_regs + FIRE_PARITY_CONTROL); - fire_write(pbm->controller_regs + FIRE_FATAL_RESET_CTL, - (FIRE_FATAL_RESET_SPARE | + upa_writeq((FIRE_FATAL_RESET_SPARE | FIRE_FATAL_RESET_MB | FIRE_FATAL_RESET_CPE | FIRE_FATAL_RESET_APE | FIRE_FATAL_RESET_PIO | FIRE_FATAL_RESET_JW | FIRE_FATAL_RESET_JI | - FIRE_FATAL_RESET_JR)); + FIRE_FATAL_RESET_JR), + pbm->controller_regs + FIRE_FATAL_RESET_CTL); - fire_write(pbm->controller_regs + FIRE_CORE_INTR_ENABLE, ~(u64)0); + upa_writeq(~(u64)0, pbm->controller_regs + FIRE_CORE_INTR_ENABLE); - val = fire_read(pbm->pbm_regs + FIRE_TLU_CTRL); + val = upa_readq(pbm->pbm_regs + FIRE_TLU_CTRL); val |= (FIRE_TLU_CTRL_TIM | FIRE_TLU_CTRL_QDET | FIRE_TLU_CTRL_CFG); - fire_write(pbm->pbm_regs + FIRE_TLU_CTRL, val); - fire_write(pbm->pbm_regs + FIRE_TLU_DEV_CTRL, 0); - fire_write(pbm->pbm_regs + FIRE_TLU_LINK_CTRL, - FIRE_TLU_LINK_CTRL_CLK); - - fire_write(pbm->pbm_regs + FIRE_LPU_RESET, 0); - fire_write(pbm->pbm_regs + FIRE_LPU_LLCFG, - FIRE_LPU_LLCFG_VC0); - fire_write(pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL, - (FIRE_LPU_FCTRL_UCTRL_N | - FIRE_LPU_FCTRL_UCTRL_P)); - fire_write(pbm->pbm_regs + FIRE_LPU_TXL_FIFOP, - ((0xffff << 16) | (0x0000 << 0))); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2, 3000000); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3, 500000); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4, - (2 << 16) | (140 << 8)); - fire_write(pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5, 0); - - fire_write(pbm->pbm_regs + FIRE_DMC_IENAB, ~(u64)0); - fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_A, 0); - fire_write(pbm->pbm_regs + FIRE_DMC_DBG_SEL_B, 0); - - fire_write(pbm->pbm_regs + FIRE_PEC_IENAB, ~(u64)0); + upa_writeq(val, pbm->pbm_regs + FIRE_TLU_CTRL); + upa_writeq(0, pbm->pbm_regs + FIRE_TLU_DEV_CTRL); + upa_writeq(FIRE_TLU_LINK_CTRL_CLK, + pbm->pbm_regs + FIRE_TLU_LINK_CTRL); + + upa_writeq(0, pbm->pbm_regs + FIRE_LPU_RESET); + upa_writeq(FIRE_LPU_LLCFG_VC0, pbm->pbm_regs + FIRE_LPU_LLCFG); + upa_writeq((FIRE_LPU_FCTRL_UCTRL_N | FIRE_LPU_FCTRL_UCTRL_P), + pbm->pbm_regs + FIRE_LPU_FCTRL_UCTRL); + upa_writeq(((0xffff << 16) | (0x0000 << 0)), + pbm->pbm_regs + FIRE_LPU_TXL_FIFOP); + upa_writeq(3000000, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG2); + upa_writeq(500000, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG3); + upa_writeq((2 << 16) | (140 << 8), + pbm->pbm_regs + FIRE_LPU_LTSSM_CFG4); + upa_writeq(0, pbm->pbm_regs + FIRE_LPU_LTSSM_CFG5); + + upa_writeq(~(u64)0, pbm->pbm_regs + FIRE_DMC_IENAB); + upa_writeq(0, pbm->pbm_regs + FIRE_DMC_DBG_SEL_A); + upa_writeq(0, pbm->pbm_regs + FIRE_DMC_DBG_SEL_B); + + upa_writeq(~(u64)0, pbm->pbm_regs + FIRE_PEC_IENAB); } -static int __init pci_fire_pbm_init(struct pci_controller_info *p, - struct device_node *dp, u32 portid) +static int __init pci_fire_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, u32 portid) { const struct linux_prom64_registers *regs; - struct pci_pbm_info *pbm; + struct device_node *dp = op->node; int err; - if ((portid & 1) == 0) - pbm = &p->pbm_A; - else - pbm = &p->pbm_B; - - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; - pbm->numa_node = -1; - pbm->scan_bus = pci_fire_scan_bus; pbm->pci_ops = &sun4u_pci_ops; pbm->config_space_reg_bits = 12; pbm->index = pci_num_pbms++; pbm->portid = portid; - pbm->parent = p; - pbm->prom_node = dp; + pbm->op = op; pbm->name = dp->full_name; regs = of_get_property(dp, "reg", NULL); @@ -481,53 +445,77 @@ static int __init pci_fire_pbm_init(struct pci_controller_info *p, pci_fire_msi_init(pbm); - return 0; -} + pbm->pci_bus = pci_scan_one_pbm(pbm, &op->dev); + + /* XXX register error interrupt handlers XXX */ + + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; -static inline int portid_compare(u32 x, u32 y) -{ - if (x == (y ^ 1)) - return 1; return 0; } -void __init fire_pci_init(struct device_node *dp, const char *model_name) +static int __devinit fire_probe(struct of_device *op, + const struct of_device_id *match) { - struct pci_controller_info *p; - u32 portid = of_getintprop_default(dp, "portid", 0xff); - struct iommu *iommu; + struct device_node *dp = op->node; struct pci_pbm_info *pbm; + struct iommu *iommu; + u32 portid; + int err; - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - if (portid_compare(pbm->portid, portid)) { - if (pci_fire_pbm_init(pbm->parent, dp, portid)) - goto fatal_memory_error; - return; - } + portid = of_getintprop_default(dp, "portid", 0xff); + + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbminfo.\n"); + goto out_err; } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n"); + goto out_free_controller; + } - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + pbm->iommu = iommu; - p->pbm_A.iommu = iommu; + err = pci_fire_pbm_init(pbm, op, portid); + if (err) + goto out_free_iommu; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + dev_set_drvdata(&op->dev, pbm); - p->pbm_B.iommu = iommu; + return 0; - if (pci_fire_pbm_init(p, dp, portid)) - goto fatal_memory_error; +out_free_iommu: + kfree(pbm->iommu); + +out_free_controller: + kfree(pbm); - return; +out_err: + return err; +} + +static struct of_device_id __initdata fire_match[] = { + { + .name = "pci", + .compatible = "pciex108e,80f0", + }, + {}, +}; -fatal_memory_error: - prom_printf("PCI_FIRE: Fatal memory allocation error.\n"); - prom_halt(); +static struct of_platform_driver fire_driver = { + .name = DRIVER_NAME, + .match_table = fire_match, + .probe = fire_probe, +}; + +static int __init fire_init(void) +{ + return of_register_driver(&fire_driver, &of_bus_type); } + +subsys_initcall(fire_init); diff --git a/arch/sparc64/kernel/pci_impl.h b/arch/sparc64/kernel/pci_impl.h index c385d126be1..03186824327 100644 --- a/arch/sparc64/kernel/pci_impl.h +++ b/arch/sparc64/kernel/pci_impl.h @@ -10,6 +10,7 @@ #include <linux/spinlock.h> #include <linux/pci.h> #include <linux/msi.h> +#include <linux/of_device.h> #include <asm/io.h> #include <asm/prom.h> #include <asm/iommu.h> @@ -56,15 +57,11 @@ struct sparc64_msiq_cookie { }; #endif -struct pci_controller_info; - struct pci_pbm_info { struct pci_pbm_info *next; + struct pci_pbm_info *sibling; int index; - /* PCI controller we sit under. */ - struct pci_controller_info *parent; - /* Physical address base of controller registers. */ unsigned long controller_regs; @@ -94,7 +91,7 @@ struct pci_pbm_info { char *name; /* OBP specific information. */ - struct device_node *prom_node; + struct of_device *op; u64 ino_bitmap; /* PBM I/O and Memory space resources. */ @@ -107,6 +104,10 @@ struct pci_pbm_info { /* This will be 12 on PCI-E controllers, 8 elsewhere. */ unsigned long config_space_reg_bits; + unsigned long pci_afsr; + unsigned long pci_afar; + unsigned long pci_csr; + /* State of 66MHz capabilities on this PBM. */ int is_66mhz_capable; int all_devs_66mhz; @@ -146,25 +147,19 @@ struct pci_pbm_info { unsigned int pci_first_busno; unsigned int pci_last_busno; struct pci_bus *pci_bus; - void (*scan_bus)(struct pci_pbm_info *); struct pci_ops *pci_ops; int numa_node; }; -struct pci_controller_info { - /* The PCI bus modules controlled by us. */ - struct pci_pbm_info pbm_A; - struct pci_pbm_info pbm_B; -}; - extern struct pci_pbm_info *pci_pbm_root; extern int pci_num_pbms; /* PCI bus scanning and fixup support. */ extern void pci_get_pbm_props(struct pci_pbm_info *pbm); -extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm); +extern struct pci_bus *pci_scan_one_pbm(struct pci_pbm_info *pbm, + struct device *parent); extern void pci_determine_mem_io_space(struct pci_pbm_info *pbm); /* Error reporting support. */ @@ -183,4 +178,8 @@ extern void pci_config_write32(u32 *addr, u32 val); extern struct pci_ops sun4u_pci_ops; extern struct pci_ops sun4v_pci_ops; +extern volatile int pci_poke_in_progress; +extern volatile int pci_poke_cpu; +extern volatile int pci_poke_faulted; + #endif /* !(PCI_IMPL_H) */ diff --git a/arch/sparc64/kernel/pci_msi.c b/arch/sparc64/kernel/pci_msi.c index 60c71e35021..2e680f34f72 100644 --- a/arch/sparc64/kernel/pci_msi.c +++ b/arch/sparc64/kernel/pci_msi.c @@ -323,7 +323,7 @@ void sparc64_pbm_msi_init(struct pci_pbm_info *pbm, const u32 *val; int len; - val = of_get_property(pbm->prom_node, "#msi-eqs", &len); + val = of_get_property(pbm->op->node, "#msi-eqs", &len); if (!val || len != 4) goto no_msi; pbm->msiq_num = *val; @@ -346,16 +346,16 @@ void sparc64_pbm_msi_init(struct pci_pbm_info *pbm, u32 msi64_len; } *arng; - val = of_get_property(pbm->prom_node, "msi-eq-size", &len); + val = of_get_property(pbm->op->node, "msi-eq-size", &len); if (!val || len != 4) goto no_msi; pbm->msiq_ent_count = *val; - mqp = of_get_property(pbm->prom_node, + mqp = of_get_property(pbm->op->node, "msi-eq-to-devino", &len); if (!mqp) - mqp = of_get_property(pbm->prom_node, + mqp = of_get_property(pbm->op->node, "msi-eq-devino", &len); if (!mqp || len != sizeof(struct msiq_prop)) goto no_msi; @@ -363,27 +363,27 @@ void sparc64_pbm_msi_init(struct pci_pbm_info *pbm, pbm->msiq_first = mqp->first_msiq; pbm->msiq_first_devino = mqp->first_devino; - val = of_get_property(pbm->prom_node, "#msi", &len); + val = of_get_property(pbm->op->node, "#msi", &len); if (!val || len != 4) goto no_msi; pbm->msi_num = *val; - mrng = of_get_property(pbm->prom_node, "msi-ranges", &len); + mrng = of_get_property(pbm->op->node, "msi-ranges", &len); if (!mrng || len != sizeof(struct msi_range_prop)) goto no_msi; pbm->msi_first = mrng->first_msi; - val = of_get_property(pbm->prom_node, "msi-data-mask", &len); + val = of_get_property(pbm->op->node, "msi-data-mask", &len); if (!val || len != 4) goto no_msi; pbm->msi_data_mask = *val; - val = of_get_property(pbm->prom_node, "msix-data-width", &len); + val = of_get_property(pbm->op->node, "msix-data-width", &len); if (!val || len != 4) goto no_msi; pbm->msix_data_width = *val; - arng = of_get_property(pbm->prom_node, "msi-address-ranges", + arng = of_get_property(pbm->op->node, "msi-address-ranges", &len); if (!arng || len != sizeof(struct addr_range_prop)) goto no_msi; diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index f85b6bebb0b..dfb3ec89298 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -17,29 +17,14 @@ #include <asm/irq.h> #include <asm/starfire.h> #include <asm/prom.h> -#include <asm/oplib.h> +#include <asm/upa.h> #include "pci_impl.h" #include "iommu_common.h" +#include "psycho_common.h" -/* All PSYCHO registers are 64-bits. The following accessor - * routines are how they are accessed. The REG parameter - * is a physical address. - */ -#define psycho_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define psycho_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") +#define DRIVER_NAME "psycho" +#define PFX DRIVER_NAME ": " /* Misc. PSYCHO PCI controller register offsets and definitions. */ #define PSYCHO_CONTROL 0x0010UL @@ -67,37 +52,7 @@ #define PSYCHO_PCICTRL_RESV4 0x00000000000000c0UL /* Reserved */ #define PSYCHO_PCICTRL_AEN 0x000000000000003fUL /* PCI DVMA Arbitration Enable */ -/* U2P Programmer's Manual, page 13-55, configuration space - * address format: - * - * 32 24 23 16 15 11 10 8 7 2 1 0 - * --------------------------------------------------------- - * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 | - * --------------------------------------------------------- - */ -#define PSYCHO_CONFIG_BASE(PBM) \ - ((PBM)->config_space | (1UL << 24)) -#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \ - (((unsigned long)(BUS) << 16) | \ - ((unsigned long)(DEVFN) << 8) | \ - ((unsigned long)(REG))) - -static void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm, - unsigned char bus, - unsigned int devfn, - int where) -{ - if (!pbm) - return NULL; - return (void *) - (PSYCHO_CONFIG_BASE(pbm) | - PSYCHO_CONFIG_ENCODE(bus, devfn, where)); -} - /* PSYCHO error handling support. */ -enum psycho_error_type { - UE_ERR, CE_ERR, PCI_ERR -}; /* Helper function of IOMMU error checking, which checks out * the state of the streaming buffers. The IOMMU lock is @@ -122,129 +77,10 @@ enum psycho_error_type { #define PSYCHO_STC_DATA_B 0xc000UL #define PSYCHO_STC_ERR_A 0xb400UL #define PSYCHO_STC_ERR_B 0xc400UL -#define PSYCHO_STCERR_WRITE 0x0000000000000002UL /* Write Error */ -#define PSYCHO_STCERR_READ 0x0000000000000001UL /* Read Error */ #define PSYCHO_STC_TAG_A 0xb800UL #define PSYCHO_STC_TAG_B 0xc800UL -#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL /* Physical Page Number */ -#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL /* Virtual Page Number */ -#define PSYCHO_STCTAG_VALID 0x0000000000000002UL /* Valid */ -#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL /* Writable */ #define PSYCHO_STC_LINE_A 0xb900UL #define PSYCHO_STC_LINE_B 0xc900UL -#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL /* LRU Index */ -#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL /* Dirty Data Start Pointer */ -#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL /* Line Address */ -#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL /* Dirty Data End Pointer */ -#define PSYCHO_STCLINE_VALID 0x0000000000000002UL /* Valid */ -#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL /* Fetch Outstanding / Flush Necessary */ - -static DEFINE_SPINLOCK(stc_buf_lock); -static unsigned long stc_error_buf[128]; -static unsigned long stc_tag_buf[16]; -static unsigned long stc_line_buf[16]; - -static void __psycho_check_one_stc(struct pci_pbm_info *pbm, - int is_pbm_a) -{ - struct strbuf *strbuf = &pbm->stc; - unsigned long regbase = pbm->controller_regs; - unsigned long err_base, tag_base, line_base; - u64 control; - int i; - - if (is_pbm_a) { - err_base = regbase + PSYCHO_STC_ERR_A; - tag_base = regbase + PSYCHO_STC_TAG_A; - line_base = regbase + PSYCHO_STC_LINE_A; - } else { - err_base = regbase + PSYCHO_STC_ERR_B; - tag_base = regbase + PSYCHO_STC_TAG_B; - line_base = regbase + PSYCHO_STC_LINE_B; - } - - spin_lock(&stc_buf_lock); - - /* This is __REALLY__ dangerous. When we put the - * streaming buffer into diagnostic mode to probe - * it's tags and error status, we _must_ clear all - * of the line tag valid bits before re-enabling - * the streaming buffer. If any dirty data lives - * in the STC when we do this, we will end up - * invalidating it before it has a chance to reach - * main memory. - */ - control = psycho_read(strbuf->strbuf_control); - psycho_write(strbuf->strbuf_control, - (control | PSYCHO_STRBUF_CTRL_DENAB)); - for (i = 0; i < 128; i++) { - unsigned long val; - - val = psycho_read(err_base + (i * 8UL)); - psycho_write(err_base + (i * 8UL), 0UL); - stc_error_buf[i] = val; - } - for (i = 0; i < 16; i++) { - stc_tag_buf[i] = psycho_read(tag_base + (i * 8UL)); - stc_line_buf[i] = psycho_read(line_base + (i * 8UL)); - psycho_write(tag_base + (i * 8UL), 0UL); - psycho_write(line_base + (i * 8UL), 0UL); - } - - /* OK, state is logged, exit diagnostic mode. */ - psycho_write(strbuf->strbuf_control, control); - - for (i = 0; i < 16; i++) { - int j, saw_error, first, last; - - saw_error = 0; - first = i * 8; - last = first + 8; - for (j = first; j < last; j++) { - unsigned long errval = stc_error_buf[j]; - if (errval != 0) { - saw_error++; - printk("%s: STC_ERR(%d)[wr(%d)rd(%d)]\n", - pbm->name, - j, - (errval & PSYCHO_STCERR_WRITE) ? 1 : 0, - (errval & PSYCHO_STCERR_READ) ? 1 : 0); - } - } - if (saw_error != 0) { - unsigned long tagval = stc_tag_buf[i]; - unsigned long lineval = stc_line_buf[i]; - printk("%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)V(%d)W(%d)]\n", - pbm->name, - i, - ((tagval & PSYCHO_STCTAG_PPN) >> 19UL), - (tagval & PSYCHO_STCTAG_VPN), - ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0), - ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0)); - printk("%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)LADDR(%lx)EP(%lx)" - "V(%d)FOFN(%d)]\n", - pbm->name, - i, - ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL), - ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL), - ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL), - ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL), - ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0), - ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0)); - } - } - - spin_unlock(&stc_buf_lock); -} - -static void __psycho_check_stc_error(struct pci_pbm_info *pbm, - unsigned long afsr, - unsigned long afar, - enum psycho_error_type type) -{ - __psycho_check_one_stc(pbm, - (pbm == &pbm->parent->pbm_A)); -} /* When an Uncorrectable Error or a PCI Error happens, we * interrogate the IOMMU state to see if it is the cause. @@ -271,122 +107,7 @@ static void __psycho_check_stc_error(struct pci_pbm_info *pbm, #define PSYCHO_IOMMU_TSBBASE 0x0208UL #define PSYCHO_IOMMU_FLUSH 0x0210UL #define PSYCHO_IOMMU_TAG 0xa580UL -#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL) -#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL) -#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL) -#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL) -#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL) -#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL #define PSYCHO_IOMMU_DATA 0xa600UL -#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL) -#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL) -#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL -static void psycho_check_iommu_error(struct pci_pbm_info *pbm, - unsigned long afsr, - unsigned long afar, - enum psycho_error_type type) -{ - struct iommu *iommu = pbm->iommu; - unsigned long iommu_tag[16]; - unsigned long iommu_data[16]; - unsigned long flags; - u64 control; - int i; - - spin_lock_irqsave(&iommu->lock, flags); - control = psycho_read(iommu->iommu_control); - if (control & PSYCHO_IOMMU_CTRL_XLTEERR) { - char *type_string; - - /* Clear the error encountered bit. */ - control &= ~PSYCHO_IOMMU_CTRL_XLTEERR; - psycho_write(iommu->iommu_control, control); - - switch((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) { - case 0: - type_string = "Protection Error"; - break; - case 1: - type_string = "Invalid Error"; - break; - case 2: - type_string = "TimeOut Error"; - break; - case 3: - default: - type_string = "ECC Error"; - break; - }; - printk("%s: IOMMU Error, type[%s]\n", - pbm->name, type_string); - - /* Put the IOMMU into diagnostic mode and probe - * it's TLB for entries with error status. - * - * It is very possible for another DVMA to occur - * while we do this probe, and corrupt the system - * further. But we are so screwed at this point - * that we are likely to crash hard anyways, so - * get as much diagnostic information to the - * console as we can. - */ - psycho_write(iommu->iommu_control, - control | PSYCHO_IOMMU_CTRL_DENAB); - for (i = 0; i < 16; i++) { - unsigned long base = pbm->controller_regs; - - iommu_tag[i] = - psycho_read(base + PSYCHO_IOMMU_TAG + (i * 8UL)); - iommu_data[i] = - psycho_read(base + PSYCHO_IOMMU_DATA + (i * 8UL)); - - /* Now clear out the entry. */ - psycho_write(base + PSYCHO_IOMMU_TAG + (i * 8UL), 0); - psycho_write(base + PSYCHO_IOMMU_DATA + (i * 8UL), 0); - } - - /* Leave diagnostic mode. */ - psycho_write(iommu->iommu_control, control); - - for (i = 0; i < 16; i++) { - unsigned long tag, data; - - tag = iommu_tag[i]; - if (!(tag & PSYCHO_IOMMU_TAG_ERR)) - continue; - - data = iommu_data[i]; - switch((tag & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) { - case 0: - type_string = "Protection Error"; - break; - case 1: - type_string = "Invalid Error"; - break; - case 2: - type_string = "TimeOut Error"; - break; - case 3: - default: - type_string = "ECC Error"; - break; - }; - printk("%s: IOMMU TAG(%d)[error(%s) wr(%d) str(%d) sz(%dK) vpg(%08lx)]\n", - pbm->name, i, type_string, - ((tag & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0), - ((tag & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0), - ((tag & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8), - (tag & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT); - printk("%s: IOMMU DATA(%d)[valid(%d) cache(%d) ppg(%016lx)]\n", - pbm->name, i, - ((data & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0), - ((data & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0), - (data & PSYCHO_IOMMU_DATA_PPAGE) << IOMMU_PAGE_SHIFT); - } - } - __psycho_check_stc_error(pbm, afsr, afar, type); - spin_unlock_irqrestore(&iommu->lock, flags); -} /* Uncorrectable Errors. Cause of the error and the address are * recorded in the UE_AFSR and UE_AFAR of PSYCHO. They are errors @@ -410,15 +131,14 @@ static void psycho_check_iommu_error(struct pci_pbm_info *pbm, static irqreturn_t psycho_ue_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; unsigned long afsr_reg = pbm->controller_regs + PSYCHO_UE_AFSR; unsigned long afar_reg = pbm->controller_regs + PSYCHO_UE_AFAR; unsigned long afsr, afar, error_bits; int reported; /* Latch uncorrectable error status. */ - afar = psycho_read(afar_reg); - afsr = psycho_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear the primary/secondary error status bits. */ error_bits = afsr & @@ -426,7 +146,7 @@ static irqreturn_t psycho_ue_intr(int irq, void *dev_id) PSYCHO_UEAFSR_SPIO | PSYCHO_UEAFSR_SDRD | PSYCHO_UEAFSR_SDWR); if (!error_bits) return IRQ_NONE; - psycho_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Uncorrectable Error, primary error type[%s]\n", @@ -463,8 +183,9 @@ static irqreturn_t psycho_ue_intr(int irq, void *dev_id) printk("]\n"); /* Interrogate both IOMMUs for error status. */ - psycho_check_iommu_error(&p->pbm_A, afsr, afar, UE_ERR); - psycho_check_iommu_error(&p->pbm_B, afsr, afar, UE_ERR); + psycho_check_iommu_error(pbm, afsr, afar, UE_ERR); + if (pbm->sibling) + psycho_check_iommu_error(pbm->sibling, afsr, afar, UE_ERR); return IRQ_HANDLED; } @@ -495,8 +216,8 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id) int reported; /* Latch error status. */ - afar = psycho_read(afar_reg); - afsr = psycho_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear primary/secondary error status bits. */ error_bits = afsr & @@ -504,7 +225,7 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id) PSYCHO_CEAFSR_SPIO | PSYCHO_CEAFSR_SDRD | PSYCHO_CEAFSR_SDWR); if (!error_bits) return IRQ_NONE; - psycho_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Correctable Error, primary error type[%s]\n", @@ -554,164 +275,9 @@ static irqreturn_t psycho_ce_intr(int irq, void *dev_id) */ #define PSYCHO_PCI_AFSR_A 0x2010UL #define PSYCHO_PCI_AFSR_B 0x4010UL -#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL /* Primary Master Abort Error */ -#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL /* Primary Target Abort Error */ -#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL /* Primary Excessive Retries */ -#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL /* Primary Parity Error */ -#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL /* Secondary Master Abort Error */ -#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL /* Secondary Target Abort Error */ -#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL /* Secondary Excessive Retries */ -#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL /* Secondary Parity Error */ -#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL /* Reserved */ -#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL /* Bytemask of failed transfer */ -#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL /* Trans was block operation */ -#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL /* Reserved */ -#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL /* MID causing the error */ -#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL /* Reserved */ #define PSYCHO_PCI_AFAR_A 0x2018UL #define PSYCHO_PCI_AFAR_B 0x4018UL -static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm, int is_pbm_a) -{ - unsigned long csr_reg, csr, csr_error_bits; - irqreturn_t ret = IRQ_NONE; - u16 stat, *addr; - - if (is_pbm_a) { - csr_reg = pbm->controller_regs + PSYCHO_PCIA_CTRL; - } else { - csr_reg = pbm->controller_regs + PSYCHO_PCIB_CTRL; - } - csr = psycho_read(csr_reg); - csr_error_bits = - csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR); - if (csr_error_bits) { - /* Clear the errors. */ - psycho_write(csr_reg, csr); - - /* Log 'em. */ - if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR) - printk("%s: PCI streaming byte hole error asserted.\n", - pbm->name); - if (csr_error_bits & PSYCHO_PCICTRL_SERR) - printk("%s: PCI SERR signal asserted.\n", pbm->name); - ret = IRQ_HANDLED; - } - addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, - 0, PCI_STATUS); - pci_config_read16(addr, &stat); - if (stat & (PCI_STATUS_PARITY | - PCI_STATUS_SIG_TARGET_ABORT | - PCI_STATUS_REC_TARGET_ABORT | - PCI_STATUS_REC_MASTER_ABORT | - PCI_STATUS_SIG_SYSTEM_ERROR)) { - printk("%s: PCI bus error, PCI_STATUS[%04x]\n", - pbm->name, stat); - pci_config_write16(addr, 0xffff); - ret = IRQ_HANDLED; - } - return ret; -} - -static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id) -{ - struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; - unsigned long afsr_reg, afar_reg; - unsigned long afsr, afar, error_bits; - int is_pbm_a, reported; - - is_pbm_a = (pbm == &pbm->parent->pbm_A); - if (is_pbm_a) { - afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_A; - afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_A; - } else { - afsr_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFSR_B; - afar_reg = p->pbm_A.controller_regs + PSYCHO_PCI_AFAR_B; - } - - /* Latch error status. */ - afar = psycho_read(afar_reg); - afsr = psycho_read(afsr_reg); - - /* Clear primary/secondary error status bits. */ - error_bits = afsr & - (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA | - PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR | - PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA | - PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR); - if (!error_bits) - return psycho_pcierr_intr_other(pbm, is_pbm_a); - psycho_write(afsr_reg, error_bits); - - /* Log the error. */ - printk("%s: PCI Error, primary error type[%s]\n", - pbm->name, - (((error_bits & PSYCHO_PCIAFSR_PMA) ? - "Master Abort" : - ((error_bits & PSYCHO_PCIAFSR_PTA) ? - "Target Abort" : - ((error_bits & PSYCHO_PCIAFSR_PRTRY) ? - "Excessive Retries" : - ((error_bits & PSYCHO_PCIAFSR_PPERR) ? - "Parity Error" : "???")))))); - printk("%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n", - pbm->name, - (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL, - (afsr & PSYCHO_PCIAFSR_MID) >> 25UL, - (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0); - printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar); - printk("%s: PCI Secondary errors [", pbm->name); - reported = 0; - if (afsr & PSYCHO_PCIAFSR_SMA) { - reported++; - printk("(Master Abort)"); - } - if (afsr & PSYCHO_PCIAFSR_STA) { - reported++; - printk("(Target Abort)"); - } - if (afsr & PSYCHO_PCIAFSR_SRTRY) { - reported++; - printk("(Excessive Retries)"); - } - if (afsr & PSYCHO_PCIAFSR_SPERR) { - reported++; - printk("(Parity Error)"); - } - if (!reported) - printk("(none)"); - printk("]\n"); - - /* For the error types shown, scan PBM's PCI bus for devices - * which have logged that error type. - */ - - /* If we see a Target Abort, this could be the result of an - * IOMMU translation error of some sort. It is extremely - * useful to log this information as usually it indicates - * a bug in the IOMMU support code or a PCI device driver. - */ - if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) { - psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR); - pci_scan_for_target_abort(pbm, pbm->pci_bus); - } - if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA)) - pci_scan_for_master_abort(pbm, pbm->pci_bus); - - /* For excessive retries, PSYCHO/PBM will abort the device - * and there is no way to specifically check for excessive - * retries in the config space status registers. So what - * we hope is that we'll catch it via the master/target - * abort events. - */ - - if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR)) - pci_scan_for_parity_error(pbm, pbm->pci_bus); - - return IRQ_HANDLED; -} - /* XXX What about PowerFail/PowerManagement??? -DaveM */ #define PSYCHO_ECC_CTRL 0x0020 #define PSYCHO_ECCCTRL_EE 0x8000000000000000UL /* Enable ECC Checking */ @@ -719,7 +285,7 @@ static irqreturn_t psycho_pcierr_intr(int irq, void *dev_id) #define PSYCHO_ECCCTRL_CE 0x2000000000000000UL /* Enable CE INterrupts */ static void psycho_register_error_handlers(struct pci_pbm_info *pbm) { - struct of_device *op = of_find_device_by_node(pbm->prom_node); + struct of_device *op = of_find_device_by_node(pbm->op->node); unsigned long base = pbm->controller_regs; u64 tmp; int err; @@ -762,27 +328,26 @@ static void psycho_register_error_handlers(struct pci_pbm_info *pbm) "err=%d\n", pbm->name, err); /* Enable UE and CE interrupts for controller. */ - psycho_write(base + PSYCHO_ECC_CTRL, - (PSYCHO_ECCCTRL_EE | - PSYCHO_ECCCTRL_UE | - PSYCHO_ECCCTRL_CE)); + upa_writeq((PSYCHO_ECCCTRL_EE | + PSYCHO_ECCCTRL_UE | + PSYCHO_ECCCTRL_CE), base + PSYCHO_ECC_CTRL); /* Enable PCI Error interrupts and clear error * bits for each PBM. */ - tmp = psycho_read(base + PSYCHO_PCIA_CTRL); + tmp = upa_readq(base + PSYCHO_PCIA_CTRL); tmp |= (PSYCHO_PCICTRL_SERR | PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_EEN); tmp &= ~(PSYCHO_PCICTRL_SBH_INT); - psycho_write(base + PSYCHO_PCIA_CTRL, tmp); + upa_writeq(tmp, base + PSYCHO_PCIA_CTRL); - tmp = psycho_read(base + PSYCHO_PCIB_CTRL); + tmp = upa_readq(base + PSYCHO_PCIB_CTRL); tmp |= (PSYCHO_PCICTRL_SERR | PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_EEN); tmp &= ~(PSYCHO_PCICTRL_SBH_INT); - psycho_write(base + PSYCHO_PCIB_CTRL, tmp); + upa_writeq(tmp, base + PSYCHO_PCIB_CTRL); } /* PSYCHO boot time probing and initialization. */ @@ -803,11 +368,12 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm) pci_config_write8(addr, 64); } -static void __init psycho_scan_bus(struct pci_pbm_info *pbm) +static void __init psycho_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { pbm_config_busmastering(pbm); pbm->is_66mhz_capable = 0; - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); /* After the PCI bus scan is complete, we can register * the error interrupt handlers. @@ -815,61 +381,6 @@ static void __init psycho_scan_bus(struct pci_pbm_info *pbm) psycho_register_error_handlers(pbm); } -static int psycho_iommu_init(struct pci_pbm_info *pbm) -{ - struct iommu *iommu = pbm->iommu; - unsigned long i; - u64 control; - int err; - - /* Register addresses. */ - iommu->iommu_control = pbm->controller_regs + PSYCHO_IOMMU_CONTROL; - iommu->iommu_tsbbase = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE; - iommu->iommu_flush = pbm->controller_regs + PSYCHO_IOMMU_FLUSH; - iommu->iommu_tags = iommu->iommu_flush + (0xa580UL - 0x0210UL); - - /* PSYCHO's IOMMU lacks ctx flushing. */ - iommu->iommu_ctxflush = 0; - - /* We use the main control register of PSYCHO as the write - * completion register. - */ - iommu->write_complete_reg = pbm->controller_regs + PSYCHO_CONTROL; - - /* - * Invalidate TLB Entries. - */ - control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL); - control |= PSYCHO_IOMMU_CTRL_DENAB; - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control); - for(i = 0; i < 16; i++) { - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TAG + (i * 8UL), 0); - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_DATA + (i * 8UL), 0); - } - - /* Leave diag mode enabled for full-flushing done - * in pci_iommu.c - */ - err = iommu_table_init(iommu, IO_TSB_SIZE, 0xc0000000, 0xffffffff, - pbm->numa_node); - if (err) - return err; - - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_TSBBASE, - __pa(iommu->page_table)); - - control = psycho_read(pbm->controller_regs + PSYCHO_IOMMU_CONTROL); - control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ); - control |= (PSYCHO_IOMMU_TSBSZ_128K | PSYCHO_IOMMU_CTRL_ENAB); - psycho_write(pbm->controller_regs + PSYCHO_IOMMU_CONTROL, control); - - /* If necessary, hook us up for starfire IRQ translations. */ - if (this_is_starfire) - starfire_hookup(pbm->portid); - - return 0; -} - #define PSYCHO_IRQ_RETRY 0x1a00UL #define PSYCHO_PCIA_DIAG 0x2020UL #define PSYCHO_PCIB_DIAG 0x4020UL @@ -886,28 +397,28 @@ static void psycho_controller_hwinit(struct pci_pbm_info *pbm) { u64 tmp; - psycho_write(pbm->controller_regs + PSYCHO_IRQ_RETRY, 5); + upa_writeq(5, pbm->controller_regs + PSYCHO_IRQ_RETRY); /* Enable arbiter for all PCI slots. */ - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_CTRL); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_CTRL); tmp |= PSYCHO_PCICTRL_AEN; - psycho_write(pbm->controller_regs + PSYCHO_PCIA_CTRL, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_CTRL); - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_CTRL); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_CTRL); tmp |= PSYCHO_PCICTRL_AEN; - psycho_write(pbm->controller_regs + PSYCHO_PCIB_CTRL, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_CTRL); /* Disable DMA write / PIO read synchronization on * both PCI bus segments. * [ U2P Erratum 1243770, STP2223BGA data sheet ] */ - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIA_DIAG); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIA_DIAG); tmp |= PSYCHO_PCIDIAG_DDWSYNC; - psycho_write(pbm->controller_regs + PSYCHO_PCIA_DIAG, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIA_DIAG); - tmp = psycho_read(pbm->controller_regs + PSYCHO_PCIB_DIAG); + tmp = upa_readq(pbm->controller_regs + PSYCHO_PCIB_DIAG); tmp |= PSYCHO_PCIDIAG_DDWSYNC; - psycho_write(pbm->controller_regs + PSYCHO_PCIB_DIAG, tmp); + upa_writeq(tmp, pbm->controller_regs + PSYCHO_PCIB_DIAG); } static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, @@ -920,10 +431,16 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_A; pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_A; pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_A; + pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_A; + pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_A; + pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_A; } else { pbm->stc.strbuf_control = base + PSYCHO_STRBUF_CONTROL_B; pbm->stc.strbuf_pflush = base + PSYCHO_STRBUF_FLUSH_B; pbm->stc.strbuf_fsync = base + PSYCHO_STRBUF_FSYNC_B; + pbm->stc.strbuf_err_stat = base + PSYCHO_STC_ERR_B; + pbm->stc.strbuf_tag_diag = base + PSYCHO_STC_TAG_B; + pbm->stc.strbuf_line_diag= base + PSYCHO_STC_LINE_B; } /* PSYCHO's streaming buffer lacks ctx flushing. */ pbm->stc.strbuf_ctxflush = 0; @@ -946,7 +463,7 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, */ #undef PSYCHO_STRBUF_RERUN_ENABLE #undef PSYCHO_STRBUF_RERUN_DISABLE - control = psycho_read(pbm->stc.strbuf_control); + control = upa_readq(pbm->stc.strbuf_control); control |= PSYCHO_STRBUF_CTRL_ENAB; control &= ~(PSYCHO_STRBUF_CTRL_LENAB | PSYCHO_STRBUF_CTRL_LPTR); #ifdef PSYCHO_STRBUF_RERUN_ENABLE @@ -956,7 +473,7 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, control |= PSYCHO_STRBUF_CTRL_RRDIS; #endif #endif - psycho_write(pbm->stc.strbuf_control, control); + upa_writeq(control, pbm->stc.strbuf_control); pbm->stc.strbuf_enabled = 1; } @@ -968,111 +485,134 @@ static void psycho_pbm_strbuf_init(struct pci_pbm_info *pbm, #define PSYCHO_MEMSPACE_B 0x180000000UL #define PSYCHO_MEMSPACE_SIZE 0x07fffffffUL -static void __init psycho_pbm_init(struct pci_controller_info *p, - struct device_node *dp, int is_pbm_a) +static void __init psycho_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, int is_pbm_a) { - struct property *prop; - struct pci_pbm_info *pbm; - - if (is_pbm_a) - pbm = &p->pbm_A; - else - pbm = &p->pbm_B; - - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; - - pbm->numa_node = -1; - - pbm->scan_bus = psycho_scan_bus; - pbm->pci_ops = &sun4u_pci_ops; - pbm->config_space_reg_bits = 8; - - pbm->index = pci_num_pbms++; - - pbm->chip_type = PBM_CHIP_TYPE_PSYCHO; - pbm->chip_version = 0; - prop = of_find_property(dp, "version#", NULL); - if (prop) - pbm->chip_version = *(int *) prop->value; - pbm->chip_revision = 0; - prop = of_find_property(dp, "module-revision#", NULL); - if (prop) - pbm->chip_revision = *(int *) prop->value; - - pbm->parent = p; - pbm->prom_node = dp; - pbm->name = dp->full_name; - - printk("%s: PSYCHO PCI Bus Module ver[%x:%x]\n", - pbm->name, - pbm->chip_version, pbm->chip_revision); - - pci_determine_mem_io_space(pbm); + psycho_pbm_init_common(pbm, op, "PSYCHO", PBM_CHIP_TYPE_PSYCHO); + psycho_pbm_strbuf_init(pbm, is_pbm_a); + psycho_scan_bus(pbm, &op->dev); +} - pci_get_pbm_props(pbm); +static struct pci_pbm_info * __devinit psycho_find_sibling(u32 upa_portid) +{ + struct pci_pbm_info *pbm; - psycho_pbm_strbuf_init(pbm, is_pbm_a); + for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { + if (pbm->portid == upa_portid) + return pbm; + } + return NULL; } #define PSYCHO_CONFIGSPACE 0x001000000UL -void __init psycho_init(struct device_node *dp, char *model_name) +static int __devinit psycho_probe(struct of_device *op, + const struct of_device_id *match) { - struct linux_prom64_registers *pr_regs; - struct pci_controller_info *p; + const struct linux_prom64_registers *pr_regs; + struct device_node *dp = op->node; struct pci_pbm_info *pbm; struct iommu *iommu; - struct property *prop; + int is_pbm_a, err; u32 upa_portid; - int is_pbm_a; - upa_portid = 0xff; - prop = of_find_property(dp, "upa-portid", NULL); - if (prop) - upa_portid = *(u32 *) prop->value; + upa_portid = of_getintprop_default(dp, "upa-portid", 0xff); - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - struct pci_controller_info *p = pbm->parent; + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n"); + goto out_err; + } - if (p->pbm_A.portid == upa_portid) { - is_pbm_a = (p->pbm_A.prom_node == NULL); - psycho_pbm_init(p, dp, is_pbm_a); - return; + pbm->sibling = psycho_find_sibling(upa_portid); + if (pbm->sibling) { + iommu = pbm->sibling->iommu; + } else { + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n"); + goto out_free_controller; } } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + pbm->iommu = iommu; + pbm->portid = upa_portid; - p->pbm_A.iommu = p->pbm_B.iommu = iommu; + pr_regs = of_get_property(dp, "reg", NULL); + err = -ENODEV; + if (!pr_regs) { + printk(KERN_ERR PFX "No reg property.\n"); + goto out_free_iommu; + } - p->pbm_A.portid = upa_portid; - p->pbm_B.portid = upa_portid; + is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); - prop = of_find_property(dp, "reg", NULL); - pr_regs = prop->value; + pbm->controller_regs = pr_regs[2].phys_addr; + pbm->config_space = (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE); - p->pbm_A.controller_regs = pr_regs[2].phys_addr; - p->pbm_B.controller_regs = pr_regs[2].phys_addr; + if (is_pbm_a) { + pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_A; + pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_A; + pbm->pci_csr = pbm->controller_regs + PSYCHO_PCIA_CTRL; + } else { + pbm->pci_afsr = pbm->controller_regs + PSYCHO_PCI_AFSR_B; + pbm->pci_afar = pbm->controller_regs + PSYCHO_PCI_AFAR_B; + pbm->pci_csr = pbm->controller_regs + PSYCHO_PCIB_CTRL; + } - p->pbm_A.config_space = p->pbm_B.config_space = - (pr_regs[2].phys_addr + PSYCHO_CONFIGSPACE); + psycho_controller_hwinit(pbm); + if (!pbm->sibling) { + err = psycho_iommu_init(pbm, 128, 0xc0000000, + 0xffffffff, PSYCHO_CONTROL); + if (err) + goto out_free_iommu; - psycho_controller_hwinit(&p->pbm_A); + /* If necessary, hook us up for starfire IRQ translations. */ + if (this_is_starfire) + starfire_hookup(pbm->portid); + } - if (psycho_iommu_init(&p->pbm_A)) - goto fatal_memory_error; + psycho_pbm_init(pbm, op, is_pbm_a); - is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000); - psycho_pbm_init(p, dp, is_pbm_a); - return; + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; + + if (pbm->sibling) + pbm->sibling->sibling = pbm; + + dev_set_drvdata(&op->dev, pbm); + + return 0; + +out_free_iommu: + if (!pbm->sibling) + kfree(pbm->iommu); -fatal_memory_error: - prom_printf("PSYCHO: Fatal memory allocation error.\n"); - prom_halt(); +out_free_controller: + kfree(pbm); + +out_err: + return err; } + +static struct of_device_id __initdata psycho_match[] = { + { + .name = "pci", + .compatible = "pci108e,8000", + }, + {}, +}; + +static struct of_platform_driver psycho_driver = { + .name = DRIVER_NAME, + .match_table = psycho_match, + .probe = psycho_probe, +}; + +static int __init psycho_init(void) +{ + return of_register_driver(&psycho_driver, &of_bus_type); +} + +subsys_initcall(psycho_init); diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index ade5184e75d..713257b6963 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -16,31 +16,15 @@ #include <asm/apb.h> #include <asm/iommu.h> #include <asm/irq.h> -#include <asm/smp.h> -#include <asm/oplib.h> #include <asm/prom.h> +#include <asm/upa.h> #include "pci_impl.h" #include "iommu_common.h" +#include "psycho_common.h" -/* All SABRE registers are 64-bits. The following accessor - * routines are how they are accessed. The REG parameter - * is a physical address. - */ -#define sabre_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define sabre_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") +#define DRIVER_NAME "sabre" +#define PFX DRIVER_NAME ": " /* SABRE PCI controller register offsets and definitions. */ #define SABRE_UE_AFSR 0x0030UL @@ -208,95 +192,6 @@ static int hummingbird_p; static struct pci_bus *sabre_root_bus; -/* SABRE error handling support. */ -static void sabre_check_iommu_error(struct pci_pbm_info *pbm, - unsigned long afsr, - unsigned long afar) -{ - struct iommu *iommu = pbm->iommu; - unsigned long iommu_tag[16]; - unsigned long iommu_data[16]; - unsigned long flags; - u64 control; - int i; - - spin_lock_irqsave(&iommu->lock, flags); - control = sabre_read(iommu->iommu_control); - if (control & SABRE_IOMMUCTRL_ERR) { - char *type_string; - - /* Clear the error encountered bit. - * NOTE: On Sabre this is write 1 to clear, - * which is different from Psycho. - */ - sabre_write(iommu->iommu_control, control); - switch((control & SABRE_IOMMUCTRL_ERRSTS) >> 25UL) { - case 1: - type_string = "Invalid Error"; - break; - case 3: - type_string = "ECC Error"; - break; - default: - type_string = "Unknown"; - break; - }; - printk("%s: IOMMU Error, type[%s]\n", - pbm->name, type_string); - - /* Enter diagnostic mode and probe for error'd - * entries in the IOTLB. - */ - control &= ~(SABRE_IOMMUCTRL_ERRSTS | SABRE_IOMMUCTRL_ERR); - sabre_write(iommu->iommu_control, - (control | SABRE_IOMMUCTRL_DENAB)); - for (i = 0; i < 16; i++) { - unsigned long base = pbm->controller_regs; - - iommu_tag[i] = - sabre_read(base + SABRE_IOMMU_TAG + (i * 8UL)); - iommu_data[i] = - sabre_read(base + SABRE_IOMMU_DATA + (i * 8UL)); - sabre_write(base + SABRE_IOMMU_TAG + (i * 8UL), 0); - sabre_write(base + SABRE_IOMMU_DATA + (i * 8UL), 0); - } - sabre_write(iommu->iommu_control, control); - - for (i = 0; i < 16; i++) { - unsigned long tag, data; - - tag = iommu_tag[i]; - if (!(tag & SABRE_IOMMUTAG_ERR)) - continue; - - data = iommu_data[i]; - switch((tag & SABRE_IOMMUTAG_ERRSTS) >> 23UL) { - case 1: - type_string = "Invalid Error"; - break; - case 3: - type_string = "ECC Error"; - break; - default: - type_string = "Unknown"; - break; - }; - printk("%s: IOMMU TAG(%d)[RAW(%016lx)error(%s)wr(%d)sz(%dK)vpg(%08lx)]\n", - pbm->name, i, tag, type_string, - ((tag & SABRE_IOMMUTAG_WRITE) ? 1 : 0), - ((tag & SABRE_IOMMUTAG_SIZE) ? 64 : 8), - ((tag & SABRE_IOMMUTAG_VPN) << IOMMU_PAGE_SHIFT)); - printk("%s: IOMMU DATA(%d)[RAW(%016lx)valid(%d)used(%d)cache(%d)ppg(%016lx)\n", - pbm->name, i, data, - ((data & SABRE_IOMMUDATA_VALID) ? 1 : 0), - ((data & SABRE_IOMMUDATA_USED) ? 1 : 0), - ((data & SABRE_IOMMUDATA_CACHE) ? 1 : 0), - ((data & SABRE_IOMMUDATA_PPN) << IOMMU_PAGE_SHIFT)); - } - } - spin_unlock_irqrestore(&iommu->lock, flags); -} - static irqreturn_t sabre_ue_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; @@ -306,8 +201,8 @@ static irqreturn_t sabre_ue_intr(int irq, void *dev_id) int reported; /* Latch uncorrectable error status. */ - afar = sabre_read(afar_reg); - afsr = sabre_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear the primary/secondary error status bits. */ error_bits = afsr & @@ -316,7 +211,7 @@ static irqreturn_t sabre_ue_intr(int irq, void *dev_id) SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE); if (!error_bits) return IRQ_NONE; - sabre_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Uncorrectable Error, primary error type[%s%s]\n", @@ -352,7 +247,7 @@ static irqreturn_t sabre_ue_intr(int irq, void *dev_id) printk("]\n"); /* Interrogate IOMMU for error status. */ - sabre_check_iommu_error(pbm, afsr, afar); + psycho_check_iommu_error(pbm, afsr, afar, UE_ERR); return IRQ_HANDLED; } @@ -366,8 +261,8 @@ static irqreturn_t sabre_ce_intr(int irq, void *dev_id) int reported; /* Latch error status. */ - afar = sabre_read(afar_reg); - afsr = sabre_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear primary/secondary error status bits. */ error_bits = afsr & @@ -375,7 +270,7 @@ static irqreturn_t sabre_ce_intr(int irq, void *dev_id) SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR); if (!error_bits) return IRQ_NONE; - sabre_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Correctable Error, primary error type[%s]\n", @@ -413,136 +308,9 @@ static irqreturn_t sabre_ce_intr(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t sabre_pcierr_intr_other(struct pci_pbm_info *pbm) -{ - unsigned long csr_reg, csr, csr_error_bits; - irqreturn_t ret = IRQ_NONE; - u16 stat; - - csr_reg = pbm->controller_regs + SABRE_PCICTRL; - csr = sabre_read(csr_reg); - csr_error_bits = - csr & SABRE_PCICTRL_SERR; - if (csr_error_bits) { - /* Clear the errors. */ - sabre_write(csr_reg, csr); - - /* Log 'em. */ - if (csr_error_bits & SABRE_PCICTRL_SERR) - printk("%s: PCI SERR signal asserted.\n", - pbm->name); - ret = IRQ_HANDLED; - } - pci_bus_read_config_word(sabre_root_bus, 0, - PCI_STATUS, &stat); - if (stat & (PCI_STATUS_PARITY | - PCI_STATUS_SIG_TARGET_ABORT | - PCI_STATUS_REC_TARGET_ABORT | - PCI_STATUS_REC_MASTER_ABORT | - PCI_STATUS_SIG_SYSTEM_ERROR)) { - printk("%s: PCI bus error, PCI_STATUS[%04x]\n", - pbm->name, stat); - pci_bus_write_config_word(sabre_root_bus, 0, - PCI_STATUS, 0xffff); - ret = IRQ_HANDLED; - } - return ret; -} - -static irqreturn_t sabre_pcierr_intr(int irq, void *dev_id) -{ - struct pci_pbm_info *pbm = dev_id; - unsigned long afsr_reg, afar_reg; - unsigned long afsr, afar, error_bits; - int reported; - - afsr_reg = pbm->controller_regs + SABRE_PIOAFSR; - afar_reg = pbm->controller_regs + SABRE_PIOAFAR; - - /* Latch error status. */ - afar = sabre_read(afar_reg); - afsr = sabre_read(afsr_reg); - - /* Clear primary/secondary error status bits. */ - error_bits = afsr & - (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_PTA | - SABRE_PIOAFSR_PRTRY | SABRE_PIOAFSR_PPERR | - SABRE_PIOAFSR_SMA | SABRE_PIOAFSR_STA | - SABRE_PIOAFSR_SRTRY | SABRE_PIOAFSR_SPERR); - if (!error_bits) - return sabre_pcierr_intr_other(pbm); - sabre_write(afsr_reg, error_bits); - - /* Log the error. */ - printk("%s: PCI Error, primary error type[%s]\n", - pbm->name, - (((error_bits & SABRE_PIOAFSR_PMA) ? - "Master Abort" : - ((error_bits & SABRE_PIOAFSR_PTA) ? - "Target Abort" : - ((error_bits & SABRE_PIOAFSR_PRTRY) ? - "Excessive Retries" : - ((error_bits & SABRE_PIOAFSR_PPERR) ? - "Parity Error" : "???")))))); - printk("%s: bytemask[%04lx] was_block(%d)\n", - pbm->name, - (afsr & SABRE_PIOAFSR_BMSK) >> 32UL, - (afsr & SABRE_PIOAFSR_BLK) ? 1 : 0); - printk("%s: PCI AFAR [%016lx]\n", pbm->name, afar); - printk("%s: PCI Secondary errors [", pbm->name); - reported = 0; - if (afsr & SABRE_PIOAFSR_SMA) { - reported++; - printk("(Master Abort)"); - } - if (afsr & SABRE_PIOAFSR_STA) { - reported++; - printk("(Target Abort)"); - } - if (afsr & SABRE_PIOAFSR_SRTRY) { - reported++; - printk("(Excessive Retries)"); - } - if (afsr & SABRE_PIOAFSR_SPERR) { - reported++; - printk("(Parity Error)"); - } - if (!reported) - printk("(none)"); - printk("]\n"); - - /* For the error types shown, scan both PCI buses for devices - * which have logged that error type. - */ - - /* If we see a Target Abort, this could be the result of an - * IOMMU translation error of some sort. It is extremely - * useful to log this information as usually it indicates - * a bug in the IOMMU support code or a PCI device driver. - */ - if (error_bits & (SABRE_PIOAFSR_PTA | SABRE_PIOAFSR_STA)) { - sabre_check_iommu_error(pbm, afsr, afar); - pci_scan_for_target_abort(pbm, pbm->pci_bus); - } - if (error_bits & (SABRE_PIOAFSR_PMA | SABRE_PIOAFSR_SMA)) - pci_scan_for_master_abort(pbm, pbm->pci_bus); - - /* For excessive retries, SABRE/PBM will abort the device - * and there is no way to specifically check for excessive - * retries in the config space status registers. So what - * we hope is that we'll catch it via the master/target - * abort events. - */ - - if (error_bits & (SABRE_PIOAFSR_PPERR | SABRE_PIOAFSR_SPERR)) - pci_scan_for_parity_error(pbm, pbm->pci_bus); - - return IRQ_HANDLED; -} - static void sabre_register_error_handlers(struct pci_pbm_info *pbm) { - struct device_node *dp = pbm->prom_node; + struct device_node *dp = pbm->op->node; struct of_device *op; unsigned long base = pbm->controller_regs; u64 tmp; @@ -568,33 +336,34 @@ static void sabre_register_error_handlers(struct pci_pbm_info *pbm) * registering the handler so that we don't get spurious * interrupts. */ - sabre_write(base + SABRE_UE_AFSR, - (SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | - SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | - SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE)); + upa_writeq((SABRE_UEAFSR_PDRD | SABRE_UEAFSR_PDWR | + SABRE_UEAFSR_SDRD | SABRE_UEAFSR_SDWR | + SABRE_UEAFSR_SDTE | SABRE_UEAFSR_PDTE), + base + SABRE_UE_AFSR); err = request_irq(op->irqs[1], sabre_ue_intr, 0, "SABRE_UE", pbm); if (err) printk(KERN_WARNING "%s: Couldn't register UE, err=%d.\n", pbm->name, err); - sabre_write(base + SABRE_CE_AFSR, - (SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | - SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR)); + upa_writeq((SABRE_CEAFSR_PDRD | SABRE_CEAFSR_PDWR | + SABRE_CEAFSR_SDRD | SABRE_CEAFSR_SDWR), + base + SABRE_CE_AFSR); + err = request_irq(op->irqs[2], sabre_ce_intr, 0, "SABRE_CE", pbm); if (err) printk(KERN_WARNING "%s: Couldn't register CE, err=%d.\n", pbm->name, err); - err = request_irq(op->irqs[0], sabre_pcierr_intr, 0, + err = request_irq(op->irqs[0], psycho_pcierr_intr, 0, "SABRE_PCIERR", pbm); if (err) printk(KERN_WARNING "%s: Couldn't register PCIERR, err=%d.\n", pbm->name, err); - tmp = sabre_read(base + SABRE_PCICTRL); + tmp = upa_readq(base + SABRE_PCICTRL); tmp |= SABRE_PCICTRL_ERREN; - sabre_write(base + SABRE_PCICTRL, tmp); + upa_writeq(tmp, base + SABRE_PCICTRL); } static void apb_init(struct pci_bus *sabre_bus) @@ -633,7 +402,8 @@ static void apb_init(struct pci_bus *sabre_bus) } } -static void __init sabre_scan_bus(struct pci_pbm_info *pbm) +static void __init sabre_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { static int once; @@ -656,12 +426,12 @@ static void __init sabre_scan_bus(struct pci_pbm_info *pbm) * to live at bus 0. */ if (once != 0) { - prom_printf("SABRE: Multiple controllers unsupported.\n"); - prom_halt(); + printk(KERN_ERR PFX "Multiple controllers unsupported.\n"); + return; } once++; - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); if (!pbm->pci_bus) return; @@ -672,133 +442,58 @@ static void __init sabre_scan_bus(struct pci_pbm_info *pbm) sabre_register_error_handlers(pbm); } -static int sabre_iommu_init(struct pci_pbm_info *pbm, - int tsbsize, unsigned long dvma_offset, - u32 dma_mask) -{ - struct iommu *iommu = pbm->iommu; - unsigned long i; - u64 control; - int err; - - /* Register addresses. */ - iommu->iommu_control = pbm->controller_regs + SABRE_IOMMU_CONTROL; - iommu->iommu_tsbbase = pbm->controller_regs + SABRE_IOMMU_TSBBASE; - iommu->iommu_flush = pbm->controller_regs + SABRE_IOMMU_FLUSH; - iommu->iommu_tags = iommu->iommu_flush + (0xa580UL - 0x0210UL); - iommu->write_complete_reg = pbm->controller_regs + SABRE_WRSYNC; - /* Sabre's IOMMU lacks ctx flushing. */ - iommu->iommu_ctxflush = 0; - - /* Invalidate TLB Entries. */ - control = sabre_read(pbm->controller_regs + SABRE_IOMMU_CONTROL); - control |= SABRE_IOMMUCTRL_DENAB; - sabre_write(pbm->controller_regs + SABRE_IOMMU_CONTROL, control); - - for(i = 0; i < 16; i++) { - sabre_write(pbm->controller_regs + SABRE_IOMMU_TAG + (i * 8UL), 0); - sabre_write(pbm->controller_regs + SABRE_IOMMU_DATA + (i * 8UL), 0); - } - - /* Leave diag mode enabled for full-flushing done - * in pci_iommu.c - */ - err = iommu_table_init(iommu, tsbsize * 1024 * 8, - dvma_offset, dma_mask, pbm->numa_node); - if (err) - return err; - - sabre_write(pbm->controller_regs + SABRE_IOMMU_TSBBASE, - __pa(iommu->page_table)); - - control = sabre_read(pbm->controller_regs + SABRE_IOMMU_CONTROL); - control &= ~(SABRE_IOMMUCTRL_TSBSZ | SABRE_IOMMUCTRL_TBWSZ); - control |= SABRE_IOMMUCTRL_ENAB; - switch(tsbsize) { - case 64: - control |= SABRE_IOMMU_TSBSZ_64K; - break; - case 128: - control |= SABRE_IOMMU_TSBSZ_128K; - break; - default: - prom_printf("iommu_init: Illegal TSB size %d\n", tsbsize); - prom_halt(); - break; - } - sabre_write(pbm->controller_regs + SABRE_IOMMU_CONTROL, control); - - return 0; -} - -static void __init sabre_pbm_init(struct pci_controller_info *p, - struct pci_pbm_info *pbm, struct device_node *dp) +static void __init sabre_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op) { - pbm->name = dp->full_name; - printk("%s: SABRE PCI Bus Module\n", pbm->name); - - pbm->numa_node = -1; - - pbm->scan_bus = sabre_scan_bus; - pbm->pci_ops = &sun4u_pci_ops; - pbm->config_space_reg_bits = 8; - - pbm->index = pci_num_pbms++; - - pbm->chip_type = PBM_CHIP_TYPE_SABRE; - pbm->parent = p; - pbm->prom_node = dp; - pci_get_pbm_props(pbm); - - pci_determine_mem_io_space(pbm); + psycho_pbm_init_common(pbm, op, "SABRE", PBM_CHIP_TYPE_SABRE); + pbm->pci_afsr = pbm->controller_regs + SABRE_PIOAFSR; + pbm->pci_afar = pbm->controller_regs + SABRE_PIOAFAR; + pbm->pci_csr = pbm->controller_regs + SABRE_PCICTRL; + sabre_scan_bus(pbm, &op->dev); } -void __init sabre_init(struct device_node *dp, char *model_name) +static int __devinit sabre_probe(struct of_device *op, + const struct of_device_id *match) { const struct linux_prom64_registers *pr_regs; - struct pci_controller_info *p; + struct device_node *dp = op->node; struct pci_pbm_info *pbm; + u32 upa_portid, dma_mask; struct iommu *iommu; - int tsbsize; + int tsbsize, err; const u32 *vdma; - u32 upa_portid, dma_mask; u64 clear_irq; - hummingbird_p = 0; - if (!strcmp(model_name, "pci108e,a001")) - hummingbird_p = 1; - else if (!strcmp(model_name, "SUNW,sabre")) { - const char *compat = of_get_property(dp, "compatible", NULL); - if (compat && !strcmp(compat, "pci108e,a001")) - hummingbird_p = 1; - if (!hummingbird_p) { - struct device_node *dp; - - /* Of course, Sun has to encode things a thousand - * different ways, inconsistently. - */ - for_each_node_by_type(dp, "cpu") { - if (!strcmp(dp->name, "SUNW,UltraSPARC-IIe")) - hummingbird_p = 1; - } + hummingbird_p = (match->data != NULL); + if (!hummingbird_p) { + struct device_node *cpu_dp; + + /* Of course, Sun has to encode things a thousand + * different ways, inconsistently. + */ + for_each_node_by_type(cpu_dp, "cpu") { + if (!strcmp(cpu_dp->name, "SUNW,UltraSPARC-IIe")) + hummingbird_p = 1; } } - p = kzalloc(sizeof(*p), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n"); + goto out_err; + } + + iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM iommu.\n"); + goto out_free_controller; + } - iommu = kzalloc(sizeof(*iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; - pbm = &p->pbm_A; pbm->iommu = iommu; upa_portid = of_getintprop_default(dp, "upa-portid", 0xff); - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; - pbm->portid = upa_portid; /* @@ -806,6 +501,11 @@ void __init sabre_init(struct device_node *dp, char *model_name) */ pr_regs = of_get_property(dp, "reg", NULL); + err = -ENODEV; + if (!pr_regs) { + printk(KERN_ERR PFX "No reg property\n"); + goto out_free_iommu; + } /* * First REG in property is base of entire SABRE register space. @@ -816,22 +516,25 @@ void __init sabre_init(struct device_node *dp, char *model_name) /* PCI first */ for (clear_irq = SABRE_ICLR_A_SLOT0; clear_irq < SABRE_ICLR_B_SLOT0 + 0x80; clear_irq += 8) - sabre_write(pbm->controller_regs + clear_irq, 0x0UL); + upa_writeq(0x0UL, pbm->controller_regs + clear_irq); /* Then OBIO */ for (clear_irq = SABRE_ICLR_SCSI; clear_irq < SABRE_ICLR_SCSI + 0x80; clear_irq += 8) - sabre_write(pbm->controller_regs + clear_irq, 0x0UL); + upa_writeq(0x0UL, pbm->controller_regs + clear_irq); /* Error interrupts are enabled later after the bus scan. */ - sabre_write(pbm->controller_regs + SABRE_PCICTRL, - (SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR | - SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN)); + upa_writeq((SABRE_PCICTRL_MRLEN | SABRE_PCICTRL_SERR | + SABRE_PCICTRL_ARBPARK | SABRE_PCICTRL_AEN), + pbm->controller_regs + SABRE_PCICTRL); /* Now map in PCI config space for entire SABRE. */ - pbm->config_space = - (pbm->controller_regs + SABRE_CONFIGSPACE); + pbm->config_space = pbm->controller_regs + SABRE_CONFIGSPACE; vdma = of_get_property(dp, "virtual-dma", NULL); + if (!vdma) { + printk(KERN_ERR PFX "No virtual-dma property\n"); + goto out_free_iommu; + } dma_mask = vdma[0]; switch(vdma[1]) { @@ -849,20 +552,58 @@ void __init sabre_init(struct device_node *dp, char *model_name) tsbsize = 128; break; default: - prom_printf("SABRE: strange virtual-dma size.\n"); - prom_halt(); + printk(KERN_ERR PFX "Strange virtual-dma size.\n"); + goto out_free_iommu; } - if (sabre_iommu_init(pbm, tsbsize, vdma[0], dma_mask)) - goto fatal_memory_error; + err = psycho_iommu_init(pbm, tsbsize, vdma[0], dma_mask, SABRE_WRSYNC); + if (err) + goto out_free_iommu; /* * Look for APB underneath. */ - sabre_pbm_init(p, pbm, dp); - return; + sabre_pbm_init(pbm, op); -fatal_memory_error: - prom_printf("SABRE: Fatal memory allocation error.\n"); - prom_halt(); + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; + + dev_set_drvdata(&op->dev, pbm); + + return 0; + +out_free_iommu: + kfree(pbm->iommu); + +out_free_controller: + kfree(pbm); + +out_err: + return err; +} + +static struct of_device_id __initdata sabre_match[] = { + { + .name = "pci", + .compatible = "pci108e,a001", + .data = (void *) 1, + }, + { + .name = "pci", + .compatible = "pci108e,a000", + }, + {}, +}; + +static struct of_platform_driver sabre_driver = { + .name = DRIVER_NAME, + .match_table = sabre_match, + .probe = sabre_probe, +}; + +static int __init sabre_init(void) +{ + return of_register_driver(&sabre_driver, &of_bus_type); } + +subsys_initcall(sabre_init); diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index 9248c6737f0..45d9dba1ba1 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1,6 +1,6 @@ /* pci_schizo.c: SCHIZO/TOMATILLO specific PCI controller support. * - * Copyright (C) 2001, 2002, 2003, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 2001, 2002, 2003, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> @@ -13,32 +13,15 @@ #include <asm/iommu.h> #include <asm/irq.h> -#include <asm/upa.h> #include <asm/pstate.h> #include <asm/prom.h> -#include <asm/oplib.h> +#include <asm/upa.h> #include "pci_impl.h" #include "iommu_common.h" -/* All SCHIZO registers are 64-bits. The following accessor - * routines are how they are accessed. The REG parameter - * is a physical address. - */ -#define schizo_read(__reg) \ -({ u64 __ret; \ - __asm__ __volatile__("ldxa [%1] %2, %0" \ - : "=r" (__ret) \ - : "r" (__reg), "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory"); \ - __ret; \ -}) -#define schizo_write(__reg, __val) \ - __asm__ __volatile__("stxa %0, [%1] %2" \ - : /* no outputs */ \ - : "r" (__val), "r" (__reg), \ - "i" (ASI_PHYS_BYPASS_EC_E) \ - : "memory") +#define DRIVER_NAME "schizo" +#define PFX DRIVER_NAME ": " /* This is a convention that at least Excalibur and Merlin * follow. I suppose the SCHIZO used in Starcat and friends @@ -163,25 +146,25 @@ static void __schizo_check_stc_error_pbm(struct pci_pbm_info *pbm, * invalidating it before it has a chance to reach * main memory. */ - control = schizo_read(strbuf->strbuf_control); - schizo_write(strbuf->strbuf_control, - (control | SCHIZO_STRBUF_CTRL_DENAB)); + control = upa_readq(strbuf->strbuf_control); + upa_writeq((control | SCHIZO_STRBUF_CTRL_DENAB), + strbuf->strbuf_control); for (i = 0; i < 128; i++) { unsigned long val; - val = schizo_read(err_base + (i * 8UL)); - schizo_write(err_base + (i * 8UL), 0UL); + val = upa_readq(err_base + (i * 8UL)); + upa_writeq(0UL, err_base + (i * 8UL)); stc_error_buf[i] = val; } for (i = 0; i < 16; i++) { - stc_tag_buf[i] = schizo_read(tag_base + (i * 8UL)); - stc_line_buf[i] = schizo_read(line_base + (i * 8UL)); - schizo_write(tag_base + (i * 8UL), 0UL); - schizo_write(line_base + (i * 8UL), 0UL); + stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL)); + stc_line_buf[i] = upa_readq(line_base + (i * 8UL)); + upa_writeq(0UL, tag_base + (i * 8UL)); + upa_writeq(0UL, line_base + (i * 8UL)); } /* OK, state is logged, exit diagnostic mode. */ - schizo_write(strbuf->strbuf_control, control); + upa_writeq(control, strbuf->strbuf_control); for (i = 0; i < 16; i++) { int j, saw_error, first, last; @@ -258,14 +241,14 @@ static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, int i; spin_lock_irqsave(&iommu->lock, flags); - control = schizo_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); if (control & SCHIZO_IOMMU_CTRL_XLTEERR) { unsigned long base; char *type_string; /* Clear the error encountered bit. */ control &= ~SCHIZO_IOMMU_CTRL_XLTEERR; - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); switch((control & SCHIZO_IOMMU_CTRL_XLTESTAT) >> 25UL) { case 0: @@ -295,24 +278,24 @@ static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, * get as much diagnostic information to the * console as we can. */ - schizo_write(iommu->iommu_control, - control | SCHIZO_IOMMU_CTRL_DENAB); + upa_writeq(control | SCHIZO_IOMMU_CTRL_DENAB, + iommu->iommu_control); base = pbm->pbm_regs; for (i = 0; i < 16; i++) { iommu_tag[i] = - schizo_read(base + SCHIZO_IOMMU_TAG + (i * 8UL)); + upa_readq(base + SCHIZO_IOMMU_TAG + (i * 8UL)); iommu_data[i] = - schizo_read(base + SCHIZO_IOMMU_DATA + (i * 8UL)); + upa_readq(base + SCHIZO_IOMMU_DATA + (i * 8UL)); /* Now clear out the entry. */ - schizo_write(base + SCHIZO_IOMMU_TAG + (i * 8UL), 0); - schizo_write(base + SCHIZO_IOMMU_DATA + (i * 8UL), 0); + upa_writeq(0, base + SCHIZO_IOMMU_TAG + (i * 8UL)); + upa_writeq(0, base + SCHIZO_IOMMU_DATA + (i * 8UL)); } /* Leave diagnostic mode. */ - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); for (i = 0; i < 16; i++) { unsigned long tag, data; @@ -357,11 +340,12 @@ static void schizo_check_iommu_error_pbm(struct pci_pbm_info *pbm, spin_unlock_irqrestore(&iommu->lock, flags); } -static void schizo_check_iommu_error(struct pci_controller_info *p, +static void schizo_check_iommu_error(struct pci_pbm_info *pbm, enum schizo_error_type type) { - schizo_check_iommu_error_pbm(&p->pbm_A, type); - schizo_check_iommu_error_pbm(&p->pbm_B, type); + schizo_check_iommu_error_pbm(pbm, type); + if (pbm->sibling) + schizo_check_iommu_error_pbm(pbm->sibling, type); } /* Uncorrectable ECC error status gathering. */ @@ -386,14 +370,13 @@ static void schizo_check_iommu_error(struct pci_controller_info *p, static irqreturn_t schizo_ue_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; unsigned long afsr_reg = pbm->controller_regs + SCHIZO_UE_AFSR; unsigned long afar_reg = pbm->controller_regs + SCHIZO_UE_AFAR; unsigned long afsr, afar, error_bits; int reported, limit; /* Latch uncorrectable error status. */ - afar = schizo_read(afar_reg); + afar = upa_readq(afar_reg); /* If either of the error pending bits are set in the * AFSR, the error status is being actively updated by @@ -401,7 +384,7 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id) */ limit = 1000; do { - afsr = schizo_read(afsr_reg); + afsr = upa_readq(afsr_reg); } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit); /* Clear the primary/secondary error status bits. */ @@ -410,7 +393,7 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id) SCHIZO_UEAFSR_SPIO | SCHIZO_UEAFSR_SDMA); if (!error_bits) return IRQ_NONE; - schizo_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Uncorrectable Error, primary error type[%s]\n", @@ -449,7 +432,7 @@ static irqreturn_t schizo_ue_intr(int irq, void *dev_id) printk("]\n"); /* Interrogate IOMMU for error status. */ - schizo_check_iommu_error(p, UE_ERR); + schizo_check_iommu_error(pbm, UE_ERR); return IRQ_HANDLED; } @@ -481,7 +464,7 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id) int reported, limit; /* Latch error status. */ - afar = schizo_read(afar_reg); + afar = upa_readq(afar_reg); /* If either of the error pending bits are set in the * AFSR, the error status is being actively updated by @@ -489,7 +472,7 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id) */ limit = 1000; do { - afsr = schizo_read(afsr_reg); + afsr = upa_readq(afsr_reg); } while ((afsr & SCHIZO_UEAFSR_ERRPNDG) != 0 && --limit); /* Clear primary/secondary error status bits. */ @@ -498,7 +481,7 @@ static irqreturn_t schizo_ce_intr(int irq, void *dev_id) SCHIZO_CEAFSR_SPIO | SCHIZO_CEAFSR_SDMA); if (!error_bits) return IRQ_NONE; - schizo_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: Correctable Error, primary error type[%s]\n", @@ -600,7 +583,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm) u16 stat; csr_reg = pbm->pbm_regs + SCHIZO_PCI_CTRL; - csr = schizo_read(csr_reg); + csr = upa_readq(csr_reg); csr_error_bits = csr & (SCHIZO_PCICTRL_BUS_UNUS | SCHIZO_PCICTRL_TTO_ERR | @@ -610,7 +593,7 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm) SCHIZO_PCICTRL_SERR); if (csr_error_bits) { /* Clear the errors. */ - schizo_write(csr_reg, csr); + upa_writeq(csr, csr_reg); /* Log 'em. */ if (csr_error_bits & SCHIZO_PCICTRL_BUS_UNUS) @@ -650,7 +633,6 @@ static irqreturn_t schizo_pcierr_intr_other(struct pci_pbm_info *pbm) static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; unsigned long afsr_reg, afar_reg, base; unsigned long afsr, afar, error_bits; int reported; @@ -661,8 +643,8 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) afar_reg = base + SCHIZO_PCI_AFAR; /* Latch error status. */ - afar = schizo_read(afar_reg); - afsr = schizo_read(afsr_reg); + afar = upa_readq(afar_reg); + afsr = upa_readq(afsr_reg); /* Clear primary/secondary error status bits. */ error_bits = afsr & @@ -674,7 +656,7 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS); if (!error_bits) return schizo_pcierr_intr_other(pbm); - schizo_write(afsr_reg, error_bits); + upa_writeq(error_bits, afsr_reg); /* Log the error. */ printk("%s: PCI Error, primary error type[%s]\n", @@ -744,7 +726,7 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) * a bug in the IOMMU support code or a PCI device driver. */ if (error_bits & (SCHIZO_PCIAFSR_PTA | SCHIZO_PCIAFSR_STA)) { - schizo_check_iommu_error(p, PCI_ERR); + schizo_check_iommu_error(pbm, PCI_ERR); pci_scan_for_target_abort(pbm, pbm->pci_bus); } if (error_bits & (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_SMA)) @@ -805,12 +787,11 @@ static irqreturn_t schizo_pcierr_intr(int irq, void *dev_id) static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id) { struct pci_pbm_info *pbm = dev_id; - struct pci_controller_info *p = pbm->parent; u64 errlog; - errlog = schizo_read(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG); - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG, - errlog & ~(SAFARI_ERRLOG_ERROUT)); + errlog = upa_readq(pbm->controller_regs + SCHIZO_SAFARI_ERRLOG); + upa_writeq(errlog & ~(SAFARI_ERRLOG_ERROUT), + pbm->controller_regs + SCHIZO_SAFARI_ERRLOG); if (!(errlog & BUS_ERROR_UNMAP)) { printk("%s: Unexpected Safari/JBUS error interrupt, errlog[%016lx]\n", @@ -821,7 +802,7 @@ static irqreturn_t schizo_safarierr_intr(int irq, void *dev_id) printk("%s: Safari/JBUS interrupt, UNMAPPED error, interrogating IOMMUs.\n", pbm->name); - schizo_check_iommu_error(p, SAFARI_ERR); + schizo_check_iommu_error(pbm, SAFARI_ERR); return IRQ_HANDLED; } @@ -863,7 +844,7 @@ static int pbm_routes_this_ino(struct pci_pbm_info *pbm, u32 ino) */ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) { - struct of_device *op = of_find_device_by_node(pbm->prom_node); + struct of_device *op = of_find_device_by_node(pbm->op->node); u64 tmp, err_mask, err_no_mask; int err; @@ -910,10 +891,9 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) } /* Enable UE and CE interrupts for controller. */ - schizo_write(pbm->controller_regs + SCHIZO_ECC_CTRL, - (SCHIZO_ECCCTRL_EE | - SCHIZO_ECCCTRL_UE | - SCHIZO_ECCCTRL_CE)); + upa_writeq((SCHIZO_ECCCTRL_EE | + SCHIZO_ECCCTRL_UE | + SCHIZO_ECCCTRL_CE), pbm->controller_regs + SCHIZO_ECC_CTRL); /* Enable PCI Error interrupts and clear error * bits. @@ -926,10 +906,10 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) err_no_mask = SCHIZO_PCICTRL_DTO_ERR; - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL); tmp |= err_mask; tmp &= ~err_no_mask; - schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL); err_mask = (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | @@ -938,7 +918,7 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | SCHIZO_PCIAFSR_STTO); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_AFSR, err_mask); + upa_writeq(err_mask, pbm->pbm_regs + SCHIZO_PCI_AFSR); err_mask = (BUS_ERROR_BADCMD | BUS_ERROR_SNOOP_GR | BUS_ERROR_SNOOP_PCI | BUS_ERROR_SNOOP_RD | @@ -950,16 +930,16 @@ static void tomatillo_register_error_handlers(struct pci_pbm_info *pbm) BUS_ERROR_APERR | BUS_ERROR_UNMAP | BUS_ERROR_BUSERR | BUS_ERROR_TIMEOUT); - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL, - (SCHIZO_SAFERRCTRL_EN | err_mask)); + upa_writeq((SCHIZO_SAFERRCTRL_EN | err_mask), + pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL); - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_IRQCTRL, - (SCHIZO_SAFIRQCTRL_EN | (BUS_ERROR_UNMAP))); + upa_writeq((SCHIZO_SAFIRQCTRL_EN | (BUS_ERROR_UNMAP)), + pbm->controller_regs + SCHIZO_SAFARI_IRQCTRL); } static void schizo_register_error_handlers(struct pci_pbm_info *pbm) { - struct of_device *op = of_find_device_by_node(pbm->prom_node); + struct of_device *op = of_find_device_by_node(pbm->op->node); u64 tmp, err_mask, err_no_mask; int err; @@ -1006,10 +986,9 @@ static void schizo_register_error_handlers(struct pci_pbm_info *pbm) } /* Enable UE and CE interrupts for controller. */ - schizo_write(pbm->controller_regs + SCHIZO_ECC_CTRL, - (SCHIZO_ECCCTRL_EE | - SCHIZO_ECCCTRL_UE | - SCHIZO_ECCCTRL_CE)); + upa_writeq((SCHIZO_ECCCTRL_EE | + SCHIZO_ECCCTRL_UE | + SCHIZO_ECCCTRL_CE), pbm->controller_regs + SCHIZO_ECC_CTRL); err_mask = (SCHIZO_PCICTRL_BUS_UNUS | SCHIZO_PCICTRL_ESLCK | @@ -1025,18 +1004,18 @@ static void schizo_register_error_handlers(struct pci_pbm_info *pbm) /* Enable PCI Error interrupts and clear error * bits for each PBM. */ - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL); tmp |= err_mask; tmp &= ~err_no_mask; - schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_AFSR, - (SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | - SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | - SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | - SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | - SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | - SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS)); + upa_writeq((SCHIZO_PCIAFSR_PMA | SCHIZO_PCIAFSR_PTA | + SCHIZO_PCIAFSR_PRTRY | SCHIZO_PCIAFSR_PPERR | + SCHIZO_PCIAFSR_PTTO | SCHIZO_PCIAFSR_PUNUS | + SCHIZO_PCIAFSR_SMA | SCHIZO_PCIAFSR_STA | + SCHIZO_PCIAFSR_SRTRY | SCHIZO_PCIAFSR_SPERR | + SCHIZO_PCIAFSR_STTO | SCHIZO_PCIAFSR_SUNUS), + pbm->pbm_regs + SCHIZO_PCI_AFSR); /* Make all Safari error conditions fatal except unmapped * errors which we make generate interrupts. @@ -1063,8 +1042,8 @@ static void schizo_register_error_handlers(struct pci_pbm_info *pbm) BUS_ERROR_CPU0PS | BUS_ERROR_CPU0PB); #endif - schizo_write(pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL, - (SCHIZO_SAFERRCTRL_EN | err_mask)); + upa_writeq((SCHIZO_SAFERRCTRL_EN | err_mask), + pbm->controller_regs + SCHIZO_SAFARI_ERRCTRL); } static void pbm_config_busmastering(struct pci_pbm_info *pbm) @@ -1084,14 +1063,15 @@ static void pbm_config_busmastering(struct pci_pbm_info *pbm) pci_config_write8(addr, 64); } -static void __init schizo_scan_bus(struct pci_pbm_info *pbm) +static void __devinit schizo_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { pbm_config_busmastering(pbm); pbm->is_66mhz_capable = - (of_find_property(pbm->prom_node, "66mhz-capable", NULL) + (of_find_property(pbm->op->node, "66mhz-capable", NULL) != NULL); - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) tomatillo_register_error_handlers(pbm); @@ -1133,12 +1113,12 @@ static void schizo_pbm_strbuf_init(struct pci_pbm_info *pbm) * streaming buffer and leave the rerun-disable * setting however OBP set it. */ - control = schizo_read(pbm->stc.strbuf_control); + control = upa_readq(pbm->stc.strbuf_control); control &= ~(SCHIZO_STRBUF_CTRL_LPTR | SCHIZO_STRBUF_CTRL_LENAB | SCHIZO_STRBUF_CTRL_DENAB); control |= SCHIZO_STRBUF_CTRL_ENAB; - schizo_write(pbm->stc.strbuf_control, control); + upa_writeq(control, pbm->stc.strbuf_control); pbm->stc.strbuf_enabled = 1; } @@ -1150,24 +1130,17 @@ static void schizo_pbm_strbuf_init(struct pci_pbm_info *pbm) static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) { - struct iommu *iommu = pbm->iommu; + static const u32 vdma_default[] = { 0xc0000000, 0x40000000 }; unsigned long i, tagbase, database; - struct property *prop; - u32 vdma[2], dma_mask; + struct iommu *iommu = pbm->iommu; int tsbsize, err; + const u32 *vdma; + u32 dma_mask; u64 control; - prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); - if (prop) { - u32 *val = prop->value; - - vdma[0] = val[0]; - vdma[1] = val[1]; - } else { - /* No property, use default values. */ - vdma[0] = 0xc0000000; - vdma[1] = 0x40000000; - } + vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); + if (!vdma) + vdma = vdma_default; dma_mask = vdma[0]; switch (vdma[1]) { @@ -1187,9 +1160,9 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) break; default: - prom_printf("SCHIZO: strange virtual-dma size.\n"); - prom_halt(); - }; + printk(KERN_ERR PFX "Strange virtual-dma size.\n"); + return -EINVAL; + } /* Register addresses, SCHIZO has iommu ctx flushing. */ iommu->iommu_control = pbm->pbm_regs + SCHIZO_IOMMU_CONTROL; @@ -1206,15 +1179,15 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) /* * Invalidate TLB Entries. */ - control = schizo_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); control |= SCHIZO_IOMMU_CTRL_DENAB; - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); tagbase = SCHIZO_IOMMU_TAG, database = SCHIZO_IOMMU_DATA; - for(i = 0; i < 16; i++) { - schizo_write(pbm->pbm_regs + tagbase + (i * 8UL), 0); - schizo_write(pbm->pbm_regs + database + (i * 8UL), 0); + for (i = 0; i < 16; i++) { + upa_writeq(0, pbm->pbm_regs + tagbase + (i * 8UL)); + upa_writeq(0, pbm->pbm_regs + database + (i * 8UL)); } /* Leave diag mode enabled for full-flushing done @@ -1222,12 +1195,14 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) */ err = iommu_table_init(iommu, tsbsize * 8 * 1024, vdma[0], dma_mask, pbm->numa_node); - if (err) + if (err) { + printk(KERN_ERR PFX "iommu_table_init() fails with %d\n", err); return err; + } - schizo_write(iommu->iommu_tsbbase, __pa(iommu->page_table)); + upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase); - control = schizo_read(iommu->iommu_control); + control = upa_readq(iommu->iommu_control); control &= ~(SCHIZO_IOMMU_CTRL_TSBSZ | SCHIZO_IOMMU_CTRL_TBWSZ); switch (tsbsize) { case 64: @@ -1236,10 +1211,10 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) case 128: control |= SCHIZO_IOMMU_TSBSZ_128K; break; - }; + } control |= SCHIZO_IOMMU_CTRL_ENAB; - schizo_write(iommu->iommu_control, control); + upa_writeq(control, iommu->iommu_control); return 0; } @@ -1280,12 +1255,11 @@ static int schizo_pbm_iommu_init(struct pci_pbm_info *pbm) static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) { - struct property *prop; u64 tmp; - schizo_write(pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY, 5); + upa_writeq(5, pbm->pbm_regs + SCHIZO_PCI_IRQ_RETRY); - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_CTRL); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_CTRL); /* Enable arbiter for all PCI slots. */ tmp |= 0xff; @@ -1294,8 +1268,7 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) pbm->chip_version >= 0x2) tmp |= 0x3UL << SCHIZO_PCICTRL_PTO_SHIFT; - prop = of_find_property(pbm->prom_node, "no-bus-parking", NULL); - if (!prop) + if (!of_find_property(pbm->op->node, "no-bus-parking", NULL)) tmp |= SCHIZO_PCICTRL_PARK; else tmp &= ~SCHIZO_PCICTRL_PARK; @@ -1311,13 +1284,13 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) SCHIZO_PCICTRL_RDO_PREF | SCHIZO_PCICTRL_RDL_PREF); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_CTRL, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_CTRL); - tmp = schizo_read(pbm->pbm_regs + SCHIZO_PCI_DIAG); + tmp = upa_readq(pbm->pbm_regs + SCHIZO_PCI_DIAG); tmp &= ~(SCHIZO_PCIDIAG_D_RTRYARB | SCHIZO_PCIDIAG_D_RETRY | SCHIZO_PCIDIAG_D_INTSYNC); - schizo_write(pbm->pbm_regs + SCHIZO_PCI_DIAG, tmp); + upa_writeq(tmp, pbm->pbm_regs + SCHIZO_PCI_DIAG); if (pbm->chip_type == PBM_CHIP_TYPE_TOMATILLO) { /* Clear prefetch lengths to workaround a bug in @@ -1329,17 +1302,16 @@ static void schizo_pbm_hw_init(struct pci_pbm_info *pbm) TOMATILLO_IOC_RDONE_CPENAB | TOMATILLO_IOC_RDLINE_CPENAB); - schizo_write(pbm->pbm_regs + TOMATILLO_PCI_IOC_CSR, - tmp); + upa_writeq(tmp, pbm->pbm_regs + TOMATILLO_PCI_IOC_CSR); } } -static int __init schizo_pbm_init(struct pci_controller_info *p, - struct device_node *dp, u32 portid, - int chip_type) +static int __devinit schizo_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, u32 portid, + int chip_type) { const struct linux_prom64_registers *regs; - struct pci_pbm_info *pbm; + struct device_node *dp = op->node; const char *chipset_name; int is_pbm_a, err; @@ -1372,25 +1344,19 @@ static int __init schizo_pbm_init(struct pci_controller_info *p, regs = of_get_property(dp, "reg", NULL); is_pbm_a = ((regs[0].phys_addr & 0x00700000) == 0x00600000); - if (is_pbm_a) - pbm = &p->pbm_A; - else - pbm = &p->pbm_B; pbm->next = pci_pbm_root; pci_pbm_root = pbm; pbm->numa_node = -1; - pbm->scan_bus = schizo_scan_bus; pbm->pci_ops = &sun4u_pci_ops; pbm->config_space_reg_bits = 8; pbm->index = pci_num_pbms++; pbm->portid = portid; - pbm->parent = p; - pbm->prom_node = dp; + pbm->op = op; pbm->chip_type = chip_type; pbm->chip_version = of_getintprop_default(dp, "version#", 0); @@ -1420,6 +1386,8 @@ static int __init schizo_pbm_init(struct pci_controller_info *p, schizo_pbm_strbuf_init(pbm); + schizo_scan_bus(pbm, &op->dev); + return 0; } @@ -1433,62 +1401,104 @@ static inline int portid_compare(u32 x, u32 y, int chip_type) return (x == y); } -static void __init __schizo_init(struct device_node *dp, char *model_name, - int chip_type) +static struct pci_pbm_info * __devinit schizo_find_sibling(u32 portid, + int chip_type) { - struct pci_controller_info *p; + struct pci_pbm_info *pbm; + + for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { + if (portid_compare(pbm->portid, portid, chip_type)) + return pbm; + } + return NULL; +} + +static int __devinit __schizo_init(struct of_device *op, unsigned long chip_type) +{ + struct device_node *dp = op->node; struct pci_pbm_info *pbm; struct iommu *iommu; u32 portid; + int err; portid = of_getintprop_default(dp, "portid", 0xff); - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - if (portid_compare(pbm->portid, portid, chip_type)) { - if (schizo_pbm_init(pbm->parent, dp, - portid, chip_type)) - goto fatal_memory_error; - return; - } + err = -ENOMEM; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Cannot allocate pci_pbm_info.\n"); + goto out_err; + } + + pbm->sibling = schizo_find_sibling(portid, chip_type); + + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Cannot allocate PBM A iommu.\n"); + goto out_free_pbm; } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + pbm->iommu = iommu; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + if (schizo_pbm_init(pbm, op, portid, chip_type)) + goto out_free_iommu; - p->pbm_A.iommu = iommu; + if (pbm->sibling) + pbm->sibling->sibling = pbm; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + dev_set_drvdata(&op->dev, pbm); - p->pbm_B.iommu = iommu; + return 0; - if (schizo_pbm_init(p, dp, portid, chip_type)) - goto fatal_memory_error; +out_free_iommu: + kfree(pbm->iommu); - return; +out_free_pbm: + kfree(pbm); -fatal_memory_error: - prom_printf("SCHIZO: Fatal memory allocation error.\n"); - prom_halt(); +out_err: + return err; } -void __init schizo_init(struct device_node *dp, char *model_name) +static int __devinit schizo_probe(struct of_device *op, + const struct of_device_id *match) { - __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO); + return __schizo_init(op, (unsigned long) match->data); } -void __init schizo_plus_init(struct device_node *dp, char *model_name) -{ - __schizo_init(dp, model_name, PBM_CHIP_TYPE_SCHIZO_PLUS); -} +/* The ordering of this table is very important. Some Tomatillo + * nodes announce that they are compatible with both pci108e,a801 + * and pci108e,8001. So list the chips in reverse chronological + * order. + */ +static struct of_device_id __initdata schizo_match[] = { + { + .name = "pci", + .compatible = "pci108e,a801", + .data = (void *) PBM_CHIP_TYPE_TOMATILLO, + }, + { + .name = "pci", + .compatible = "pci108e,8002", + .data = (void *) PBM_CHIP_TYPE_SCHIZO_PLUS, + }, + { + .name = "pci", + .compatible = "pci108e,8001", + .data = (void *) PBM_CHIP_TYPE_SCHIZO, + }, + {}, +}; -void __init tomatillo_init(struct device_node *dp, char *model_name) +static struct of_platform_driver schizo_driver = { + .name = DRIVER_NAME, + .match_table = schizo_match, + .probe = schizo_probe, +}; + +static int __init schizo_init(void) { - __schizo_init(dp, model_name, PBM_CHIP_TYPE_TOMATILLO); + return of_register_driver(&schizo_driver, &of_bus_type); } + +subsys_initcall(schizo_init); diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index a104c80d319..e86c73ec167 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -13,12 +13,10 @@ #include <linux/irq.h> #include <linux/msi.h> #include <linux/log2.h> +#include <linux/of_device.h> #include <asm/iommu.h> #include <asm/irq.h> -#include <asm/upa.h> -#include <asm/pstate.h> -#include <asm/oplib.h> #include <asm/hypervisor.h> #include <asm/prom.h> @@ -27,6 +25,9 @@ #include "pci_sun4v.h" +#define DRIVER_NAME "pci_sun4v" +#define PFX DRIVER_NAME ": " + static unsigned long vpci_major = 1; static unsigned long vpci_minor = 1; @@ -41,6 +42,7 @@ struct iommu_batch { }; static DEFINE_PER_CPU(struct iommu_batch, iommu_batch); +static int iommu_batch_initialized; /* Interrupts must be disabled. */ static inline void iommu_batch_start(struct device *dev, unsigned long prot, unsigned long entry) @@ -542,15 +544,16 @@ static const struct dma_ops sun4v_dma_ops = { .sync_sg_for_cpu = dma_4v_sync_sg_for_cpu, }; -static void __init pci_sun4v_scan_bus(struct pci_pbm_info *pbm) +static void __init pci_sun4v_scan_bus(struct pci_pbm_info *pbm, + struct device *parent) { struct property *prop; struct device_node *dp; - dp = pbm->prom_node; + dp = pbm->op->node; prop = of_find_property(dp, "66mhz-capable", NULL); pbm->is_66mhz_capable = (prop != NULL); - pbm->pci_bus = pci_scan_one_pbm(pbm); + pbm->pci_bus = pci_scan_one_pbm(pbm, parent); /* XXX register error interrupt handlers XXX */ } @@ -583,29 +586,22 @@ static unsigned long __init probe_existing_entries(struct pci_pbm_info *pbm, return cnt; } -static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) +static int __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) { + static const u32 vdma_default[] = { 0x80000000, 0x80000000 }; struct iommu *iommu = pbm->iommu; - struct property *prop; unsigned long num_tsb_entries, sz, tsbsize; - u32 vdma[2], dma_mask, dma_offset; - - prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); - if (prop) { - u32 *val = prop->value; - - vdma[0] = val[0]; - vdma[1] = val[1]; - } else { - /* No property, use default values. */ - vdma[0] = 0x80000000; - vdma[1] = 0x80000000; - } + u32 dma_mask, dma_offset; + const u32 *vdma; + + vdma = of_get_property(pbm->op->node, "virtual-dma", NULL); + if (!vdma) + vdma = vdma_default; if ((vdma[0] | vdma[1]) & ~IO_PAGE_MASK) { - prom_printf("PCI-SUN4V: strange virtual-dma[%08x:%08x].\n", - vdma[0], vdma[1]); - prom_halt(); + printk(KERN_ERR PFX "Strange virtual-dma[%08x:%08x].\n", + vdma[0], vdma[1]); + return -EINVAL; }; dma_mask = (roundup_pow_of_two(vdma[1]) - 1UL); @@ -625,8 +621,8 @@ static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) sz = (sz + 7UL) & ~7UL; iommu->arena.map = kzalloc(sz, GFP_KERNEL); if (!iommu->arena.map) { - prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n"); - prom_halt(); + printk(KERN_ERR PFX "Error, kmalloc(arena.map) failed.\n"); + return -ENOMEM; } iommu->arena.limit = num_tsb_entries; @@ -634,6 +630,8 @@ static void __init pci_sun4v_iommu_init(struct pci_pbm_info *pbm) if (sz) printk("%s: Imported %lu TSB entries from OBP\n", pbm->name, sz); + + return 0; } #ifdef CONFIG_PCI_MSI @@ -890,29 +888,20 @@ static void pci_sun4v_msi_init(struct pci_pbm_info *pbm) } #endif /* !(CONFIG_PCI_MSI) */ -static void __init pci_sun4v_pbm_init(struct pci_controller_info *p, - struct device_node *dp, u32 devhandle) +static int __init pci_sun4v_pbm_init(struct pci_pbm_info *pbm, + struct of_device *op, u32 devhandle) { - struct pci_pbm_info *pbm; - - if (devhandle & 0x40) - pbm = &p->pbm_B; - else - pbm = &p->pbm_A; - - pbm->next = pci_pbm_root; - pci_pbm_root = pbm; + struct device_node *dp = op->node; + int err; pbm->numa_node = of_node_to_nid(dp); - pbm->scan_bus = pci_sun4v_scan_bus; pbm->pci_ops = &sun4v_pci_ops; pbm->config_space_reg_bits = 12; pbm->index = pci_num_pbms++; - pbm->parent = p; - pbm->prom_node = dp; + pbm->op = op; pbm->devhandle = devhandle; @@ -924,82 +913,120 @@ static void __init pci_sun4v_pbm_init(struct pci_controller_info *p, pci_determine_mem_io_space(pbm); pci_get_pbm_props(pbm); - pci_sun4v_iommu_init(pbm); + + err = pci_sun4v_iommu_init(pbm); + if (err) + return err; + pci_sun4v_msi_init(pbm); + + pci_sun4v_scan_bus(pbm, &op->dev); + + pbm->next = pci_pbm_root; + pci_pbm_root = pbm; + + return 0; } -void __init sun4v_pci_init(struct device_node *dp, char *model_name) +static int __devinit pci_sun4v_probe(struct of_device *op, + const struct of_device_id *match) { + const struct linux_prom64_registers *regs; static int hvapi_negotiated = 0; - struct pci_controller_info *p; struct pci_pbm_info *pbm; + struct device_node *dp; struct iommu *iommu; - struct property *prop; - struct linux_prom64_registers *regs; u32 devhandle; - int i; + int i, err; + + dp = op->node; if (!hvapi_negotiated++) { - int err = sun4v_hvapi_register(HV_GRP_PCI, - vpci_major, - &vpci_minor); + err = sun4v_hvapi_register(HV_GRP_PCI, + vpci_major, + &vpci_minor); if (err) { - prom_printf("SUN4V_PCI: Could not register hvapi, " - "err=%d\n", err); - prom_halt(); + printk(KERN_ERR PFX "Could not register hvapi, " + "err=%d\n", err); + return err; } - printk("SUN4V_PCI: Registered hvapi major[%lu] minor[%lu]\n", + printk(KERN_INFO PFX "Registered hvapi major[%lu] minor[%lu]\n", vpci_major, vpci_minor); dma_ops = &sun4v_dma_ops; } - prop = of_find_property(dp, "reg", NULL); - if (!prop) { - prom_printf("SUN4V_PCI: Could not find config registers\n"); - prom_halt(); + regs = of_get_property(dp, "reg", NULL); + err = -ENODEV; + if (!regs) { + printk(KERN_ERR PFX "Could not find config registers\n"); + goto out_err; } - regs = prop->value; - devhandle = (regs->phys_addr >> 32UL) & 0x0fffffff; - for (pbm = pci_pbm_root; pbm; pbm = pbm->next) { - if (pbm->devhandle == (devhandle ^ 0x40)) { - pci_sun4v_pbm_init(pbm->parent, dp, devhandle); - return; + err = -ENOMEM; + if (!iommu_batch_initialized) { + for_each_possible_cpu(i) { + unsigned long page = get_zeroed_page(GFP_KERNEL); + + if (!page) + goto out_err; + + per_cpu(iommu_batch, i).pglist = (u64 *) page; } + iommu_batch_initialized = 1; } - for_each_possible_cpu(i) { - unsigned long page = get_zeroed_page(GFP_ATOMIC); - - if (!page) - goto fatal_memory_error; + pbm = kzalloc(sizeof(*pbm), GFP_KERNEL); + if (!pbm) { + printk(KERN_ERR PFX "Could not allocate pci_pbm_info\n"); + goto out_err; + } - per_cpu(iommu_batch, i).pglist = (u64 *) page; + iommu = kzalloc(sizeof(struct iommu), GFP_KERNEL); + if (!iommu) { + printk(KERN_ERR PFX "Could not allocate pbm iommu\n"); + goto out_free_controller; } - p = kzalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; + pbm->iommu = iommu; - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; + err = pci_sun4v_pbm_init(pbm, op, devhandle); + if (err) + goto out_free_iommu; + + dev_set_drvdata(&op->dev, pbm); + + return 0; + +out_free_iommu: + kfree(pbm->iommu); - p->pbm_A.iommu = iommu; +out_free_controller: + kfree(pbm); - iommu = kzalloc(sizeof(struct iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; +out_err: + return err; +} - p->pbm_B.iommu = iommu; +static struct of_device_id __initdata pci_sun4v_match[] = { + { + .name = "pci", + .compatible = "SUNW,sun4v-pci", + }, + {}, +}; - pci_sun4v_pbm_init(p, dp, devhandle); - return; +static struct of_platform_driver pci_sun4v_driver = { + .name = DRIVER_NAME, + .match_table = pci_sun4v_match, + .probe = pci_sun4v_probe, +}; -fatal_memory_error: - prom_printf("SUN4V_PCI: Fatal memory allocation error.\n"); - prom_halt(); +static int __init pci_sun4v_init(void) +{ + return of_register_driver(&pci_sun4v_driver, &of_bus_type); } + +subsys_initcall(pci_sun4v_init); diff --git a/arch/sparc64/kernel/pci_sun4v_asm.S b/arch/sparc64/kernel/pci_sun4v_asm.S index ecb81f389b0..e606d46c681 100644 --- a/arch/sparc64/kernel/pci_sun4v_asm.S +++ b/arch/sparc64/kernel/pci_sun4v_asm.S @@ -1,8 +1,9 @@ /* pci_sun4v_asm: Hypervisor calls for PCI support. * - * Copyright (C) 2006 David S. Miller <davem@davemloft.net> + * Copyright (C) 2006, 2008 David S. Miller <davem@davemloft.net> */ +#include <linux/linkage.h> #include <asm/hypervisor.h> /* %o0: devhandle @@ -14,8 +15,7 @@ * returns %o0: -status if status was non-zero, else * %o0: num pages mapped */ - .globl pci_sun4v_iommu_map -pci_sun4v_iommu_map: +ENTRY(pci_sun4v_iommu_map) mov %o5, %g1 mov HV_FAST_PCI_IOMMU_MAP, %o5 ta HV_FAST_TRAP @@ -24,6 +24,7 @@ pci_sun4v_iommu_map: mov %o1, %o0 1: retl nop +ENDPROC(pci_sun4v_iommu_map) /* %o0: devhandle * %o1: tsbid @@ -31,12 +32,12 @@ pci_sun4v_iommu_map: * * returns %o0: num ttes demapped */ - .globl pci_sun4v_iommu_demap -pci_sun4v_iommu_demap: +ENTRY(pci_sun4v_iommu_demap) mov HV_FAST_PCI_IOMMU_DEMAP, %o5 ta HV_FAST_TRAP retl mov %o1, %o0 +ENDPROC(pci_sun4v_iommu_demap) /* %o0: devhandle * %o1: tsbid @@ -45,8 +46,7 @@ pci_sun4v_iommu_demap: * * returns %o0: status */ - .globl pci_sun4v_iommu_getmap -pci_sun4v_iommu_getmap: +ENTRY(pci_sun4v_iommu_getmap) mov %o2, %o4 mov HV_FAST_PCI_IOMMU_GETMAP, %o5 ta HV_FAST_TRAP @@ -54,6 +54,7 @@ pci_sun4v_iommu_getmap: stx %o2, [%o3] retl mov %o0, %o0 +ENDPROC(pci_sun4v_iommu_getmap) /* %o0: devhandle * %o1: pci_device @@ -65,14 +66,14 @@ pci_sun4v_iommu_getmap: * If there is an error, the data will be returned * as all 1's. */ - .globl pci_sun4v_config_get -pci_sun4v_config_get: +ENTRY(pci_sun4v_config_get) mov HV_FAST_PCI_CONFIG_GET, %o5 ta HV_FAST_TRAP brnz,a,pn %o1, 1f mov -1, %o2 1: retl mov %o2, %o0 +ENDPROC(pci_sun4v_config_get) /* %o0: devhandle * %o1: pci_device @@ -85,14 +86,14 @@ pci_sun4v_config_get: * status will be zero if the operation completed * successfully, else -1 if not */ - .globl pci_sun4v_config_put -pci_sun4v_config_put: +ENTRY(pci_sun4v_config_put) mov HV_FAST_PCI_CONFIG_PUT, %o5 ta HV_FAST_TRAP brnz,a,pn %o1, 1f mov -1, %o1 1: retl mov %o1, %o0 +ENDPROC(pci_sun4v_config_put) /* %o0: devhandle * %o1: msiqid @@ -104,12 +105,12 @@ pci_sun4v_config_put: * status will be zero if the operation completed * successfully, else -1 if not */ - .globl pci_sun4v_msiq_conf -pci_sun4v_msiq_conf: +ENTRY(pci_sun4v_msiq_conf) mov HV_FAST_PCI_MSIQ_CONF, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_conf) /* %o0: devhandle * %o1: msiqid @@ -118,8 +119,7 @@ pci_sun4v_msiq_conf: * * returns %o0: status */ - .globl pci_sun4v_msiq_info -pci_sun4v_msiq_info: +ENTRY(pci_sun4v_msiq_info) mov %o2, %o4 mov HV_FAST_PCI_MSIQ_INFO, %o5 ta HV_FAST_TRAP @@ -127,6 +127,7 @@ pci_sun4v_msiq_info: stx %o2, [%o3] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_info) /* %o0: devhandle * %o1: msiqid @@ -134,13 +135,13 @@ pci_sun4v_msiq_info: * * returns %o0: status */ - .globl pci_sun4v_msiq_getvalid -pci_sun4v_msiq_getvalid: +ENTRY(pci_sun4v_msiq_getvalid) mov HV_FAST_PCI_MSIQ_GETVALID, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_getvalid) /* %o0: devhandle * %o1: msiqid @@ -148,12 +149,12 @@ pci_sun4v_msiq_getvalid: * * returns %o0: status */ - .globl pci_sun4v_msiq_setvalid -pci_sun4v_msiq_setvalid: +ENTRY(pci_sun4v_msiq_setvalid) mov HV_FAST_PCI_MSIQ_SETVALID, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_setvalid) /* %o0: devhandle * %o1: msiqid @@ -161,13 +162,13 @@ pci_sun4v_msiq_setvalid: * * returns %o0: status */ - .globl pci_sun4v_msiq_getstate -pci_sun4v_msiq_getstate: +ENTRY(pci_sun4v_msiq_getstate) mov HV_FAST_PCI_MSIQ_GETSTATE, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_getstate) /* %o0: devhandle * %o1: msiqid @@ -175,12 +176,12 @@ pci_sun4v_msiq_getstate: * * returns %o0: status */ - .globl pci_sun4v_msiq_setstate -pci_sun4v_msiq_setstate: +ENTRY(pci_sun4v_msiq_setstate) mov HV_FAST_PCI_MSIQ_SETSTATE, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_setstate) /* %o0: devhandle * %o1: msiqid @@ -188,13 +189,13 @@ pci_sun4v_msiq_setstate: * * returns %o0: status */ - .globl pci_sun4v_msiq_gethead -pci_sun4v_msiq_gethead: +ENTRY(pci_sun4v_msiq_gethead) mov HV_FAST_PCI_MSIQ_GETHEAD, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_gethead) /* %o0: devhandle * %o1: msiqid @@ -202,12 +203,12 @@ pci_sun4v_msiq_gethead: * * returns %o0: status */ - .globl pci_sun4v_msiq_sethead -pci_sun4v_msiq_sethead: +ENTRY(pci_sun4v_msiq_sethead) mov HV_FAST_PCI_MSIQ_SETHEAD, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_sethead) /* %o0: devhandle * %o1: msiqid @@ -215,13 +216,13 @@ pci_sun4v_msiq_sethead: * * returns %o0: status */ - .globl pci_sun4v_msiq_gettail -pci_sun4v_msiq_gettail: +ENTRY(pci_sun4v_msiq_gettail) mov HV_FAST_PCI_MSIQ_GETTAIL, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msiq_gettail) /* %o0: devhandle * %o1: msinum @@ -229,13 +230,13 @@ pci_sun4v_msiq_gettail: * * returns %o0: status */ - .globl pci_sun4v_msi_getvalid -pci_sun4v_msi_getvalid: +ENTRY(pci_sun4v_msi_getvalid) mov HV_FAST_PCI_MSI_GETVALID, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_getvalid) /* %o0: devhandle * %o1: msinum @@ -243,12 +244,12 @@ pci_sun4v_msi_getvalid: * * returns %o0: status */ - .globl pci_sun4v_msi_setvalid -pci_sun4v_msi_setvalid: +ENTRY(pci_sun4v_msi_setvalid) mov HV_FAST_PCI_MSI_SETVALID, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_setvalid) /* %o0: devhandle * %o1: msinum @@ -256,13 +257,13 @@ pci_sun4v_msi_setvalid: * * returns %o0: status */ - .globl pci_sun4v_msi_getmsiq -pci_sun4v_msi_getmsiq: +ENTRY(pci_sun4v_msi_getmsiq) mov HV_FAST_PCI_MSI_GETMSIQ, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_getmsiq) /* %o0: devhandle * %o1: msinum @@ -271,12 +272,12 @@ pci_sun4v_msi_getmsiq: * * returns %o0: status */ - .globl pci_sun4v_msi_setmsiq -pci_sun4v_msi_setmsiq: +ENTRY(pci_sun4v_msi_setmsiq) mov HV_FAST_PCI_MSI_SETMSIQ, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_setmsiq) /* %o0: devhandle * %o1: msinum @@ -284,13 +285,13 @@ pci_sun4v_msi_setmsiq: * * returns %o0: status */ - .globl pci_sun4v_msi_getstate -pci_sun4v_msi_getstate: +ENTRY(pci_sun4v_msi_getstate) mov HV_FAST_PCI_MSI_GETSTATE, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_getstate) /* %o0: devhandle * %o1: msinum @@ -298,12 +299,12 @@ pci_sun4v_msi_getstate: * * returns %o0: status */ - .globl pci_sun4v_msi_setstate -pci_sun4v_msi_setstate: +ENTRY(pci_sun4v_msi_setstate) mov HV_FAST_PCI_MSI_SETSTATE, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msi_setstate) /* %o0: devhandle * %o1: msinum @@ -311,13 +312,13 @@ pci_sun4v_msi_setstate: * * returns %o0: status */ - .globl pci_sun4v_msg_getmsiq -pci_sun4v_msg_getmsiq: +ENTRY(pci_sun4v_msg_getmsiq) mov HV_FAST_PCI_MSG_GETMSIQ, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_getmsiq) /* %o0: devhandle * %o1: msinum @@ -325,12 +326,12 @@ pci_sun4v_msg_getmsiq: * * returns %o0: status */ - .globl pci_sun4v_msg_setmsiq -pci_sun4v_msg_setmsiq: +ENTRY(pci_sun4v_msg_setmsiq) mov HV_FAST_PCI_MSG_SETMSIQ, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_setmsiq) /* %o0: devhandle * %o1: msinum @@ -338,13 +339,13 @@ pci_sun4v_msg_setmsiq: * * returns %o0: status */ - .globl pci_sun4v_msg_getvalid -pci_sun4v_msg_getvalid: +ENTRY(pci_sun4v_msg_getvalid) mov HV_FAST_PCI_MSG_GETVALID, %o5 ta HV_FAST_TRAP stx %o1, [%o2] retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_getvalid) /* %o0: devhandle * %o1: msinum @@ -352,10 +353,10 @@ pci_sun4v_msg_getvalid: * * returns %o0: status */ - .globl pci_sun4v_msg_setvalid -pci_sun4v_msg_setvalid: +ENTRY(pci_sun4v_msg_setvalid) mov HV_FAST_PCI_MSG_SETVALID, %o5 ta HV_FAST_TRAP retl mov %o0, %o0 +ENDPROC(pci_sun4v_msg_setvalid) diff --git a/arch/sparc64/kernel/power.c b/arch/sparc64/kernel/power.c index 3bb987a6d03..076cad7f975 100644 --- a/arch/sparc64/kernel/power.c +++ b/arch/sparc64/kernel/power.c @@ -1,34 +1,17 @@ /* power.c: Power management driver. * - * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 1999, 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> -#include <linux/sched.h> -#include <linux/signal.h> -#include <linux/delay.h> #include <linux/interrupt.h> -#include <linux/pm.h> -#include <linux/syscalls.h> #include <linux/reboot.h> #include <linux/of_device.h> -#include <asm/system.h> -#include <asm/auxio.h> #include <asm/prom.h> #include <asm/io.h> -#include <asm/sstate.h> -#include <asm/reboot.h> - -#include <linux/unistd.h> - -/* - * sysctl - toggle power-off restriction for serial console - * systems in machine_power_off() - */ -int scons_pwroff = 1; static void __iomem *power_reg; @@ -40,31 +23,6 @@ static irqreturn_t power_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static void (*poweroff_method)(void) = machine_alt_power_off; - -void machine_power_off(void) -{ - sstate_poweroff(); - if (strcmp(of_console_device->type, "serial") || scons_pwroff) { - if (power_reg) { - /* Both register bits seem to have the - * same effect, so until I figure out - * what the difference is... - */ - writel(AUXIO_PCIO_CPWR_OFF | AUXIO_PCIO_SPWR_OFF, power_reg); - } else { - if (poweroff_method != NULL) { - poweroff_method(); - /* not reached */ - } - } - } - machine_halt(); -} - -void (*pm_power_off)(void) = machine_power_off; -EXPORT_SYMBOL(pm_power_off); - static int __init has_button_interrupt(unsigned int irq, struct device_node *dp) { if (irq == 0xffffffff) @@ -85,8 +43,6 @@ static int __devinit power_probe(struct of_device *op, const struct of_device_id printk(KERN_INFO "%s: Control reg at %lx\n", op->node->name, res->start); - poweroff_method = machine_halt; /* able to use the standard halt */ - if (has_button_interrupt(irq, op->node)) { if (request_irq(irq, power_handler, 0, "power", NULL) < 0) @@ -96,7 +52,7 @@ static int __devinit power_probe(struct of_device *op, const struct of_device_id return 0; } -static struct of_device_id power_match[] = { +static struct of_device_id __initdata power_match[] = { { .name = "power", }, @@ -111,8 +67,9 @@ static struct of_platform_driver power_driver = { }, }; -void __init power_init(void) +static int __init power_init(void) { - of_register_driver(&power_driver, &of_platform_bus_type); - return; + return of_register_driver(&power_driver, &of_platform_bus_type); } + +device_initcall(power_init); diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 15f4178592e..d5e2acef987 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -22,7 +22,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/user.h> -#include <linux/reboot.h> #include <linux/delay.h> #include <linux/compat.h> #include <linux/tick.h> @@ -31,7 +30,6 @@ #include <linux/elfcore.h> #include <linux/sysrq.h> -#include <asm/oplib.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/page.h> @@ -46,8 +44,6 @@ #include <asm/mmu_context.h> #include <asm/unistd.h> #include <asm/hypervisor.h> -#include <asm/sstate.h> -#include <asm/reboot.h> #include <asm/syscalls.h> #include <asm/irq_regs.h> #include <asm/smp.h> @@ -115,35 +111,6 @@ void cpu_idle(void) } } -void machine_halt(void) -{ - sstate_halt(); - prom_halt(); - panic("Halt failed!"); -} - -void machine_alt_power_off(void) -{ - sstate_poweroff(); - prom_halt_power_off(); - panic("Power-off failed!"); -} - -void machine_restart(char * cmd) -{ - char *p; - - sstate_reboot(); - p = strchr (reboot_command, '\n'); - if (p) *p = 0; - if (cmd) - prom_reboot(cmd); - if (*reboot_command) - prom_reboot(reboot_command); - prom_reboot(""); - panic("Reboot failed!"); -} - #ifdef CONFIG_COMPAT static void show_regwindow32(struct pt_regs *regs) { @@ -248,7 +215,6 @@ static void __global_reg_self(struct thread_info *tp, struct pt_regs *regs, global_reg_snapshot[this_cpu].o7 = regs->u_regs[UREG_I7]; if (regs->tstate & TSTATE_PRIV) { - struct thread_info *tp = current_thread_info(); struct reg_window *rw; rw = (struct reg_window *) @@ -304,7 +270,6 @@ void __trigger_all_cpu_backtrace(void) for_each_online_cpu(cpu) { struct global_reg_snapshot *gp = &global_reg_snapshot[cpu]; - struct thread_info *tp; __global_reg_poll(gp); diff --git a/arch/sparc64/kernel/prom.c b/arch/sparc64/kernel/prom.c index 7151513f156..dbba82f9b14 100644 --- a/arch/sparc64/kernel/prom.c +++ b/arch/sparc64/kernel/prom.c @@ -38,7 +38,7 @@ struct device_node *of_find_node_by_phandle(phandle handle) { struct device_node *np; - for (np = allnodes; np != 0; np = np->allnext) + for (np = allnodes; np; np = np->allnext) if (np->node == handle) break; @@ -59,6 +59,9 @@ int of_getintprop_default(struct device_node *np, const char *name, int def) } EXPORT_SYMBOL(of_getintprop_default); +DEFINE_MUTEX(of_set_property_mutex); +EXPORT_SYMBOL(of_set_property_mutex); + int of_set_property(struct device_node *dp, const char *name, void *val, int len) { struct property **prevp; @@ -82,7 +85,10 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len void *old_val = prop->value; int ret; + mutex_lock(&of_set_property_mutex); ret = prom_setprop(dp->node, name, val, len); + mutex_unlock(&of_set_property_mutex); + err = -EINVAL; if (ret >= 0) { prop->value = new_val; @@ -945,22 +951,30 @@ static void __init irq_trans_init(struct device_node *dp) for (i = 0; i < ARRAY_SIZE(pci_irq_trans_table); i++) { struct irq_trans *t = &pci_irq_trans_table[i]; - if (!strcmp(model, t->name)) - return t->init(dp); + if (!strcmp(model, t->name)) { + t->init(dp); + return; + } } } #endif #ifdef CONFIG_SBUS if (!strcmp(dp->name, "sbus") || - !strcmp(dp->name, "sbi")) - return sbus_irq_trans_init(dp); + !strcmp(dp->name, "sbi")) { + sbus_irq_trans_init(dp); + return; + } #endif if (!strcmp(dp->name, "fhc") && - !strcmp(dp->parent->name, "central")) - return central_irq_trans_init(dp); + !strcmp(dp->parent->name, "central")) { + central_irq_trans_init(dp); + return; + } if (!strcmp(dp->name, "virtual-devices") || - !strcmp(dp->name, "niu")) - return sun4v_vdev_irq_trans_init(dp); + !strcmp(dp->name, "niu")) { + sun4v_vdev_irq_trans_init(dp); + return; + } } static int is_root_node(const struct device_node *dp) @@ -1231,32 +1245,49 @@ static void __init __build_path_component(struct device_node *dp, char *tmp_buf) if (parent != NULL) { if (!strcmp(parent->type, "pci") || - !strcmp(parent->type, "pciex")) - return pci_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "sbus")) - return sbus_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "upa")) - return upa_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "ebus")) - return ebus_path_component(dp, tmp_buf); + !strcmp(parent->type, "pciex")) { + pci_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "sbus")) { + sbus_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "upa")) { + upa_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "ebus")) { + ebus_path_component(dp, tmp_buf); + return; + } if (!strcmp(parent->name, "usb") || - !strcmp(parent->name, "hub")) - return usb_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "i2c")) - return i2c_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "firewire")) - return ieee1394_path_component(dp, tmp_buf); - if (!strcmp(parent->type, "virtual-devices")) - return vdev_path_component(dp, tmp_buf); - + !strcmp(parent->name, "hub")) { + usb_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "i2c")) { + i2c_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "firewire")) { + ieee1394_path_component(dp, tmp_buf); + return; + } + if (!strcmp(parent->type, "virtual-devices")) { + vdev_path_component(dp, tmp_buf); + return; + } /* "isa" is handled with platform naming */ } /* Use platform naming convention. */ - if (tlb_type == hypervisor) - return sun4v_path_component(dp, tmp_buf); - else - return sun4u_path_component(dp, tmp_buf); + if (tlb_type == hypervisor) { + sun4v_path_component(dp, tmp_buf); + return; + } else { + sun4u_path_component(dp, tmp_buf); + } } static char * __init build_path_component(struct device_node *dp) diff --git a/arch/sparc64/kernel/psycho_common.c b/arch/sparc64/kernel/psycho_common.c new file mode 100644 index 00000000000..790996428c1 --- /dev/null +++ b/arch/sparc64/kernel/psycho_common.c @@ -0,0 +1,470 @@ +/* psycho_common.c: Code common to PSYCHO and derivative PCI controllers. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ +#include <linux/kernel.h> +#include <linux/interrupt.h> + +#include <asm/upa.h> + +#include "pci_impl.h" +#include "iommu_common.h" +#include "psycho_common.h" + +#define PSYCHO_STRBUF_CTRL_DENAB 0x0000000000000002UL +#define PSYCHO_STCERR_WRITE 0x0000000000000002UL +#define PSYCHO_STCERR_READ 0x0000000000000001UL +#define PSYCHO_STCTAG_PPN 0x0fffffff00000000UL +#define PSYCHO_STCTAG_VPN 0x00000000ffffe000UL +#define PSYCHO_STCTAG_VALID 0x0000000000000002UL +#define PSYCHO_STCTAG_WRITE 0x0000000000000001UL +#define PSYCHO_STCLINE_LINDX 0x0000000001e00000UL +#define PSYCHO_STCLINE_SPTR 0x00000000001f8000UL +#define PSYCHO_STCLINE_LADDR 0x0000000000007f00UL +#define PSYCHO_STCLINE_EPTR 0x00000000000000fcUL +#define PSYCHO_STCLINE_VALID 0x0000000000000002UL +#define PSYCHO_STCLINE_FOFN 0x0000000000000001UL + +static DEFINE_SPINLOCK(stc_buf_lock); +static unsigned long stc_error_buf[128]; +static unsigned long stc_tag_buf[16]; +static unsigned long stc_line_buf[16]; + +static void psycho_check_stc_error(struct pci_pbm_info *pbm) +{ + unsigned long err_base, tag_base, line_base; + struct strbuf *strbuf = &pbm->stc; + u64 control; + int i; + + if (!strbuf->strbuf_control) + return; + + err_base = strbuf->strbuf_err_stat; + tag_base = strbuf->strbuf_tag_diag; + line_base = strbuf->strbuf_line_diag; + + spin_lock(&stc_buf_lock); + + /* This is __REALLY__ dangerous. When we put the streaming + * buffer into diagnostic mode to probe it's tags and error + * status, we _must_ clear all of the line tag valid bits + * before re-enabling the streaming buffer. If any dirty data + * lives in the STC when we do this, we will end up + * invalidating it before it has a chance to reach main + * memory. + */ + control = upa_readq(strbuf->strbuf_control); + upa_writeq(control | PSYCHO_STRBUF_CTRL_DENAB, strbuf->strbuf_control); + for (i = 0; i < 128; i++) { + u64 val; + + val = upa_readq(err_base + (i * 8UL)); + upa_writeq(0UL, err_base + (i * 8UL)); + stc_error_buf[i] = val; + } + for (i = 0; i < 16; i++) { + stc_tag_buf[i] = upa_readq(tag_base + (i * 8UL)); + stc_line_buf[i] = upa_readq(line_base + (i * 8UL)); + upa_writeq(0UL, tag_base + (i * 8UL)); + upa_writeq(0UL, line_base + (i * 8UL)); + } + + /* OK, state is logged, exit diagnostic mode. */ + upa_writeq(control, strbuf->strbuf_control); + + for (i = 0; i < 16; i++) { + int j, saw_error, first, last; + + saw_error = 0; + first = i * 8; + last = first + 8; + for (j = first; j < last; j++) { + u64 errval = stc_error_buf[j]; + if (errval != 0) { + saw_error++; + printk(KERN_ERR "%s: STC_ERR(%d)[wr(%d)" + "rd(%d)]\n", + pbm->name, + j, + (errval & PSYCHO_STCERR_WRITE) ? 1 : 0, + (errval & PSYCHO_STCERR_READ) ? 1 : 0); + } + } + if (saw_error != 0) { + u64 tagval = stc_tag_buf[i]; + u64 lineval = stc_line_buf[i]; + printk(KERN_ERR "%s: STC_TAG(%d)[PA(%016lx)VA(%08lx)" + "V(%d)W(%d)]\n", + pbm->name, + i, + ((tagval & PSYCHO_STCTAG_PPN) >> 19UL), + (tagval & PSYCHO_STCTAG_VPN), + ((tagval & PSYCHO_STCTAG_VALID) ? 1 : 0), + ((tagval & PSYCHO_STCTAG_WRITE) ? 1 : 0)); + printk(KERN_ERR "%s: STC_LINE(%d)[LIDX(%lx)SP(%lx)" + "LADDR(%lx)EP(%lx)V(%d)FOFN(%d)]\n", + pbm->name, + i, + ((lineval & PSYCHO_STCLINE_LINDX) >> 21UL), + ((lineval & PSYCHO_STCLINE_SPTR) >> 15UL), + ((lineval & PSYCHO_STCLINE_LADDR) >> 8UL), + ((lineval & PSYCHO_STCLINE_EPTR) >> 2UL), + ((lineval & PSYCHO_STCLINE_VALID) ? 1 : 0), + ((lineval & PSYCHO_STCLINE_FOFN) ? 1 : 0)); + } + } + + spin_unlock(&stc_buf_lock); +} + +#define PSYCHO_IOMMU_TAG 0xa580UL +#define PSYCHO_IOMMU_DATA 0xa600UL + +static void psycho_record_iommu_tags_and_data(struct pci_pbm_info *pbm, + u64 *tag, u64 *data) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long base = pbm->controller_regs; + unsigned long off = i * 8UL; + + tag[i] = upa_readq(base + PSYCHO_IOMMU_TAG+off); + data[i] = upa_readq(base + PSYCHO_IOMMU_DATA+off); + + /* Now clear out the entry. */ + upa_writeq(0, base + PSYCHO_IOMMU_TAG + off); + upa_writeq(0, base + PSYCHO_IOMMU_DATA + off); + } +} + +#define PSYCHO_IOMMU_TAG_ERRSTS (0x3UL << 23UL) +#define PSYCHO_IOMMU_TAG_ERR (0x1UL << 22UL) +#define PSYCHO_IOMMU_TAG_WRITE (0x1UL << 21UL) +#define PSYCHO_IOMMU_TAG_STREAM (0x1UL << 20UL) +#define PSYCHO_IOMMU_TAG_SIZE (0x1UL << 19UL) +#define PSYCHO_IOMMU_TAG_VPAGE 0x7ffffUL +#define PSYCHO_IOMMU_DATA_VALID (1UL << 30UL) +#define PSYCHO_IOMMU_DATA_CACHE (1UL << 28UL) +#define PSYCHO_IOMMU_DATA_PPAGE 0xfffffffUL + +static void psycho_dump_iommu_tags_and_data(struct pci_pbm_info *pbm, + u64 *tag, u64 *data) +{ + int i; + + for (i = 0; i < 16; i++) { + u64 tag_val, data_val; + const char *type_str; + tag_val = tag[i]; + if (!(tag_val & PSYCHO_IOMMU_TAG_ERR)) + continue; + + data_val = data[i]; + switch((tag_val & PSYCHO_IOMMU_TAG_ERRSTS) >> 23UL) { + case 0: + type_str = "Protection Error"; + break; + case 1: + type_str = "Invalid Error"; + break; + case 2: + type_str = "TimeOut Error"; + break; + case 3: + default: + type_str = "ECC Error"; + break; + } + + printk(KERN_ERR "%s: IOMMU TAG(%d)[error(%s) wr(%d) " + "str(%d) sz(%dK) vpg(%08lx)]\n", + pbm->name, i, type_str, + ((tag_val & PSYCHO_IOMMU_TAG_WRITE) ? 1 : 0), + ((tag_val & PSYCHO_IOMMU_TAG_STREAM) ? 1 : 0), + ((tag_val & PSYCHO_IOMMU_TAG_SIZE) ? 64 : 8), + (tag_val & PSYCHO_IOMMU_TAG_VPAGE) << IOMMU_PAGE_SHIFT); + printk(KERN_ERR "%s: IOMMU DATA(%d)[valid(%d) cache(%d) " + "ppg(%016lx)]\n", + pbm->name, i, + ((data_val & PSYCHO_IOMMU_DATA_VALID) ? 1 : 0), + ((data_val & PSYCHO_IOMMU_DATA_CACHE) ? 1 : 0), + (data_val & PSYCHO_IOMMU_DATA_PPAGE)<<IOMMU_PAGE_SHIFT); + } +} + +#define PSYCHO_IOMMU_CTRL_XLTESTAT 0x0000000006000000UL +#define PSYCHO_IOMMU_CTRL_XLTEERR 0x0000000001000000UL + +void psycho_check_iommu_error(struct pci_pbm_info *pbm, + unsigned long afsr, + unsigned long afar, + enum psycho_error_type type) +{ + u64 control, iommu_tag[16], iommu_data[16]; + struct iommu *iommu = pbm->iommu; + unsigned long flags; + + spin_lock_irqsave(&iommu->lock, flags); + control = upa_readq(iommu->iommu_control); + if (control & PSYCHO_IOMMU_CTRL_XLTEERR) { + const char *type_str; + + control &= ~PSYCHO_IOMMU_CTRL_XLTEERR; + upa_writeq(control, iommu->iommu_control); + + switch ((control & PSYCHO_IOMMU_CTRL_XLTESTAT) >> 25UL) { + case 0: + type_str = "Protection Error"; + break; + case 1: + type_str = "Invalid Error"; + break; + case 2: + type_str = "TimeOut Error"; + break; + case 3: + default: + type_str = "ECC Error"; + break; + }; + printk(KERN_ERR "%s: IOMMU Error, type[%s]\n", + pbm->name, type_str); + + /* It is very possible for another DVMA to occur while + * we do this probe, and corrupt the system further. + * But we are so screwed at this point that we are + * likely to crash hard anyways, so get as much + * diagnostic information to the console as we can. + */ + psycho_record_iommu_tags_and_data(pbm, iommu_tag, iommu_data); + psycho_dump_iommu_tags_and_data(pbm, iommu_tag, iommu_data); + } + psycho_check_stc_error(pbm); + spin_unlock_irqrestore(&iommu->lock, flags); +} + +#define PSYCHO_PCICTRL_SBH_ERR 0x0000000800000000UL +#define PSYCHO_PCICTRL_SERR 0x0000000400000000UL + +static irqreturn_t psycho_pcierr_intr_other(struct pci_pbm_info *pbm) +{ + irqreturn_t ret = IRQ_NONE; + u64 csr, csr_error_bits; + u16 stat, *addr; + + csr = upa_readq(pbm->pci_csr); + csr_error_bits = csr & (PSYCHO_PCICTRL_SBH_ERR | PSYCHO_PCICTRL_SERR); + if (csr_error_bits) { + /* Clear the errors. */ + upa_writeq(csr, pbm->pci_csr); + + /* Log 'em. */ + if (csr_error_bits & PSYCHO_PCICTRL_SBH_ERR) + printk(KERN_ERR "%s: PCI streaming byte hole " + "error asserted.\n", pbm->name); + if (csr_error_bits & PSYCHO_PCICTRL_SERR) + printk(KERN_ERR "%s: PCI SERR signal asserted.\n", + pbm->name); + ret = IRQ_HANDLED; + } + addr = psycho_pci_config_mkaddr(pbm, pbm->pci_first_busno, + 0, PCI_STATUS); + pci_config_read16(addr, &stat); + if (stat & (PCI_STATUS_PARITY | + PCI_STATUS_SIG_TARGET_ABORT | + PCI_STATUS_REC_TARGET_ABORT | + PCI_STATUS_REC_MASTER_ABORT | + PCI_STATUS_SIG_SYSTEM_ERROR)) { + printk(KERN_ERR "%s: PCI bus error, PCI_STATUS[%04x]\n", + pbm->name, stat); + pci_config_write16(addr, 0xffff); + ret = IRQ_HANDLED; + } + return ret; +} + +#define PSYCHO_PCIAFSR_PMA 0x8000000000000000UL +#define PSYCHO_PCIAFSR_PTA 0x4000000000000000UL +#define PSYCHO_PCIAFSR_PRTRY 0x2000000000000000UL +#define PSYCHO_PCIAFSR_PPERR 0x1000000000000000UL +#define PSYCHO_PCIAFSR_SMA 0x0800000000000000UL +#define PSYCHO_PCIAFSR_STA 0x0400000000000000UL +#define PSYCHO_PCIAFSR_SRTRY 0x0200000000000000UL +#define PSYCHO_PCIAFSR_SPERR 0x0100000000000000UL +#define PSYCHO_PCIAFSR_RESV1 0x00ff000000000000UL +#define PSYCHO_PCIAFSR_BMSK 0x0000ffff00000000UL +#define PSYCHO_PCIAFSR_BLK 0x0000000080000000UL +#define PSYCHO_PCIAFSR_RESV2 0x0000000040000000UL +#define PSYCHO_PCIAFSR_MID 0x000000003e000000UL +#define PSYCHO_PCIAFSR_RESV3 0x0000000001ffffffUL + +irqreturn_t psycho_pcierr_intr(int irq, void *dev_id) +{ + struct pci_pbm_info *pbm = dev_id; + u64 afsr, afar, error_bits; + int reported; + + afsr = upa_readq(pbm->pci_afsr); + afar = upa_readq(pbm->pci_afar); + error_bits = afsr & + (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_PTA | + PSYCHO_PCIAFSR_PRTRY | PSYCHO_PCIAFSR_PPERR | + PSYCHO_PCIAFSR_SMA | PSYCHO_PCIAFSR_STA | + PSYCHO_PCIAFSR_SRTRY | PSYCHO_PCIAFSR_SPERR); + if (!error_bits) + return psycho_pcierr_intr_other(pbm); + upa_writeq(error_bits, pbm->pci_afsr); + printk(KERN_ERR "%s: PCI Error, primary error type[%s]\n", + pbm->name, + (((error_bits & PSYCHO_PCIAFSR_PMA) ? + "Master Abort" : + ((error_bits & PSYCHO_PCIAFSR_PTA) ? + "Target Abort" : + ((error_bits & PSYCHO_PCIAFSR_PRTRY) ? + "Excessive Retries" : + ((error_bits & PSYCHO_PCIAFSR_PPERR) ? + "Parity Error" : "???")))))); + printk(KERN_ERR "%s: bytemask[%04lx] UPA_MID[%02lx] was_block(%d)\n", + pbm->name, + (afsr & PSYCHO_PCIAFSR_BMSK) >> 32UL, + (afsr & PSYCHO_PCIAFSR_MID) >> 25UL, + (afsr & PSYCHO_PCIAFSR_BLK) ? 1 : 0); + printk(KERN_ERR "%s: PCI AFAR [%016lx]\n", pbm->name, afar); + printk(KERN_ERR "%s: PCI Secondary errors [", pbm->name); + reported = 0; + if (afsr & PSYCHO_PCIAFSR_SMA) { + reported++; + printk("(Master Abort)"); + } + if (afsr & PSYCHO_PCIAFSR_STA) { + reported++; + printk("(Target Abort)"); + } + if (afsr & PSYCHO_PCIAFSR_SRTRY) { + reported++; + printk("(Excessive Retries)"); + } + if (afsr & PSYCHO_PCIAFSR_SPERR) { + reported++; + printk("(Parity Error)"); + } + if (!reported) + printk("(none)"); + printk("]\n"); + + if (error_bits & (PSYCHO_PCIAFSR_PTA | PSYCHO_PCIAFSR_STA)) { + psycho_check_iommu_error(pbm, afsr, afar, PCI_ERR); + pci_scan_for_target_abort(pbm, pbm->pci_bus); + } + if (error_bits & (PSYCHO_PCIAFSR_PMA | PSYCHO_PCIAFSR_SMA)) + pci_scan_for_master_abort(pbm, pbm->pci_bus); + + if (error_bits & (PSYCHO_PCIAFSR_PPERR | PSYCHO_PCIAFSR_SPERR)) + pci_scan_for_parity_error(pbm, pbm->pci_bus); + + return IRQ_HANDLED; +} + +static void psycho_iommu_flush(struct pci_pbm_info *pbm) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long off = i * 8; + + upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_TAG + off); + upa_writeq(0, pbm->controller_regs + PSYCHO_IOMMU_DATA + off); + } +} + +#define PSYCHO_IOMMU_CONTROL 0x0200UL +#define PSYCHO_IOMMU_CTRL_TSBSZ 0x0000000000070000UL +#define PSYCHO_IOMMU_TSBSZ_1K 0x0000000000000000UL +#define PSYCHO_IOMMU_TSBSZ_2K 0x0000000000010000UL +#define PSYCHO_IOMMU_TSBSZ_4K 0x0000000000020000UL +#define PSYCHO_IOMMU_TSBSZ_8K 0x0000000000030000UL +#define PSYCHO_IOMMU_TSBSZ_16K 0x0000000000040000UL +#define PSYCHO_IOMMU_TSBSZ_32K 0x0000000000050000UL +#define PSYCHO_IOMMU_TSBSZ_64K 0x0000000000060000UL +#define PSYCHO_IOMMU_TSBSZ_128K 0x0000000000070000UL +#define PSYCHO_IOMMU_CTRL_TBWSZ 0x0000000000000004UL +#define PSYCHO_IOMMU_CTRL_DENAB 0x0000000000000002UL +#define PSYCHO_IOMMU_CTRL_ENAB 0x0000000000000001UL +#define PSYCHO_IOMMU_FLUSH 0x0210UL +#define PSYCHO_IOMMU_TSBBASE 0x0208UL + +int psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize, + u32 dvma_offset, u32 dma_mask, + unsigned long write_complete_offset) +{ + struct iommu *iommu = pbm->iommu; + u64 control; + int err; + + iommu->iommu_control = pbm->controller_regs + PSYCHO_IOMMU_CONTROL; + iommu->iommu_tsbbase = pbm->controller_regs + PSYCHO_IOMMU_TSBBASE; + iommu->iommu_flush = pbm->controller_regs + PSYCHO_IOMMU_FLUSH; + iommu->iommu_tags = pbm->controller_regs + PSYCHO_IOMMU_TAG; + iommu->write_complete_reg = (pbm->controller_regs + + write_complete_offset); + + iommu->iommu_ctxflush = 0; + + control = upa_readq(iommu->iommu_control); + control |= PSYCHO_IOMMU_CTRL_DENAB; + upa_writeq(control, iommu->iommu_control); + + psycho_iommu_flush(pbm); + + /* Leave diag mode enabled for full-flushing done in pci_iommu.c */ + err = iommu_table_init(iommu, tsbsize * 1024 * 8, + dvma_offset, dma_mask, pbm->numa_node); + if (err) + return err; + + upa_writeq(__pa(iommu->page_table), iommu->iommu_tsbbase); + + control = upa_readq(iommu->iommu_control); + control &= ~(PSYCHO_IOMMU_CTRL_TSBSZ | PSYCHO_IOMMU_CTRL_TBWSZ); + control |= PSYCHO_IOMMU_CTRL_ENAB; + + switch (tsbsize) { + case 64: + control |= PSYCHO_IOMMU_TSBSZ_64K; + break; + case 128: + control |= PSYCHO_IOMMU_TSBSZ_128K; + break; + default: + return -EINVAL; + } + + upa_writeq(control, iommu->iommu_control); + + return 0; + +} + +void psycho_pbm_init_common(struct pci_pbm_info *pbm, struct of_device *op, + const char *chip_name, int chip_type) +{ + struct device_node *dp = op->node; + + pbm->name = dp->full_name; + pbm->numa_node = -1; + pbm->chip_type = chip_type; + pbm->chip_version = of_getintprop_default(dp, "version#", 0); + pbm->chip_revision = of_getintprop_default(dp, "module-revision#", 0); + pbm->op = op; + pbm->pci_ops = &sun4u_pci_ops; + pbm->config_space_reg_bits = 8; + pbm->index = pci_num_pbms++; + pci_get_pbm_props(pbm); + pci_determine_mem_io_space(pbm); + + printk(KERN_INFO "%s: %s PCI Bus Module ver[%x:%x]\n", + pbm->name, chip_name, + pbm->chip_version, pbm->chip_revision); +} diff --git a/arch/sparc64/kernel/psycho_common.h b/arch/sparc64/kernel/psycho_common.h new file mode 100644 index 00000000000..092c278ef28 --- /dev/null +++ b/arch/sparc64/kernel/psycho_common.h @@ -0,0 +1,48 @@ +#ifndef _PSYCHO_COMMON_H +#define _PSYCHO_COMMON_H + +/* U2P Programmer's Manual, page 13-55, configuration space + * address format: + * + * 32 24 23 16 15 11 10 8 7 2 1 0 + * --------------------------------------------------------- + * |0 0 0 0 0 0 0 0 1| bus | device | function | reg | 0 0 | + * --------------------------------------------------------- + */ +#define PSYCHO_CONFIG_BASE(PBM) \ + ((PBM)->config_space | (1UL << 24)) +#define PSYCHO_CONFIG_ENCODE(BUS, DEVFN, REG) \ + (((unsigned long)(BUS) << 16) | \ + ((unsigned long)(DEVFN) << 8) | \ + ((unsigned long)(REG))) + +static inline void *psycho_pci_config_mkaddr(struct pci_pbm_info *pbm, + unsigned char bus, + unsigned int devfn, + int where) +{ + return (void *) + (PSYCHO_CONFIG_BASE(pbm) | + PSYCHO_CONFIG_ENCODE(bus, devfn, where)); +} + +enum psycho_error_type { + UE_ERR, CE_ERR, PCI_ERR +}; + +extern void psycho_check_iommu_error(struct pci_pbm_info *pbm, + unsigned long afsr, + unsigned long afar, + enum psycho_error_type type); + +extern irqreturn_t psycho_pcierr_intr(int irq, void *dev_id); + +extern int psycho_iommu_init(struct pci_pbm_info *pbm, int tsbsize, + u32 dvma_offset, u32 dma_mask, + unsigned long write_complete_offset); + +extern void psycho_pbm_init_common(struct pci_pbm_info *pbm, + struct of_device *op, + const char *chip_name, int chip_type); + +#endif /* _PSYCHO_COMMON_H */ diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 10306e476e3..f43adbc773c 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -1050,31 +1050,17 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) return ret; } -asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) +asmlinkage int syscall_trace_enter(struct pt_regs *regs) { int ret = 0; /* do the secure computing check first */ secure_computing(regs->u_regs[UREG_G1]); - if (unlikely(current->audit_context) && syscall_exit_p) { - unsigned long tstate = regs->tstate; - int result = AUDITSC_SUCCESS; - - if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) - result = AUDITSC_FAILURE; - - audit_syscall_exit(result, regs->u_regs[UREG_I0]); - } - - if (test_thread_flag(TIF_SYSCALL_TRACE)) { - if (syscall_exit_p) - tracehook_report_syscall_exit(regs, 0); - else - ret = tracehook_report_syscall_entry(regs); - } + if (test_thread_flag(TIF_SYSCALL_TRACE)) + ret = tracehook_report_syscall_entry(regs); - if (unlikely(current->audit_context) && !syscall_exit_p && !ret) + if (unlikely(current->audit_context) && !ret) audit_syscall_entry((test_thread_flag(TIF_32BIT) ? AUDIT_ARCH_SPARC : AUDIT_ARCH_SPARC64), @@ -1086,3 +1072,19 @@ asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) return ret; } + +asmlinkage void syscall_trace_leave(struct pt_regs *regs) +{ + if (unlikely(current->audit_context)) { + unsigned long tstate = regs->tstate; + int result = AUDITSC_SUCCESS; + + if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) + result = AUDITSC_FAILURE; + + audit_syscall_exit(result, regs->u_regs[UREG_I0]); + } + + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, 0); +} diff --git a/arch/sparc64/kernel/reboot.c b/arch/sparc64/kernel/reboot.c new file mode 100644 index 00000000000..ef89d3d6974 --- /dev/null +++ b/arch/sparc64/kernel/reboot.c @@ -0,0 +1,53 @@ +/* reboot.c: reboot/shutdown/halt/poweroff handling + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ +#include <linux/kernel.h> +#include <linux/reboot.h> +#include <linux/module.h> +#include <linux/pm.h> + +#include <asm/system.h> +#include <asm/oplib.h> +#include <asm/prom.h> + +/* sysctl - toggle power-off restriction for serial console + * systems in machine_power_off() + */ +int scons_pwroff = 1; + +/* This isn't actually used, it exists merely to satisfy the + * reference in kernel/sys.c + */ +void (*pm_power_off)(void) = machine_power_off; +EXPORT_SYMBOL(pm_power_off); + +void machine_power_off(void) +{ + if (strcmp(of_console_device->type, "serial") || scons_pwroff) + prom_halt_power_off(); + + prom_halt(); +} + +void machine_halt(void) +{ + prom_halt(); + panic("Halt failed!"); +} + +void machine_restart(char *cmd) +{ + char *p; + + p = strchr(reboot_command, '\n'); + if (p) + *p = 0; + if (cmd) + prom_reboot(cmd); + if (*reboot_command) + prom_reboot(reboot_command); + prom_reboot(""); + panic("Reboot failed!"); +} + diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c index e33a8a660e9..2ead310066d 100644 --- a/arch/sparc64/kernel/sbus.c +++ b/arch/sparc64/kernel/sbus.c @@ -11,15 +11,17 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/page.h> -#include <asm/sbus.h> #include <asm/io.h> #include <asm/upa.h> #include <asm/cache.h> #include <asm/dma.h> #include <asm/irq.h> #include <asm/prom.h> +#include <asm/oplib.h> #include <asm/starfire.h> #include "iommu_common.h" @@ -52,13 +54,23 @@ #define STRBUF_TAG_VALID 0x02UL /* Enable 64-bit DVMA mode for the given device. */ -void sbus_set_sbus64(struct sbus_dev *sdev, int bursts) +void sbus_set_sbus64(struct device *dev, int bursts) { - struct iommu *iommu = sdev->ofdev.dev.archdata.iommu; - int slot = sdev->slot; + struct iommu *iommu = dev->archdata.iommu; + struct of_device *op = to_of_device(dev); + const struct linux_prom_registers *regs; unsigned long cfg_reg; + int slot; u64 val; + regs = of_get_property(op->node, "reg", NULL); + if (!regs) { + printk(KERN_ERR "sbus_set_sbus64: Cannot find regs for %s\n", + op->node->full_name); + return; + } + slot = regs->which_io; + cfg_reg = iommu->write_complete_reg; switch (slot) { case 0: @@ -191,10 +203,9 @@ static unsigned long sysio_imap_to_iclr(unsigned long imap) return imap + diff; } -unsigned int sbus_build_irq(void *buscookie, unsigned int ino) +static unsigned int sbus_build_irq(struct of_device *op, unsigned int ino) { - struct sbus_bus *sbus = (struct sbus_bus *)buscookie; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long imap, iclr; int sbus_level = 0; @@ -255,12 +266,12 @@ unsigned int sbus_build_irq(void *buscookie, unsigned int ino) #define SYSIO_UEAFSR_RESV2 0x0000001fffffffffUL /* Reserved */ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) { - struct sbus_bus *sbus = dev_id; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct of_device *op = dev_id; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long afsr_reg, afar_reg; unsigned long afsr, afar, error_bits; - int reported; + int reported, portid; afsr_reg = reg_base + SYSIO_UE_AFSR; afar_reg = reg_base + SYSIO_UE_AFAR; @@ -275,9 +286,11 @@ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) SYSIO_UEAFSR_SPIO | SYSIO_UEAFSR_SDRD | SYSIO_UEAFSR_SDWR); upa_writeq(error_bits, afsr_reg); + portid = of_getintprop_default(op->node, "portid", -1); + /* Log the error. */ printk("SYSIO[%x]: Uncorrectable ECC Error, primary error type[%s]\n", - sbus->portid, + portid, (((error_bits & SYSIO_UEAFSR_PPIO) ? "PIO" : ((error_bits & SYSIO_UEAFSR_PDRD) ? @@ -285,12 +298,12 @@ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) ((error_bits & SYSIO_UEAFSR_PDWR) ? "DVMA Write" : "???"))))); printk("SYSIO[%x]: DOFF[%lx] SIZE[%lx] MID[%lx]\n", - sbus->portid, + portid, (afsr & SYSIO_UEAFSR_DOFF) >> 45UL, (afsr & SYSIO_UEAFSR_SIZE) >> 42UL, (afsr & SYSIO_UEAFSR_MID) >> 37UL); - printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar); - printk("SYSIO[%x]: Secondary UE errors [", sbus->portid); + printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar); + printk("SYSIO[%x]: Secondary UE errors [", portid); reported = 0; if (afsr & SYSIO_UEAFSR_SPIO) { reported++; @@ -327,12 +340,12 @@ static irqreturn_t sysio_ue_handler(int irq, void *dev_id) #define SYSIO_CEAFSR_RESV2 0x0000001fffffffffUL /* Reserved */ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) { - struct sbus_bus *sbus = dev_id; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct of_device *op = dev_id; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned long afsr_reg, afar_reg; unsigned long afsr, afar, error_bits; - int reported; + int reported, portid; afsr_reg = reg_base + SYSIO_CE_AFSR; afar_reg = reg_base + SYSIO_CE_AFAR; @@ -347,8 +360,10 @@ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) SYSIO_CEAFSR_SPIO | SYSIO_CEAFSR_SDRD | SYSIO_CEAFSR_SDWR); upa_writeq(error_bits, afsr_reg); + portid = of_getintprop_default(op->node, "portid", -1); + printk("SYSIO[%x]: Correctable ECC Error, primary error type[%s]\n", - sbus->portid, + portid, (((error_bits & SYSIO_CEAFSR_PPIO) ? "PIO" : ((error_bits & SYSIO_CEAFSR_PDRD) ? @@ -360,14 +375,14 @@ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) * XXX UDB CE trap handler does... -DaveM */ printk("SYSIO[%x]: DOFF[%lx] ECC Syndrome[%lx] Size[%lx] MID[%lx]\n", - sbus->portid, + portid, (afsr & SYSIO_CEAFSR_DOFF) >> 45UL, (afsr & SYSIO_CEAFSR_ESYND) >> 48UL, (afsr & SYSIO_CEAFSR_SIZE) >> 42UL, (afsr & SYSIO_CEAFSR_MID) >> 37UL); - printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar); + printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar); - printk("SYSIO[%x]: Secondary CE errors [", sbus->portid); + printk("SYSIO[%x]: Secondary CE errors [", portid); reported = 0; if (afsr & SYSIO_CEAFSR_SPIO) { reported++; @@ -404,11 +419,11 @@ static irqreturn_t sysio_ce_handler(int irq, void *dev_id) #define SYSIO_SBAFSR_RESV3 0x0000001fffffffffUL /* Reserved */ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) { - struct sbus_bus *sbus = dev_id; - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct of_device *op = dev_id; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long afsr_reg, afar_reg, reg_base; unsigned long afsr, afar, error_bits; - int reported; + int reported, portid; reg_base = iommu->write_complete_reg - 0x2000UL; afsr_reg = reg_base + SYSIO_SBUS_AFSR; @@ -423,9 +438,11 @@ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) SYSIO_SBAFSR_SLE | SYSIO_SBAFSR_STO | SYSIO_SBAFSR_SBERR); upa_writeq(error_bits, afsr_reg); + portid = of_getintprop_default(op->node, "portid", -1); + /* Log the error. */ printk("SYSIO[%x]: SBUS Error, primary error type[%s] read(%d)\n", - sbus->portid, + portid, (((error_bits & SYSIO_SBAFSR_PLE) ? "Late PIO Error" : ((error_bits & SYSIO_SBAFSR_PTO) ? @@ -434,11 +451,11 @@ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) "Error Ack" : "???")))), (afsr & SYSIO_SBAFSR_RD) ? 1 : 0); printk("SYSIO[%x]: size[%lx] MID[%lx]\n", - sbus->portid, + portid, (afsr & SYSIO_SBAFSR_SIZE) >> 42UL, (afsr & SYSIO_SBAFSR_MID) >> 37UL); - printk("SYSIO[%x]: AFAR[%016lx]\n", sbus->portid, afar); - printk("SYSIO[%x]: Secondary SBUS errors [", sbus->portid); + printk("SYSIO[%x]: AFAR[%016lx]\n", portid, afar); + printk("SYSIO[%x]: Secondary SBUS errors [", portid); reported = 0; if (afsr & SYSIO_SBAFSR_SLE) { reported++; @@ -470,34 +487,37 @@ static irqreturn_t sysio_sbus_error_handler(int irq, void *dev_id) #define SYSIO_CE_INO 0x35 #define SYSIO_SBUSERR_INO 0x36 -static void __init sysio_register_error_handlers(struct sbus_bus *sbus) +static void __init sysio_register_error_handlers(struct of_device *op) { - struct iommu *iommu = sbus->ofdev.dev.archdata.iommu; + struct iommu *iommu = op->dev.archdata.iommu; unsigned long reg_base = iommu->write_complete_reg - 0x2000UL; unsigned int irq; u64 control; + int portid; + + portid = of_getintprop_default(op->node, "portid", -1); - irq = sbus_build_irq(sbus, SYSIO_UE_INO); + irq = sbus_build_irq(op, SYSIO_UE_INO); if (request_irq(irq, sysio_ue_handler, 0, - "SYSIO_UE", sbus) < 0) { + "SYSIO_UE", op) < 0) { prom_printf("SYSIO[%x]: Cannot register UE interrupt.\n", - sbus->portid); + portid); prom_halt(); } - irq = sbus_build_irq(sbus, SYSIO_CE_INO); + irq = sbus_build_irq(op, SYSIO_CE_INO); if (request_irq(irq, sysio_ce_handler, 0, - "SYSIO_CE", sbus) < 0) { + "SYSIO_CE", op) < 0) { prom_printf("SYSIO[%x]: Cannot register CE interrupt.\n", - sbus->portid); + portid); prom_halt(); } - irq = sbus_build_irq(sbus, SYSIO_SBUSERR_INO); + irq = sbus_build_irq(op, SYSIO_SBUSERR_INO); if (request_irq(irq, sysio_sbus_error_handler, 0, - "SYSIO_SBERR", sbus) < 0) { + "SYSIO_SBERR", op) < 0) { prom_printf("SYSIO[%x]: Cannot register SBUS Error interrupt.\n", - sbus->portid); + portid); prom_halt(); } @@ -513,19 +533,15 @@ static void __init sysio_register_error_handlers(struct sbus_bus *sbus) } /* Boot time initialization. */ -static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) +static void __init sbus_iommu_init(struct of_device *op) { const struct linux_prom64_registers *pr; - struct device_node *dp; + struct device_node *dp = op->node; struct iommu *iommu; struct strbuf *strbuf; unsigned long regs, reg_base; + int i, portid; u64 control; - int i; - - dp = of_find_node_by_phandle(__node); - - sbus->portid = of_getintprop_default(dp, "upa-portid", -1); pr = of_get_property(dp, "reg", NULL); if (!pr) { @@ -542,9 +558,9 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) if (!strbuf) goto fatal_memory_error; - sbus->ofdev.dev.archdata.iommu = iommu; - sbus->ofdev.dev.archdata.stc = strbuf; - sbus->ofdev.dev.archdata.numa_node = -1; + op->dev.archdata.iommu = iommu; + op->dev.archdata.stc = strbuf; + op->dev.archdata.numa_node = -1; reg_base = regs + SYSIO_IOMMUREG_BASE; iommu->iommu_control = reg_base + IOMMU_CONTROL; @@ -572,8 +588,9 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) */ iommu->write_complete_reg = regs + 0x2000UL; - printk("SYSIO: UPA portID %x, at %016lx\n", - sbus->portid, regs); + portid = of_getintprop_default(op->node, "portid", -1); + printk(KERN_INFO "SYSIO: UPA portID %x, at %016lx\n", + portid, regs); /* Setup for TSB_SIZE=7, TBW_SIZE=0, MMU_DE=1, MMU_EN=1 */ if (iommu_table_init(iommu, IO_TSB_SIZE, MAP_BASE, 0xffffffff, -1)) @@ -631,56 +648,27 @@ static void __init sbus_iommu_init(int __node, struct sbus_bus *sbus) /* Now some Xfire specific grot... */ if (this_is_starfire) - starfire_hookup(sbus->portid); + starfire_hookup(portid); - sysio_register_error_handlers(sbus); + sysio_register_error_handlers(op); return; fatal_memory_error: prom_printf("sbus_iommu_init: Fatal memory allocation error.\n"); } -void sbus_fill_device_irq(struct sbus_dev *sdev) +static int __init sbus_init(void) { - struct device_node *dp = of_find_node_by_phandle(sdev->prom_node); - const struct linux_prom_irqs *irqs; - - irqs = of_get_property(dp, "interrupts", NULL); - if (!irqs) { - sdev->irqs[0] = 0; - sdev->num_irqs = 0; - } else { - unsigned int pri = irqs[0].pri; + struct device_node *dp; - sdev->num_irqs = 1; - if (pri < 0x20) - pri += sdev->slot * 8; + for_each_node_by_name(dp, "sbus") { + struct of_device *op = of_find_device_by_node(dp); - sdev->irqs[0] = sbus_build_irq(sdev->bus, pri); + sbus_iommu_init(op); + of_propagate_archdata(op); } -} -void __init sbus_arch_bus_ranges_init(struct device_node *pn, struct sbus_bus *sbus) -{ -} - -void __init sbus_setup_iommu(struct sbus_bus *sbus, struct device_node *dp) -{ - sbus_iommu_init(dp->node, sbus); -} - -void __init sbus_setup_arch_props(struct sbus_bus *sbus, struct device_node *dp) -{ -} - -int __init sbus_arch_preinit(void) -{ return 0; } -void __init sbus_arch_postinit(void) -{ - extern void firetruck_init(void); - - firetruck_init(); -} +subsys_initcall(sbus_init); diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 2be166c544c..e5627118e61 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -21,6 +21,7 @@ #include <linux/jiffies.h> #include <linux/profile.h> #include <linux/lmb.h> +#include <linux/cpu.h> #include <asm/head.h> #include <asm/ptrace.h> @@ -115,6 +116,9 @@ void __cpuinit smp_callin(void) atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; + /* inform the notifiers about the new cpu */ + notify_cpu_starting(cpuid); + while (!cpu_isset(cpuid, smp_commenced_mask)) rmb(); diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index 0804f71df6c..30bba8b0a3b 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -36,7 +36,6 @@ #include <asm/elf.h> #include <asm/head.h> #include <asm/smp.h> -#include <asm/mostek.h> #include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/checksum.h> @@ -44,12 +43,8 @@ #include <asm/pgalloc.h> #include <asm/cacheflush.h> #ifdef CONFIG_SBUS -#include <asm/sbus.h> #include <asm/dma.h> #endif -#ifdef CONFIG_PCI -#include <asm/ebus.h> -#endif #include <asm/ns87303.h> #include <asm/timer.h> #include <asm/cpudata.h> @@ -68,7 +63,6 @@ extern void *__memscan_zero(void *, size_t); extern void *__memscan_generic(void *, int, size_t); extern int __memcmp(const void *, const void *, __kernel_size_t); extern __kernel_size_t strlen(const char *); -extern void syscall_trace(struct pt_regs *, int); extern void sys_sigsuspend(void); extern int compat_sys_ioctl(unsigned int fd, unsigned int cmd, u32 arg); extern int (*handle_mathemu)(struct pt_regs *, struct fpustate *); @@ -154,26 +148,12 @@ EXPORT_SYMBOL(flush_dcache_page); EXPORT_SYMBOL(__flush_dcache_range); #endif -EXPORT_SYMBOL(mostek_lock); -EXPORT_SYMBOL(mstk48t02_regs); #ifdef CONFIG_SUN_AUXIO EXPORT_SYMBOL(auxio_set_led); EXPORT_SYMBOL(auxio_set_lte); #endif #ifdef CONFIG_SBUS -EXPORT_SYMBOL(sbus_root); -EXPORT_SYMBOL(dma_chain); EXPORT_SYMBOL(sbus_set_sbus64); -EXPORT_SYMBOL(sbus_alloc_consistent); -EXPORT_SYMBOL(sbus_free_consistent); -EXPORT_SYMBOL(sbus_map_single); -EXPORT_SYMBOL(sbus_unmap_single); -EXPORT_SYMBOL(sbus_map_sg); -EXPORT_SYMBOL(sbus_unmap_sg); -EXPORT_SYMBOL(sbus_dma_sync_single_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_single_for_device); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_cpu); -EXPORT_SYMBOL(sbus_dma_sync_sg_for_device); #endif EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(outsw); @@ -182,7 +162,6 @@ EXPORT_SYMBOL(insb); EXPORT_SYMBOL(insw); EXPORT_SYMBOL(insl); #ifdef CONFIG_PCI -EXPORT_SYMBOL(ebus_chain); EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); EXPORT_SYMBOL(pci_map_single); @@ -300,3 +279,5 @@ EXPORT_SYMBOL(xor_niagara_2); EXPORT_SYMBOL(xor_niagara_3); EXPORT_SYMBOL(xor_niagara_4); EXPORT_SYMBOL(xor_niagara_5); + +EXPORT_SYMBOL_GPL(real_hard_smp_processor_id); diff --git a/arch/sparc64/kernel/sstate.c b/arch/sparc64/kernel/sstate.c index 5b6e75b7f05..8cdbe5946b4 100644 --- a/arch/sparc64/kernel/sstate.c +++ b/arch/sparc64/kernel/sstate.c @@ -1,14 +1,15 @@ /* sstate.c: System soft state support. * - * Copyright (C) 2007 David S. Miller <davem@davemloft.net> + * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h> #include <linux/notifier.h> +#include <linux/reboot.h> #include <linux/init.h> #include <asm/hypervisor.h> -#include <asm/sstate.h> +#include <asm/spitfire.h> #include <asm/oplib.h> #include <asm/head.h> #include <asm/io.h> @@ -50,31 +51,34 @@ static const char rebooting_msg[32] __attribute__((aligned(32))) = static const char panicing_msg[32] __attribute__((aligned(32))) = "Linux panicing"; -void sstate_booting(void) +static int sstate_reboot_call(struct notifier_block *np, unsigned long type, void *_unused) { - do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg); -} + const char *msg; -void sstate_running(void) -{ - do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg); -} + switch (type) { + case SYS_DOWN: + default: + msg = rebooting_msg; + break; -void sstate_halt(void) -{ - do_set_sstate(HV_SOFT_STATE_TRANSITION, halting_msg); -} + case SYS_HALT: + msg = halting_msg; + break; -void sstate_poweroff(void) -{ - do_set_sstate(HV_SOFT_STATE_TRANSITION, poweroff_msg); -} + case SYS_POWER_OFF: + msg = poweroff_msg; + break; + } -void sstate_reboot(void) -{ - do_set_sstate(HV_SOFT_STATE_TRANSITION, rebooting_msg); + do_set_sstate(HV_SOFT_STATE_TRANSITION, msg); + + return NOTIFY_OK; } +static struct notifier_block sstate_reboot_notifier = { + .notifier_call = sstate_reboot_call, +}; + static int sstate_panic_event(struct notifier_block *n, unsigned long event, void *ptr) { do_set_sstate(HV_SOFT_STATE_TRANSITION, panicing_msg); @@ -87,18 +91,37 @@ static struct notifier_block sstate_panic_block = { .priority = INT_MAX, }; -void __init sun4v_sstate_init(void) +static int __init sstate_init(void) { unsigned long major, minor; + if (tlb_type != hypervisor) + return 0; + major = 1; minor = 0; if (sun4v_hvapi_register(HV_GRP_SOFT_STATE, major, &minor)) - return; + return 0; hv_supports_soft_state = 1; prom_sun4v_guest_soft_state(); + + do_set_sstate(HV_SOFT_STATE_TRANSITION, booting_msg); + atomic_notifier_chain_register(&panic_notifier_list, &sstate_panic_block); + register_reboot_notifier(&sstate_reboot_notifier); + + return 0; } + +core_initcall(sstate_init); + +static int __init sstate_running(void) +{ + do_set_sstate(HV_SOFT_STATE_NORMAL, running_msg); + return 0; +} + +late_initcall(sstate_running); diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c index 7461581b3bb..060d0f3a615 100644 --- a/arch/sparc64/kernel/starfire.c +++ b/arch/sparc64/kernel/starfire.c @@ -28,11 +28,6 @@ void check_if_starfire(void) this_is_starfire = 1; } -void starfire_cpu_setup(void) -{ - /* Currently, nothing to do. */ -} - int starfire_hard_smp_processor_id(void) { return upa_readl(0x1fff40000d0UL); diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 3d118531baf..3320c9d0075 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -575,14 +575,6 @@ asmlinkage long sys32_settimeofday(struct compat_timeval __user *tv, return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); } -/* These are here just in case some old sparc32 binary calls it. */ -asmlinkage long sys32_pause(void) -{ - current->state = TASK_INTERRUPTIBLE; - schedule(); - return -ERESTARTNOHAND; -} - asmlinkage compat_ssize_t sys32_pread64(unsigned int fd, char __user *ubuf, compat_size_t count, diff --git a/arch/sparc64/kernel/syscalls.S b/arch/sparc64/kernel/syscalls.S index a2f24270ed8..7a6786a7136 100644 --- a/arch/sparc64/kernel/syscalls.S +++ b/arch/sparc64/kernel/syscalls.S @@ -65,9 +65,8 @@ sys32_rt_sigreturn: andcc %l5, (_TIF_SYSCALL_TRACE|_TIF_SECCOMP|_TIF_SYSCALL_AUDIT), %g0 be,pt %icc, rtrap nop - add %sp, PTREGS_OFF, %o0 - call syscall_trace - mov 1, %o1 + call syscall_trace_leave + add %sp, PTREGS_OFF, %o0 ba,pt %xcc, rtrap nop @@ -159,9 +158,8 @@ linux_sparc_ni_syscall: or %l7, %lo(sys_ni_syscall), %l7 linux_syscall_trace32: - add %sp, PTREGS_OFF, %o0 - call syscall_trace - clr %o1 + call syscall_trace_enter + add %sp, PTREGS_OFF, %o0 brnz,pn %o0, 3f mov -ENOSYS, %o0 srl %i0, 0, %o0 @@ -172,9 +170,8 @@ linux_syscall_trace32: srl %i3, 0, %o3 linux_syscall_trace: - add %sp, PTREGS_OFF, %o0 - call syscall_trace - clr %o1 + call syscall_trace_enter + add %sp, PTREGS_OFF, %o0 brnz,pn %o0, 3f mov -ENOSYS, %o0 mov %i0, %o0 @@ -275,9 +272,8 @@ ret_sys_call: b,pt %xcc, rtrap stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] linux_syscall_trace2: - add %sp, PTREGS_OFF, %o0 - call syscall_trace - mov 1, %o1 + call syscall_trace_leave + add %sp, PTREGS_OFF, %o0 stx %l1, [%sp + PTREGS_OFF + PT_V9_TPC] ba,pt %xcc, rtrap stx %l2, [%sp + PTREGS_OFF + PT_V9_TNPC] diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 0fdbf3ba956..5daee4b04dd 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -23,7 +23,7 @@ sys_call_table32: /*10*/ .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod /*15*/ .word sys_chmod, sys_lchown16, sparc_brk, sys32_perfctr, sys32_lseek /*20*/ .word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16 -/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys32_pause +/*25*/ .word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys_pause /*30*/ .word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice .word sys_chown, sys_sync, sys32_kill, compat_sys_newstat, sys32_sendfile /*40*/ .word compat_sys_newlstat, sys_dup, sys_pipe, compat_sys_times, sys_getuid diff --git a/arch/sparc64/kernel/time.c b/arch/sparc64/kernel/time.c index cc16fdcf98a..80d71a5ce1e 100644 --- a/arch/sparc64/kernel/time.c +++ b/arch/sparc64/kernel/time.c @@ -30,13 +30,14 @@ #include <linux/percpu.h> #include <linux/miscdevice.h> #include <linux/rtc.h> +#include <linux/rtc/m48t59.h> #include <linux/kernel_stat.h> #include <linux/clockchips.h> #include <linux/clocksource.h> #include <linux/of_device.h> +#include <linux/platform_device.h> #include <asm/oplib.h> -#include <asm/mostek.h> #include <asm/timer.h> #include <asm/irq.h> #include <asm/io.h> @@ -50,18 +51,7 @@ #include "entry.h" -DEFINE_SPINLOCK(mostek_lock); DEFINE_SPINLOCK(rtc_lock); -void __iomem *mstk48t02_regs = NULL; -#ifdef CONFIG_PCI -unsigned long ds1287_regs = 0UL; -static void __iomem *bq4802_regs; -#endif - -static void __iomem *mstk48t08_regs; -static void __iomem *mstk48t59_regs; - -static int set_rtc_mmss(unsigned long); #define TICK_PRIV_BIT (1UL << 63) #define TICKCMP_IRQ_BIT (1UL << 63) @@ -405,313 +395,167 @@ static unsigned long timer_ticks_per_nsec_quotient __read_mostly; int update_persistent_clock(struct timespec now) { - return set_rtc_mmss(now.tv_sec); -} + struct rtc_device *rtc = rtc_class_open("rtc0"); + int err = -1; -/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ -static void __init kick_start_clock(void) -{ - void __iomem *regs = mstk48t02_regs; - u8 sec, tmp; - int i, count; - - prom_printf("CLOCK: Clock was stopped. Kick start "); - - spin_lock_irq(&mostek_lock); - - /* Turn on the kick start bit to start the oscillator. */ - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - tmp = mostek_read(regs + MOSTEK_SEC); - tmp &= ~MSTK_STOP; - mostek_write(regs + MOSTEK_SEC, tmp); - tmp = mostek_read(regs + MOSTEK_HOUR); - tmp |= MSTK_KICK_START; - mostek_write(regs + MOSTEK_HOUR, tmp); - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - /* Delay to allow the clock oscillator to start. */ - sec = MSTK_REG_SEC(regs); - for (i = 0; i < 3; i++) { - while (sec == MSTK_REG_SEC(regs)) - for (count = 0; count < 100000; count++) - /* nothing */ ; - prom_printf("."); - sec = MSTK_REG_SEC(regs); - } - prom_printf("\n"); - - spin_lock_irq(&mostek_lock); - - /* Turn off kick start and set a "valid" time and date. */ - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - tmp = mostek_read(regs + MOSTEK_HOUR); - tmp &= ~MSTK_KICK_START; - mostek_write(regs + MOSTEK_HOUR, tmp); - MSTK_SET_REG_SEC(regs,0); - MSTK_SET_REG_MIN(regs,0); - MSTK_SET_REG_HOUR(regs,0); - MSTK_SET_REG_DOW(regs,5); - MSTK_SET_REG_DOM(regs,1); - MSTK_SET_REG_MONTH(regs,8); - MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - /* Ensure the kick start bit is off. If it isn't, turn it off. */ - while (mostek_read(regs + MOSTEK_HOUR) & MSTK_KICK_START) { - prom_printf("CLOCK: Kick start still on!\n"); - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - tmp = mostek_read(regs + MOSTEK_HOUR); - tmp &= ~MSTK_KICK_START; - mostek_write(regs + MOSTEK_HOUR, tmp); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); + if (rtc) { + err = rtc_set_mmss(rtc, now.tv_sec); + rtc_class_close(rtc); } - prom_printf("CLOCK: Kick start procedure successful.\n"); + return err; } -/* Return nonzero if the clock chip battery is low. */ -static int __init has_low_battery(void) -{ - void __iomem *regs = mstk48t02_regs; - u8 data1, data2; - - spin_lock_irq(&mostek_lock); +unsigned long cmos_regs; +EXPORT_SYMBOL(cmos_regs); - data1 = mostek_read(regs + MOSTEK_EEPROM); /* Read some data. */ - mostek_write(regs + MOSTEK_EEPROM, ~data1); /* Write back the complement. */ - data2 = mostek_read(regs + MOSTEK_EEPROM); /* Read back the complement. */ - mostek_write(regs + MOSTEK_EEPROM, data1); /* Restore original value. */ +static struct resource rtc_cmos_resource; - spin_unlock_irq(&mostek_lock); - - return (data1 == data2); /* Was the write blocked? */ -} +static struct platform_device rtc_cmos_device = { + .name = "rtc_cmos", + .id = -1, + .resource = &rtc_cmos_resource, + .num_resources = 1, +}; -static void __init mostek_set_system_time(void __iomem *mregs) +static int __devinit rtc_probe(struct of_device *op, const struct of_device_id *match) { - unsigned int year, mon, day, hour, min, sec; - u8 tmp; - - spin_lock_irq(&mostek_lock); + struct resource *r; - /* Traditional Mostek chip. */ - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); + printk(KERN_INFO "%s: RTC regs at 0x%lx\n", + op->node->full_name, op->resource[0].start); - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); - - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); + /* The CMOS RTC driver only accepts IORESOURCE_IO, so cons + * up a fake resource so that the probe works for all cases. + * When the RTC is behind an ISA bus it will have IORESOURCE_IO + * already, whereas when it's behind EBUS is will be IORESOURCE_MEM. + */ - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); + r = &rtc_cmos_resource; + r->flags = IORESOURCE_IO; + r->name = op->resource[0].name; + r->start = op->resource[0].start; + r->end = op->resource[0].end; - spin_unlock_irq(&mostek_lock); + cmos_regs = op->resource[0].start; + return platform_device_register(&rtc_cmos_device); } -/* Probe for the real time clock chip. */ -static void __init set_system_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - void __iomem *mregs = mstk48t02_regs; -#ifdef CONFIG_PCI - unsigned long dregs = ds1287_regs; - void __iomem *bregs = bq4802_regs; -#else - unsigned long dregs = 0UL; - void __iomem *bregs = 0UL; -#endif - - if (!mregs && !dregs && !bregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - - if (mregs) { - mostek_set_system_time(mregs); - return; - } - - if (bregs) { - unsigned char val = readb(bregs + 0x0e); - unsigned int century; +static struct of_device_id __initdata rtc_match[] = { + { + .name = "rtc", + .compatible = "m5819", + }, + { + .name = "rtc", + .compatible = "isa-m5819p", + }, + { + .name = "rtc", + .compatible = "isa-m5823p", + }, + { + .name = "rtc", + .compatible = "ds1287", + }, + {}, +}; - /* BQ4802 RTC chip. */ +static struct of_platform_driver rtc_driver = { + .match_table = rtc_match, + .probe = rtc_probe, + .driver = { + .name = "rtc", + }, +}; - writeb(val | 0x08, bregs + 0x0e); +static struct platform_device rtc_bq4802_device = { + .name = "rtc-bq4802", + .id = -1, + .num_resources = 1, +}; - sec = readb(bregs + 0x00); - min = readb(bregs + 0x02); - hour = readb(bregs + 0x04); - day = readb(bregs + 0x06); - mon = readb(bregs + 0x09); - year = readb(bregs + 0x0a); - century = readb(bregs + 0x0f); +static int __devinit bq4802_probe(struct of_device *op, const struct of_device_id *match) +{ - writeb(val, bregs + 0x0e); + printk(KERN_INFO "%s: BQ4802 regs at 0x%lx\n", + op->node->full_name, op->resource[0].start); - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - BCD_TO_BIN(century); + rtc_bq4802_device.resource = &op->resource[0]; + return platform_device_register(&rtc_bq4802_device); +} - year += (century * 100); - } else { - /* Dallas 12887 RTC chip. */ - - do { - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - } while (sec != CMOS_READ(RTC_SECONDS)); - - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BCD_TO_BIN(sec); - BCD_TO_BIN(min); - BCD_TO_BIN(hour); - BCD_TO_BIN(day); - BCD_TO_BIN(mon); - BCD_TO_BIN(year); - } - if ((year += 1900) < 1970) - year += 100; - } +static struct of_device_id __initdata bq4802_match[] = { + { + .name = "rtc", + .compatible = "bq4802", + }, +}; - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); -} +static struct of_platform_driver bq4802_driver = { + .match_table = bq4802_match, + .probe = bq4802_probe, + .driver = { + .name = "bq4802", + }, +}; -/* davem suggests we keep this within the 4M locked kernel image */ -static u32 starfire_get_time(void) +static unsigned char mostek_read_byte(struct device *dev, u32 ofs) { - static char obp_gettod[32]; - static u32 unix_tod; + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs; + unsigned char val; - sprintf(obp_gettod, "h# %08x unix-gettod", - (unsigned int) (long) &unix_tod); - prom_feval(obp_gettod); + regs = (void __iomem *) pdev->resource[0].start; + val = readb(regs + ofs); - return unix_tod; + /* the year 0 is 1968 */ + if (ofs == pdata->offset + M48T59_YEAR) { + val += 0x68; + if ((val & 0xf) > 9) + val += 6; + } + return val; } -static int starfire_set_time(u32 val) +static void mostek_write_byte(struct device *dev, u32 ofs, u8 val) { - /* Do nothing, time is set using the service processor - * console on this platform. - */ - return 0; -} + struct platform_device *pdev = to_platform_device(dev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; + void __iomem *regs; -static u32 hypervisor_get_time(void) -{ - unsigned long ret, time; - int retries = 10000; - -retry: - ret = sun4v_tod_get(&time); - if (ret == HV_EOK) - return time; - if (ret == HV_EWOULDBLOCK) { - if (--retries > 0) { - udelay(100); - goto retry; - } - printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); - return 0; + regs = (void __iomem *) pdev->resource[0].start; + if (ofs == pdata->offset + M48T59_YEAR) { + if (val < 0x68) + val += 0x32; + else + val -= 0x68; + if ((val & 0xf) > 9) + val += 6; + if ((val & 0xf0) > 0x9A) + val += 0x60; } - printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); - return 0; + writeb(val, regs + ofs); } -static int hypervisor_set_time(u32 secs) -{ - unsigned long ret; - int retries = 10000; - -retry: - ret = sun4v_tod_set(secs); - if (ret == HV_EOK) - return 0; - if (ret == HV_EWOULDBLOCK) { - if (--retries > 0) { - udelay(100); - goto retry; - } - printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); - return -EAGAIN; - } - printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); - return -EOPNOTSUPP; -} +static struct m48t59_plat_data m48t59_data = { + .read_byte = mostek_read_byte, + .write_byte = mostek_write_byte, +}; -static int __init clock_model_matches(const char *model) -{ - if (strcmp(model, "mk48t02") && - strcmp(model, "mk48t08") && - strcmp(model, "mk48t59") && - strcmp(model, "m5819") && - strcmp(model, "m5819p") && - strcmp(model, "m5823") && - strcmp(model, "ds1287") && - strcmp(model, "bq4802")) - return 0; - - return 1; -} +static struct platform_device m48t59_rtc = { + .name = "rtc-m48t59", + .id = 0, + .num_resources = 1, + .dev = { + .platform_data = &m48t59_data, + }, +}; -static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) +static int __devinit mostek_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dp = op->node; - const char *model = of_get_property(dp, "model", NULL); - const char *compat = of_get_property(dp, "compatible", NULL); - unsigned long size, flags; - void __iomem *regs; - - if (!model) - model = compat; - - if (!model || !clock_model_matches(model)) - return -ENODEV; /* On an Enterprise system there can be multiple mostek clocks. * We should only match the one that is on the central FHC bus. @@ -720,88 +564,51 @@ static int __devinit clock_probe(struct of_device *op, const struct of_device_id strcmp(dp->parent->parent->name, "central") != 0) return -ENODEV; - size = (op->resource[0].end - op->resource[0].start) + 1; - regs = of_ioremap(&op->resource[0], 0, size, "clock"); - if (!regs) - return -ENOMEM; - -#ifdef CONFIG_PCI - if (!strcmp(model, "ds1287") || - !strcmp(model, "m5819") || - !strcmp(model, "m5819p") || - !strcmp(model, "m5823")) { - ds1287_regs = (unsigned long) regs; - } else if (!strcmp(model, "bq4802")) { - bq4802_regs = regs; - } else -#endif - if (model[5] == '0' && model[6] == '2') { - mstk48t02_regs = regs; - } else if(model[5] == '0' && model[6] == '8') { - mstk48t08_regs = regs; - mstk48t02_regs = mstk48t08_regs + MOSTEK_48T08_48T02; - } else { - mstk48t59_regs = regs; - mstk48t02_regs = mstk48t59_regs + MOSTEK_48T59_48T02; - } - - printk(KERN_INFO "%s: Clock regs at %p\n", dp->full_name, regs); - - local_irq_save(flags); - - if (mstk48t02_regs != NULL) { - /* Report a low battery voltage condition. */ - if (has_low_battery()) - prom_printf("NVRAM: Low battery voltage!\n"); - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - } - - set_system_time(); - - local_irq_restore(flags); + printk(KERN_INFO "%s: Mostek regs at 0x%lx\n", + dp->full_name, op->resource[0].start); - return 0; + m48t59_rtc.resource = &op->resource[0]; + return platform_device_register(&m48t59_rtc); } -static struct of_device_id clock_match[] = { +static struct of_device_id __initdata mostek_match[] = { { .name = "eeprom", }, - { - .name = "rtc", - }, {}, }; -static struct of_platform_driver clock_driver = { - .match_table = clock_match, - .probe = clock_probe, +static struct of_platform_driver mostek_driver = { + .match_table = mostek_match, + .probe = mostek_probe, .driver = { - .name = "clock", + .name = "mostek", }, }; +static struct platform_device rtc_sun4v_device = { + .name = "rtc-sun4v", + .id = -1, +}; + +static struct platform_device rtc_starfire_device = { + .name = "rtc-starfire", + .id = -1, +}; + static int __init clock_init(void) { - if (this_is_starfire) { - xtime.tv_sec = starfire_get_time(); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - return 0; - } - if (tlb_type == hypervisor) { - xtime.tv_sec = hypervisor_get_time(); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - return 0; - } + if (this_is_starfire) + return platform_device_register(&rtc_starfire_device); + + if (tlb_type == hypervisor) + return platform_device_register(&rtc_sun4v_device); + + (void) of_register_driver(&rtc_driver, &of_platform_bus_type); + (void) of_register_driver(&mostek_driver, &of_platform_bus_type); + (void) of_register_driver(&bq4802_driver, &of_platform_bus_type); - return of_register_driver(&clock_driver, &of_platform_bus_type); + return 0; } /* Must be after subsys_initcall() so that busses are probed. Must @@ -814,7 +621,7 @@ fs_initcall(clock_init); static unsigned long sparc64_init_timers(void) { struct device_node *dp; - unsigned long clock; + unsigned long freq; dp = of_find_node_by_path("/"); if (tlb_type == spitfire) { @@ -827,17 +634,17 @@ static unsigned long sparc64_init_timers(void) if (manuf == 0x17 && impl == 0x13) { /* Hummingbird, aka Ultra-IIe */ tick_ops = &hbtick_operations; - clock = of_getintprop_default(dp, "stick-frequency", 0); + freq = of_getintprop_default(dp, "stick-frequency", 0); } else { tick_ops = &tick_operations; - clock = local_cpu_data().clock_tick; + freq = local_cpu_data().clock_tick; } } else { tick_ops = &stick_operations; - clock = of_getintprop_default(dp, "stick-frequency", 0); + freq = of_getintprop_default(dp, "stick-frequency", 0); } - return clock; + return freq; } struct freq_table { @@ -1029,16 +836,16 @@ EXPORT_SYMBOL(udelay); void __init time_init(void) { - unsigned long clock = sparc64_init_timers(); + unsigned long freq = sparc64_init_timers(); - tb_ticks_per_usec = clock / USEC_PER_SEC; + tb_ticks_per_usec = freq / USEC_PER_SEC; timer_ticks_per_nsec_quotient = - clocksource_hz2mult(clock, SPARC64_NSEC_PER_CYC_SHIFT); + clocksource_hz2mult(freq, SPARC64_NSEC_PER_CYC_SHIFT); clocksource_tick.name = tick_ops->name; clocksource_tick.mult = - clocksource_hz2mult(clock, + clocksource_hz2mult(freq, clocksource_tick.shift); clocksource_tick.read = tick_ops->get_tick; @@ -1049,7 +856,7 @@ void __init time_init(void) sparc64_clockevent.name = tick_ops->name; - setup_clockevent_multiplier(clock); + setup_clockevent_multiplier(freq); sparc64_clockevent.max_delta_ns = clockevent_delta2ns(0x7fffffffffffffffUL, &sparc64_clockevent); @@ -1070,672 +877,8 @@ unsigned long long sched_clock(void) >> SPARC64_NSEC_PER_CYC_SHIFT; } -static int set_rtc_mmss(unsigned long nowtime) -{ - int real_seconds, real_minutes, chip_minutes; - void __iomem *mregs = mstk48t02_regs; -#ifdef CONFIG_PCI - unsigned long dregs = ds1287_regs; - void __iomem *bregs = bq4802_regs; -#else - unsigned long dregs = 0UL; - void __iomem *bregs = 0UL; -#endif - unsigned long flags; - u8 tmp; - - /* - * Not having a register set can lead to trouble. - * Also starfire doesn't have a tod clock. - */ - if (!mregs && !dregs && !bregs) - return -1; - - if (mregs) { - spin_lock_irqsave(&mostek_lock, flags); - - /* Read the current RTC minutes. */ - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); - - chip_minutes = MSTK_REG_MIN(mregs); - - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(mregs + MOSTEK_CREG, tmp); - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - chip_minutes) < 30) { - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(mregs + MOSTEK_CREG, tmp); - - MSTK_SET_REG_SEC(mregs,real_seconds); - MSTK_SET_REG_MIN(mregs,real_minutes); - - tmp = mostek_read(mregs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(mregs + MOSTEK_CREG, tmp); - - spin_unlock_irqrestore(&mostek_lock, flags); - - return 0; - } else { - spin_unlock_irqrestore(&mostek_lock, flags); - - return -1; - } - } else if (bregs) { - int retval = 0; - unsigned char val = readb(bregs + 0x0e); - - /* BQ4802 RTC chip. */ - - writeb(val | 0x08, bregs + 0x0e); - - chip_minutes = readb(bregs + 0x02); - BCD_TO_BIN(chip_minutes); - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) - real_minutes += 30; - real_minutes %= 60; - - if (abs(real_minutes - chip_minutes) < 30) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - writeb(real_seconds, bregs + 0x00); - writeb(real_minutes, bregs + 0x02); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - chip_minutes, real_minutes); - retval = -1; - } - - writeb(val, bregs + 0x0e); - - return retval; - } else { - int retval = 0; - unsigned char save_control, save_freq_select; - - /* Stolen from arch/i386/kernel/time.c, see there for - * credits and descriptive comments. - */ - spin_lock_irqsave(&rtc_lock, flags); - save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */ - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */ - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - chip_minutes = CMOS_READ(RTC_MINUTES); - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) - BCD_TO_BIN(chip_minutes); - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - chip_minutes) + 15)/30) & 1) - real_minutes += 30; - real_minutes %= 60; - - if (abs(real_minutes - chip_minutes) < 30) { - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(real_seconds); - BIN_TO_BCD(real_minutes); - } - CMOS_WRITE(real_seconds,RTC_SECONDS); - CMOS_WRITE(real_minutes,RTC_MINUTES); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - chip_minutes, real_minutes); - retval = -1; - } - - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - spin_unlock_irqrestore(&rtc_lock, flags); - - return retval; - } -} - -#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ -static unsigned char mini_rtc_status; /* bitmapped status byte. */ - -#define FEBRUARY 2 -#define STARTOFTIME 1970 -#define SECDAY 86400L -#define SECYR (SECDAY * 365) -#define leapyear(year) ((year) % 4 == 0 && \ - ((year) % 100 != 0 || (year) % 400 == 0)) -#define days_in_year(a) (leapyear(a) ? 366 : 365) -#define days_in_month(a) (month_days[(a) - 1]) - -static int month_days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; - -/* - * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) - */ -static void GregorianDay(struct rtc_time * tm) -{ - int leapsToDate; - int lastYear; - int day; - int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; - - lastYear = tm->tm_year - 1; - - /* - * Number of leap corrections to apply up to end of last year - */ - leapsToDate = lastYear / 4 - lastYear / 100 + lastYear / 400; - - /* - * This year is a leap year if it is divisible by 4 except when it is - * divisible by 100 unless it is divisible by 400 - * - * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 was - */ - day = tm->tm_mon > 2 && leapyear(tm->tm_year); - - day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + - tm->tm_mday; - - tm->tm_wday = day % 7; -} - -static void to_tm(int tim, struct rtc_time *tm) -{ - register int i; - register long hms, day; - - day = tim / SECDAY; - hms = tim % SECDAY; - - /* Hours, minutes, seconds are easy */ - tm->tm_hour = hms / 3600; - tm->tm_min = (hms % 3600) / 60; - tm->tm_sec = (hms % 3600) % 60; - - /* Number of years in days */ - for (i = STARTOFTIME; day >= days_in_year(i); i++) - day -= days_in_year(i); - tm->tm_year = i; - - /* Number of months in days left */ - if (leapyear(tm->tm_year)) - days_in_month(FEBRUARY) = 29; - for (i = 1; day >= days_in_month(i); i++) - day -= days_in_month(i); - days_in_month(FEBRUARY) = 28; - tm->tm_mon = i; - - /* Days are what is left over (+1) from all that. */ - tm->tm_mday = day + 1; - - /* - * Determine the day of week - */ - GregorianDay(tm); -} - -/* Both Starfire and SUN4V give us seconds since Jan 1st, 1970, - * aka Unix time. So we have to convert to/from rtc_time. - */ -static void starfire_get_rtc_time(struct rtc_time *time) -{ - u32 seconds = starfire_get_time(); - - to_tm(seconds, time); - time->tm_year -= 1900; - time->tm_mon -= 1; -} - -static int starfire_set_rtc_time(struct rtc_time *time) -{ - u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, - time->tm_mday, time->tm_hour, - time->tm_min, time->tm_sec); - - return starfire_set_time(seconds); -} - -static void hypervisor_get_rtc_time(struct rtc_time *time) -{ - u32 seconds = hypervisor_get_time(); - - to_tm(seconds, time); - time->tm_year -= 1900; - time->tm_mon -= 1; -} - -static int hypervisor_set_rtc_time(struct rtc_time *time) -{ - u32 seconds = mktime(time->tm_year + 1900, time->tm_mon + 1, - time->tm_mday, time->tm_hour, - time->tm_min, time->tm_sec); - - return hypervisor_set_time(seconds); -} - -#ifdef CONFIG_PCI -static void bq4802_get_rtc_time(struct rtc_time *time) -{ - unsigned char val = readb(bq4802_regs + 0x0e); - unsigned int century; - - writeb(val | 0x08, bq4802_regs + 0x0e); - - time->tm_sec = readb(bq4802_regs + 0x00); - time->tm_min = readb(bq4802_regs + 0x02); - time->tm_hour = readb(bq4802_regs + 0x04); - time->tm_mday = readb(bq4802_regs + 0x06); - time->tm_mon = readb(bq4802_regs + 0x09); - time->tm_year = readb(bq4802_regs + 0x0a); - time->tm_wday = readb(bq4802_regs + 0x08); - century = readb(bq4802_regs + 0x0f); - - writeb(val, bq4802_regs + 0x0e); - - BCD_TO_BIN(time->tm_sec); - BCD_TO_BIN(time->tm_min); - BCD_TO_BIN(time->tm_hour); - BCD_TO_BIN(time->tm_mday); - BCD_TO_BIN(time->tm_mon); - BCD_TO_BIN(time->tm_year); - BCD_TO_BIN(time->tm_wday); - BCD_TO_BIN(century); - - time->tm_year += (century * 100); - time->tm_year -= 1900; - - time->tm_mon--; -} - -static int bq4802_set_rtc_time(struct rtc_time *time) -{ - unsigned char val = readb(bq4802_regs + 0x0e); - unsigned char sec, min, hrs, day, mon, yrs, century; - unsigned int year; - - year = time->tm_year + 1900; - century = year / 100; - yrs = year % 100; - - mon = time->tm_mon + 1; /* tm_mon starts at zero */ - day = time->tm_mday; - hrs = time->tm_hour; - min = time->tm_min; - sec = time->tm_sec; - - BIN_TO_BCD(sec); - BIN_TO_BCD(min); - BIN_TO_BCD(hrs); - BIN_TO_BCD(day); - BIN_TO_BCD(mon); - BIN_TO_BCD(yrs); - BIN_TO_BCD(century); - - writeb(val | 0x08, bq4802_regs + 0x0e); - - writeb(sec, bq4802_regs + 0x00); - writeb(min, bq4802_regs + 0x02); - writeb(hrs, bq4802_regs + 0x04); - writeb(day, bq4802_regs + 0x06); - writeb(mon, bq4802_regs + 0x09); - writeb(yrs, bq4802_regs + 0x0a); - writeb(century, bq4802_regs + 0x0f); - - writeb(val, bq4802_regs + 0x0e); - - return 0; -} - -static void cmos_get_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned char ctrl; - - rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); - rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); - rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); - rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); - rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); - rtc_tm->tm_year = CMOS_READ(RTC_YEAR); - rtc_tm->tm_wday = CMOS_READ(RTC_DAY_OF_WEEK); - - ctrl = CMOS_READ(RTC_CONTROL); - if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BCD_TO_BIN(rtc_tm->tm_sec); - BCD_TO_BIN(rtc_tm->tm_min); - BCD_TO_BIN(rtc_tm->tm_hour); - BCD_TO_BIN(rtc_tm->tm_mday); - BCD_TO_BIN(rtc_tm->tm_mon); - BCD_TO_BIN(rtc_tm->tm_year); - BCD_TO_BIN(rtc_tm->tm_wday); - } - - if (rtc_tm->tm_year <= 69) - rtc_tm->tm_year += 100; - - rtc_tm->tm_mon--; -} - -static int cmos_set_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned char mon, day, hrs, min, sec; - unsigned char save_control, save_freq_select; - unsigned int yrs; - - yrs = rtc_tm->tm_year; - mon = rtc_tm->tm_mon + 1; - day = rtc_tm->tm_mday; - hrs = rtc_tm->tm_hour; - min = rtc_tm->tm_min; - sec = rtc_tm->tm_sec; - - if (yrs >= 100) - yrs -= 100; - - if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(sec); - BIN_TO_BCD(min); - BIN_TO_BCD(hrs); - BIN_TO_BCD(day); - BIN_TO_BCD(mon); - BIN_TO_BCD(yrs); - } - - save_control = CMOS_READ(RTC_CONTROL); - CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - save_freq_select = CMOS_READ(RTC_FREQ_SELECT); - CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - CMOS_WRITE(yrs, RTC_YEAR); - CMOS_WRITE(mon, RTC_MONTH); - CMOS_WRITE(day, RTC_DAY_OF_MONTH); - CMOS_WRITE(hrs, RTC_HOURS); - CMOS_WRITE(min, RTC_MINUTES); - CMOS_WRITE(sec, RTC_SECONDS); - - CMOS_WRITE(save_control, RTC_CONTROL); - CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); - - return 0; -} -#endif /* CONFIG_PCI */ - -static void mostek_get_rtc_time(struct rtc_time *rtc_tm) -{ - void __iomem *regs = mstk48t02_regs; - u8 tmp; - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); - - rtc_tm->tm_sec = MSTK_REG_SEC(regs); - rtc_tm->tm_min = MSTK_REG_MIN(regs); - rtc_tm->tm_hour = MSTK_REG_HOUR(regs); - rtc_tm->tm_mday = MSTK_REG_DOM(regs); - rtc_tm->tm_mon = MSTK_REG_MONTH(regs); - rtc_tm->tm_year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) ); - rtc_tm->tm_wday = MSTK_REG_DOW(regs); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - rtc_tm->tm_mon--; - rtc_tm->tm_wday--; - rtc_tm->tm_year -= 1900; -} - -static int mostek_set_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned char mon, day, hrs, min, sec, wday; - void __iomem *regs = mstk48t02_regs; - unsigned int yrs; - u8 tmp; - - yrs = rtc_tm->tm_year + 1900; - mon = rtc_tm->tm_mon + 1; - day = rtc_tm->tm_mday; - wday = rtc_tm->tm_wday + 1; - hrs = rtc_tm->tm_hour; - min = rtc_tm->tm_min; - sec = rtc_tm->tm_sec; - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - MSTK_SET_REG_SEC(regs, sec); - MSTK_SET_REG_MIN(regs, min); - MSTK_SET_REG_HOUR(regs, hrs); - MSTK_SET_REG_DOW(regs, wday); - MSTK_SET_REG_DOM(regs, day); - MSTK_SET_REG_MONTH(regs, mon); - MSTK_SET_REG_YEAR(regs, yrs - MSTK_YEAR_ZERO); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); - - return 0; -} - -struct mini_rtc_ops { - void (*get_rtc_time)(struct rtc_time *); - int (*set_rtc_time)(struct rtc_time *); -}; - -static struct mini_rtc_ops starfire_rtc_ops = { - .get_rtc_time = starfire_get_rtc_time, - .set_rtc_time = starfire_set_rtc_time, -}; - -static struct mini_rtc_ops hypervisor_rtc_ops = { - .get_rtc_time = hypervisor_get_rtc_time, - .set_rtc_time = hypervisor_set_rtc_time, -}; - -#ifdef CONFIG_PCI -static struct mini_rtc_ops bq4802_rtc_ops = { - .get_rtc_time = bq4802_get_rtc_time, - .set_rtc_time = bq4802_set_rtc_time, -}; - -static struct mini_rtc_ops cmos_rtc_ops = { - .get_rtc_time = cmos_get_rtc_time, - .set_rtc_time = cmos_set_rtc_time, -}; -#endif /* CONFIG_PCI */ - -static struct mini_rtc_ops mostek_rtc_ops = { - .get_rtc_time = mostek_get_rtc_time, - .set_rtc_time = mostek_set_rtc_time, -}; - -static struct mini_rtc_ops *mini_rtc_ops; - -static inline void mini_get_rtc_time(struct rtc_time *time) -{ - unsigned long flags; - - spin_lock_irqsave(&rtc_lock, flags); - mini_rtc_ops->get_rtc_time(time); - spin_unlock_irqrestore(&rtc_lock, flags); -} - -static inline int mini_set_rtc_time(struct rtc_time *time) -{ - unsigned long flags; - int err; - - spin_lock_irqsave(&rtc_lock, flags); - err = mini_rtc_ops->set_rtc_time(time); - spin_unlock_irqrestore(&rtc_lock, flags); - - return err; -} - -static int mini_rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct rtc_time wtime; - void __user *argp = (void __user *)arg; - - switch (cmd) { - - case RTC_PLL_GET: - return -EINVAL; - - case RTC_PLL_SET: - return -EINVAL; - - case RTC_UIE_OFF: /* disable ints from RTC updates. */ - return 0; - - case RTC_UIE_ON: /* enable ints for RTC updates. */ - return -EINVAL; - - case RTC_RD_TIME: /* Read the time/date from RTC */ - /* this doesn't get week-day, who cares */ - memset(&wtime, 0, sizeof(wtime)); - mini_get_rtc_time(&wtime); - - return copy_to_user(argp, &wtime, sizeof(wtime)) ? -EFAULT : 0; - - case RTC_SET_TIME: /* Set the RTC */ - { - int year, days; - - if (!capable(CAP_SYS_TIME)) - return -EACCES; - - if (copy_from_user(&wtime, argp, sizeof(wtime))) - return -EFAULT; - - year = wtime.tm_year + 1900; - days = month_days[wtime.tm_mon] + - ((wtime.tm_mon == 1) && leapyear(year)); - - if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || - (wtime.tm_mday < 1)) - return -EINVAL; - - if (wtime.tm_mday < 0 || wtime.tm_mday > days) - return -EINVAL; - - if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 || - wtime.tm_min < 0 || wtime.tm_min >= 60 || - wtime.tm_sec < 0 || wtime.tm_sec >= 60) - return -EINVAL; - - return mini_set_rtc_time(&wtime); - } - } - - return -EINVAL; -} - -static int mini_rtc_open(struct inode *inode, struct file *file) -{ - lock_kernel(); - if (mini_rtc_status & RTC_IS_OPEN) { - unlock_kernel(); - return -EBUSY; - } - - mini_rtc_status |= RTC_IS_OPEN; - unlock_kernel(); - - return 0; -} - -static int mini_rtc_release(struct inode *inode, struct file *file) -{ - mini_rtc_status &= ~RTC_IS_OPEN; - return 0; -} - - -static const struct file_operations mini_rtc_fops = { - .owner = THIS_MODULE, - .ioctl = mini_rtc_ioctl, - .open = mini_rtc_open, - .release = mini_rtc_release, -}; - -static struct miscdevice rtc_mini_dev = -{ - .minor = RTC_MINOR, - .name = "rtc", - .fops = &mini_rtc_fops, -}; - -static int __init rtc_mini_init(void) -{ - int retval; - - if (tlb_type == hypervisor) - mini_rtc_ops = &hypervisor_rtc_ops; - else if (this_is_starfire) - mini_rtc_ops = &starfire_rtc_ops; -#ifdef CONFIG_PCI - else if (bq4802_regs) - mini_rtc_ops = &bq4802_rtc_ops; - else if (ds1287_regs) - mini_rtc_ops = &cmos_rtc_ops; -#endif /* CONFIG_PCI */ - else if (mstk48t02_regs) - mini_rtc_ops = &mostek_rtc_ops; - else - return -ENODEV; - - printk(KERN_INFO "Mini RTC Driver\n"); - - retval = misc_register(&rtc_mini_dev); - if (retval < 0) - return retval; - - return 0; -} - -static void __exit rtc_mini_exit(void) -{ - misc_deregister(&rtc_mini_dev); -} - int __devinit read_current_timer(unsigned long *timer_val) { *timer_val = tick_ops->get_tick(); return 0; } - -module_init(rtc_mini_init); -module_exit(rtc_mini_exit); diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index c824df13f58..81ccd22e78d 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -38,6 +38,7 @@ #include <asm/timer.h> #include <asm/head.h> #include <asm/prom.h> +#include <asm/memctrl.h> #include "entry.h" #include "kstack.h" @@ -129,6 +130,56 @@ void do_BUG(const char *file, int line) } #endif +static DEFINE_SPINLOCK(dimm_handler_lock); +static dimm_printer_t dimm_handler; + +static int sprintf_dimm(int synd_code, unsigned long paddr, char *buf, int buflen) +{ + unsigned long flags; + int ret = -ENODEV; + + spin_lock_irqsave(&dimm_handler_lock, flags); + if (dimm_handler) { + ret = dimm_handler(synd_code, paddr, buf, buflen); + } else if (tlb_type == spitfire) { + if (prom_getunumber(synd_code, paddr, buf, buflen) == -1) + ret = -EINVAL; + else + ret = 0; + } else + ret = -ENODEV; + spin_unlock_irqrestore(&dimm_handler_lock, flags); + + return ret; +} + +int register_dimm_printer(dimm_printer_t func) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dimm_handler_lock, flags); + if (!dimm_handler) + dimm_handler = func; + else + ret = -EEXIST; + spin_unlock_irqrestore(&dimm_handler_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(register_dimm_printer); + +void unregister_dimm_printer(dimm_printer_t func) +{ + unsigned long flags; + + spin_lock_irqsave(&dimm_handler_lock, flags); + if (dimm_handler == func) + dimm_handler = NULL; + spin_unlock_irqrestore(&dimm_handler_lock, flags); +} +EXPORT_SYMBOL_GPL(unregister_dimm_printer); + void spitfire_insn_access_exception(struct pt_regs *regs, unsigned long sfsr, unsigned long sfar) { siginfo_t info; @@ -291,10 +342,7 @@ void sun4v_data_access_exception_tl1(struct pt_regs *regs, unsigned long addr, u } #ifdef CONFIG_PCI -/* This is really pathetic... */ -extern volatile int pci_poke_in_progress; -extern volatile int pci_poke_cpu; -extern volatile int pci_poke_faulted; +#include "pci_impl.h" #endif /* When access exceptions happen, we must do this. */ @@ -376,8 +424,7 @@ static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, un if (udbl & bit) { scode = ecc_syndrome_table[udbl & 0xff]; - if (prom_getunumber(scode, afar, - memmod_str, sizeof(memmod_str)) == -1) + if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0) p = syndrome_unknown; else p = memmod_str; @@ -388,8 +435,7 @@ static void spitfire_log_udb_syndrome(unsigned long afar, unsigned long udbh, un if (udbh & bit) { scode = ecc_syndrome_table[udbh & 0xff]; - if (prom_getunumber(scode, afar, - memmod_str, sizeof(memmod_str)) == -1) + if (sprintf_dimm(scode, afar, memmod_str, sizeof(memmod_str)) < 0) p = syndrome_unknown; else p = memmod_str; @@ -1062,8 +1108,6 @@ static const char *cheetah_get_string(unsigned long bit) return "???"; } -extern int chmc_getunumber(int, unsigned long, char *, int); - static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *info, unsigned long afsr, unsigned long afar, int recoverable) { @@ -1105,7 +1149,7 @@ static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *in syndrome = (afsr & CHAFSR_E_SYNDROME) >> CHAFSR_E_SYNDROME_SHIFT; syndrome = cheetah_ecc_syntab[syndrome]; - ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum)); + ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum)); if (ret != -1) printk("%s" "ERROR(%d): AFAR E-syndrome [%s]\n", (recoverable ? KERN_WARNING : KERN_CRIT), @@ -1116,7 +1160,7 @@ static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *in syndrome = (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT; syndrome = cheetah_mtag_syntab[syndrome]; - ret = chmc_getunumber(syndrome, afar, unum, sizeof(unum)); + ret = sprintf_dimm(syndrome, afar, unum, sizeof(unum)); if (ret != -1) printk("%s" "ERROR(%d): AFAR M-syndrome [%s]\n", (recoverable ? KERN_WARNING : KERN_CRIT), @@ -2224,7 +2268,6 @@ void die_if_kernel(char *str, struct pt_regs *regs) extern int handle_popc(u32 insn, struct pt_regs *regs); extern int handle_ldf_stq(u32 insn, struct pt_regs *regs); -extern int vis_emul(struct pt_regs *, unsigned int); void do_illegal_instruction(struct pt_regs *regs) { diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index a490077891a..92b1f8ec01d 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c @@ -152,7 +152,7 @@ show_pciobppath_attr(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_pciobppath_attr, NULL); -struct device_node *cdev_node; +static struct device_node *cdev_node; static struct vio_dev *root_vdev; static u64 cdev_cfg_handle; @@ -371,9 +371,9 @@ static struct mdesc_notifier_client vio_ds_notifier = { .node_name = "domain-services-port", }; -const char *channel_devices_node = "channel-devices"; -const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; -const char *cfg_handle_prop = "cfg-handle"; +static const char *channel_devices_node = "channel-devices"; +static const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; +static const char *cfg_handle_prop = "cfg-handle"; static int __init vio_init(void) { diff --git a/arch/sparc64/kernel/visemul.c b/arch/sparc64/kernel/visemul.c index c3fd64706b5..9e05cb5cb85 100644 --- a/arch/sparc64/kernel/visemul.c +++ b/arch/sparc64/kernel/visemul.c @@ -243,7 +243,7 @@ static inline unsigned int *fps_regaddr(struct fpustate *f, struct edge_tab { u16 left, right; }; -struct edge_tab edge8_tab[8] = { +static struct edge_tab edge8_tab[8] = { { 0xff, 0x80 }, { 0x7f, 0xc0 }, { 0x3f, 0xe0 }, @@ -253,7 +253,7 @@ struct edge_tab edge8_tab[8] = { { 0x03, 0xfe }, { 0x01, 0xff }, }; -struct edge_tab edge8_tab_l[8] = { +static struct edge_tab edge8_tab_l[8] = { { 0xff, 0x01 }, { 0xfe, 0x03 }, { 0xfc, 0x07 }, @@ -263,23 +263,23 @@ struct edge_tab edge8_tab_l[8] = { { 0xc0, 0x7f }, { 0x80, 0xff }, }; -struct edge_tab edge16_tab[4] = { +static struct edge_tab edge16_tab[4] = { { 0xf, 0x8 }, { 0x7, 0xc }, { 0x3, 0xe }, { 0x1, 0xf }, }; -struct edge_tab edge16_tab_l[4] = { +static struct edge_tab edge16_tab_l[4] = { { 0xf, 0x1 }, { 0xe, 0x3 }, { 0xc, 0x7 }, { 0x8, 0xf }, }; -struct edge_tab edge32_tab[2] = { +static struct edge_tab edge32_tab[2] = { { 0x3, 0x2 }, { 0x1, 0x3 }, }; -struct edge_tab edge32_tab_l[2] = { +static struct edge_tab edge32_tab_l[2] = { { 0x3, 0x1 }, { 0x2, 0x3 }, }; diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index ea7d7ae76bc..a9e474bf638 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -51,43 +51,6 @@ static inline int notify_page_fault(struct pt_regs *regs) } #endif -/* - * To debug kernel to catch accesses to certain virtual/physical addresses. - * Mode = 0 selects physical watchpoints, mode = 1 selects virtual watchpoints. - * flags = VM_READ watches memread accesses, flags = VM_WRITE watches memwrite accesses. - * Caller passes in a 64bit aligned addr, with mask set to the bytes that need to be - * watched. This is only useful on a single cpu machine for now. After the watchpoint - * is detected, the process causing it will be killed, thus preventing an infinite loop. - */ -void set_brkpt(unsigned long addr, unsigned char mask, int flags, int mode) -{ - unsigned long lsubits; - - __asm__ __volatile__("ldxa [%%g0] %1, %0" - : "=r" (lsubits) - : "i" (ASI_LSU_CONTROL)); - lsubits &= ~(LSU_CONTROL_PM | LSU_CONTROL_VM | - LSU_CONTROL_PR | LSU_CONTROL_VR | - LSU_CONTROL_PW | LSU_CONTROL_VW); - - __asm__ __volatile__("stxa %0, [%1] %2\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (addr), "r" (mode ? VIRT_WATCHPOINT : PHYS_WATCHPOINT), - "i" (ASI_DMMU)); - - lsubits |= ((unsigned long)mask << (mode ? 25 : 33)); - if (flags & VM_READ) - lsubits |= (mode ? LSU_CONTROL_VR : LSU_CONTROL_PR); - if (flags & VM_WRITE) - lsubits |= (mode ? LSU_CONTROL_VW : LSU_CONTROL_PW); - __asm__ __volatile__("stxa %0, [%%g0] %1\n\t" - "membar #Sync" - : /* no outputs */ - : "r" (lsubits), "i" (ASI_LSU_CONTROL) - : "memory"); -} - static void __kprobes unhandled_fault(unsigned long address, struct task_struct *tsk, struct pt_regs *regs) diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index a41df7bef03..3c10daf8fc0 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -46,15 +46,11 @@ #include <asm/tsb.h> #include <asm/hypervisor.h> #include <asm/prom.h> -#include <asm/sstate.h> #include <asm/mdesc.h> #include <asm/cpudata.h> #include <asm/irq.h> -#define MAX_PHYS_ADDRESS (1UL << 42UL) -#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL) -#define KPTE_BITMAP_BYTES \ - ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8) +#include "init.h" unsigned long kern_linear_pte_xor[2] __read_mostly; @@ -416,17 +412,9 @@ void mmu_info(struct seq_file *m) #endif /* CONFIG_DEBUG_DCFLUSH */ } -struct linux_prom_translation { - unsigned long virt; - unsigned long size; - unsigned long data; -}; - -/* Exported for kernel TLB miss handling in ktlb.S */ struct linux_prom_translation prom_trans[512] __read_mostly; unsigned int prom_trans_ents __read_mostly; -/* Exported for SMP bootup purposes. */ unsigned long kern_locked_tte_data; /* The obp translations are saved based on 8k pagesize, since obp can @@ -938,6 +926,10 @@ int of_node_to_nid(struct device_node *dp) int count, nid; u64 grp; + /* This is the right thing to do on currently supported + * SUN4U NUMA platforms as well, as the PCI controller does + * not sit behind any particular memory controller. + */ if (!mlgroups) return -1; @@ -1206,8 +1198,44 @@ out: return err; } +static int __init numa_parse_jbus(void) +{ + unsigned long cpu, index; + + /* NUMA node id is encoded in bits 36 and higher, and there is + * a 1-to-1 mapping from CPU ID to NUMA node ID. + */ + index = 0; + for_each_present_cpu(cpu) { + numa_cpu_lookup_table[cpu] = index; + numa_cpumask_lookup_table[index] = cpumask_of_cpu(cpu); + node_masks[index].mask = ~((1UL << 36UL) - 1UL); + node_masks[index].val = cpu << 36UL; + + index++; + } + num_node_masks = index; + + add_node_ranges(); + + for (index = 0; index < num_node_masks; index++) { + allocate_node_data(index); + node_set_online(index); + } + + return 0; +} + static int __init numa_parse_sun4u(void) { + if (tlb_type == cheetah || tlb_type == cheetah_plus) { + unsigned long ver; + + __asm__ ("rdpr %%ver, %0" : "=r" (ver)); + if ((ver >> 32UL) == __JALAPENO_ID || + (ver >> 32UL) == __SERRANO_ID) + return numa_parse_jbus(); + } return -1; } @@ -1633,8 +1661,6 @@ void __cpuinit sun4v_ktsb_register(void) /* paging_init() sets up the page tables */ -extern void central_probe(void); - static unsigned long last_valid_pfn; pgd_t swapper_pg_dir[2048]; @@ -1679,8 +1705,6 @@ void __init paging_init(void) kern_base = (prom_boot_mapping_phys_low >> 22UL) << 22UL; kern_size = (unsigned long)&_end - (unsigned long)KERNBASE; - sstate_booting(); - /* Invalidate both kernel TSBs. */ memset(swapper_tsb, 0x40, sizeof(swapper_tsb)); #ifndef CONFIG_DEBUG_PAGEALLOC @@ -1803,9 +1827,6 @@ void __init paging_init(void) } printk("Booting Linux...\n"); - - central_probe(); - cpu_probe(); } int __init page_in_phys_avail(unsigned long paddr) @@ -2032,7 +2053,6 @@ pgprot_t PAGE_COPY __read_mostly; pgprot_t PAGE_SHARED __read_mostly; EXPORT_SYMBOL(PAGE_SHARED); -pgprot_t PAGE_EXEC __read_mostly; unsigned long pg_iobits __read_mostly; unsigned long _PAGE_IE __read_mostly; @@ -2045,14 +2065,6 @@ unsigned long _PAGE_CACHE __read_mostly; EXPORT_SYMBOL(_PAGE_CACHE); #ifdef CONFIG_SPARSEMEM_VMEMMAP - -#define VMEMMAP_CHUNK_SHIFT 22 -#define VMEMMAP_CHUNK (1UL << VMEMMAP_CHUNK_SHIFT) -#define VMEMMAP_CHUNK_MASK ~(VMEMMAP_CHUNK - 1UL) -#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK) - -#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \ - sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT) unsigned long vmemmap_table[VMEMMAP_SIZE]; int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) @@ -2136,7 +2148,6 @@ static void __init sun4u_pgprot_init(void) _PAGE_CACHE_4U | _PAGE_P_4U | __ACCESS_BITS_4U | __DIRTY_BITS_4U | _PAGE_EXEC_4U | _PAGE_L_4U); - PAGE_EXEC = __pgprot(_PAGE_EXEC_4U); _PAGE_IE = _PAGE_IE_4U; _PAGE_E = _PAGE_E_4U; @@ -2147,10 +2158,10 @@ static void __init sun4u_pgprot_init(void) #ifdef CONFIG_DEBUG_PAGEALLOC kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4U) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #else kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4U) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #endif kern_linear_pte_xor[0] |= (_PAGE_CP_4U | _PAGE_CV_4U | _PAGE_P_4U | _PAGE_W_4U); @@ -2188,7 +2199,6 @@ static void __init sun4v_pgprot_init(void) __ACCESS_BITS_4V | __DIRTY_BITS_4V | _PAGE_EXEC_4V); PAGE_KERNEL_LOCKED = PAGE_KERNEL; - PAGE_EXEC = __pgprot(_PAGE_EXEC_4V); _PAGE_IE = _PAGE_IE_4V; _PAGE_E = _PAGE_E_4V; @@ -2196,20 +2206,20 @@ static void __init sun4v_pgprot_init(void) #ifdef CONFIG_DEBUG_PAGEALLOC kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #else kern_linear_pte_xor[0] = (_PAGE_VALID | _PAGE_SZ4MB_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #endif kern_linear_pte_xor[0] |= (_PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V | _PAGE_W_4V); #ifdef CONFIG_DEBUG_PAGEALLOC kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZBITS_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #else kern_linear_pte_xor[1] = (_PAGE_VALID | _PAGE_SZ256MB_4V) ^ - 0xfffff80000000000; + 0xfffff80000000000UL; #endif kern_linear_pte_xor[1] |= (_PAGE_CP_4V | _PAGE_CV_4V | _PAGE_P_4V | _PAGE_W_4V); diff --git a/arch/sparc64/mm/init.h b/arch/sparc64/mm/init.h new file mode 100644 index 00000000000..16063870a48 --- /dev/null +++ b/arch/sparc64/mm/init.h @@ -0,0 +1,49 @@ +#ifndef _SPARC64_MM_INIT_H +#define _SPARC64_MM_INIT_H + +/* Most of the symbols in this file are defined in init.c and + * marked non-static so that assembler code can get at them. + */ + +#define MAX_PHYS_ADDRESS (1UL << 42UL) +#define KPTE_BITMAP_CHUNK_SZ (256UL * 1024UL * 1024UL) +#define KPTE_BITMAP_BYTES \ + ((MAX_PHYS_ADDRESS / KPTE_BITMAP_CHUNK_SZ) / 8) + +extern unsigned long kern_linear_pte_xor[2]; +extern unsigned long kpte_linear_bitmap[KPTE_BITMAP_BYTES / sizeof(unsigned long)]; +extern unsigned int sparc64_highest_unlocked_tlb_ent; +extern unsigned long sparc64_kern_pri_context; +extern unsigned long sparc64_kern_pri_nuc_bits; +extern unsigned long sparc64_kern_sec_context; +extern void mmu_info(struct seq_file *m); + +struct linux_prom_translation { + unsigned long virt; + unsigned long size; + unsigned long data; +}; + +/* Exported for kernel TLB miss handling in ktlb.S */ +extern struct linux_prom_translation prom_trans[512]; +extern unsigned int prom_trans_ents; + +/* Exported for SMP bootup purposes. */ +extern unsigned long kern_locked_tte_data; + +extern void prom_world(int enter); + +extern void free_initmem(void); + +#ifdef CONFIG_SPARSEMEM_VMEMMAP +#define VMEMMAP_CHUNK_SHIFT 22 +#define VMEMMAP_CHUNK (1UL << VMEMMAP_CHUNK_SHIFT) +#define VMEMMAP_CHUNK_MASK ~(VMEMMAP_CHUNK - 1UL) +#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK) + +#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \ + sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT) +extern unsigned long vmemmap_table[VMEMMAP_SIZE]; +#endif + +#endif /* _SPARC64_MM_INIT_H */ diff --git a/arch/sparc64/mm/tlb.c b/arch/sparc64/mm/tlb.c index ae24919cba7..d8f21e24a82 100644 --- a/arch/sparc64/mm/tlb.c +++ b/arch/sparc64/mm/tlb.c @@ -19,7 +19,7 @@ /* Heavily inspired by the ppc64 code. */ -DEFINE_PER_CPU(struct mmu_gather, mmu_gathers) = { 0, }; +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); void flush_tlb_pending(void) { diff --git a/arch/um/Kconfig.i386 b/arch/um/Kconfig.i386 index e09edfa560d..1f57c113df6 100644 --- a/arch/um/Kconfig.i386 +++ b/arch/um/Kconfig.i386 @@ -9,8 +9,9 @@ config UML_X86 default y config X86_32 - bool - default y + bool + default y + select HAVE_AOUT config RWSEM_XCHGADD_ALGORITHM def_bool y @@ -42,6 +43,3 @@ config ARCH_REUSE_HOST_VSYSCALL_AREA config GENERIC_HWEIGHT bool default y - -config ARCH_SUPPORTS_AOUT - def_bool y diff --git a/arch/um/Kconfig.x86_64 b/arch/um/Kconfig.x86_64 index 5696e7b374b..40b3407cfe1 100644 --- a/arch/um/Kconfig.x86_64 +++ b/arch/um/Kconfig.x86_64 @@ -37,6 +37,3 @@ config SMP_BROKEN config GENERIC_HWEIGHT bool default y - -config ARCH_SUPPORTS_AOUT - def_bool y diff --git a/arch/um/drivers/line.c b/arch/um/drivers/line.c index d741f35d7b3..14a102e877d 100644 --- a/arch/um/drivers/line.c +++ b/arch/um/drivers/line.c @@ -275,6 +275,8 @@ int line_ioctl(struct tty_struct *tty, struct file * file, case TIOCGLTC: case TIOCSLTC: #endif + /* Note: these are out of date as we now have TCGETS2 etc but this + whole lot should probably go away */ case TCGETS: case TCSETSF: case TCSETSW: diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 44d4f2130d0..f65c2744d57 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -18,6 +18,7 @@ config X86_64 ### Arch settings config X86 def_bool y + select HAVE_AOUT if X86_32 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_IDE select HAVE_OPROFILE @@ -152,9 +153,6 @@ config AUDIT_ARCH bool default X86_64 -config ARCH_SUPPORTS_AOUT - def_bool y - config ARCH_SUPPORTS_OPTIMIZED_INLINING def_bool y @@ -778,23 +776,45 @@ config X86_REBOOTFIXUPS Say N otherwise. config MICROCODE - tristate "/dev/cpu/microcode - Intel IA32 CPU microcode support" + tristate "/dev/cpu/microcode - microcode support" select FW_LOADER ---help--- If you say Y here, you will be able to update the microcode on - Intel processors in the IA32 family, e.g. Pentium Pro, Pentium II, - Pentium III, Pentium 4, Xeon etc. You will obviously need the - actual microcode binary data itself which is not shipped with the - Linux kernel. + certain Intel and AMD processors. The Intel support is for the + IA32 family, e.g. Pentium Pro, Pentium II, Pentium III, + Pentium 4, Xeon etc. The AMD support is for family 0x10 and + 0x11 processors, e.g. Opteron, Phenom and Turion 64 Ultra. + You will obviously need the actual microcode binary data itself + which is not shipped with the Linux kernel. - For latest news and information on obtaining all the required - ingredients for this driver, check: - <http://www.urbanmyth.org/microcode/>. + This option selects the general module only, you need to select + at least one vendor specific module as well. To compile this driver as a module, choose M here: the module will be called microcode. -config MICROCODE_OLD_INTERFACE +config MICROCODE_INTEL + bool "Intel microcode patch loading support" + depends on MICROCODE + default MICROCODE + select FW_LOADER + --help--- + This options enables microcode patch loading support for Intel + processors. + + For latest news and information on obtaining all the required + Intel ingredients for this driver, check: + <http://www.urbanmyth.org/microcode/>. + +config MICROCODE_AMD + bool "AMD microcode patch loading support" + depends on MICROCODE + select FW_LOADER + --help--- + If you select this option, microcode patch loading support for AMD + processors will be enabled. + + config MICROCODE_OLD_INTERFACE def_bool y depends on MICROCODE @@ -1061,6 +1081,56 @@ config HIGHPTE low memory. Setting this option will put user-space page table entries in high memory. +config X86_CHECK_BIOS_CORRUPTION + bool "Check for low memory corruption" + help + Periodically check for memory corruption in low memory, which + is suspected to be caused by BIOS. Even when enabled in the + configuration, it is disabled at runtime. Enable it by + setting "memory_corruption_check=1" on the kernel command + line. By default it scans the low 64k of memory every 60 + seconds; see the memory_corruption_check_size and + memory_corruption_check_period parameters in + Documentation/kernel-parameters.txt to adjust this. + + When enabled with the default parameters, this option has + almost no overhead, as it reserves a relatively small amount + of memory and scans it infrequently. It both detects corruption + and prevents it from affecting the running system. + + It is, however, intended as a diagnostic tool; if repeatable + BIOS-originated corruption always affects the same memory, + you can use memmap= to prevent the kernel from using that + memory. + +config X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK + bool "Set the default setting of memory_corruption_check" + depends on X86_CHECK_BIOS_CORRUPTION + default y + help + Set whether the default state of memory_corruption_check is + on or off. + +config X86_RESERVE_LOW_64K + bool "Reserve low 64K of RAM on AMI/Phoenix BIOSen" + default y + help + Reserve the first 64K of physical RAM on BIOSes that are known + to potentially corrupt that memory range. A numbers of BIOSes are + known to utilize this area during suspend/resume, so it must not + be used by the kernel. + + Set this to N if you are absolutely sure that you trust the BIOS + to get all its memory reservations and usages right. + + If you have doubts about the BIOS (e.g. suspend/resume does not + work or there's kernel crashes after certain hardware hotplug + events) and it's not AMI or Phoenix, then you might want to enable + X86_CHECK_BIOS_CORRUPTION=y to allow the kernel to check typical + corruption patterns. + + Say Y if unsure. + config MATH_EMULATION bool prompt "Math emulation" if X86_32 @@ -1813,7 +1883,7 @@ config IA32_EMULATION config IA32_AOUT tristate "IA32 a.out support" - depends on IA32_EMULATION && ARCH_SUPPORTS_AOUT + depends on IA32_EMULATION help Support old a.out binaries in the 32bit emulation. diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index f8843c3ae77..0b7c4a3f065 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -38,8 +38,7 @@ config M386 - "Crusoe" for the Transmeta Crusoe series. - "Efficeon" for the Transmeta Efficeon series. - "Winchip-C6" for original IDT Winchip. - - "Winchip-2" for IDT Winchip 2. - - "Winchip-2A" for IDT Winchips with 3dNow! capabilities. + - "Winchip-2" for IDT Winchips with 3dNow! capabilities. - "GeodeGX1" for Geode GX1 (Cyrix MediaGX). - "Geode GX/LX" For AMD Geode GX and LX processors. - "CyrixIII/VIA C3" for VIA Cyrix III or VIA C3. @@ -194,19 +193,11 @@ config MWINCHIPC6 treat this chip as a 586TSC with some extended instructions and alignment requirements. -config MWINCHIP2 - bool "Winchip-2" - depends on X86_32 - help - Select this for an IDT Winchip-2. Linux and GCC - treat this chip as a 586TSC with some extended instructions - and alignment requirements. - config MWINCHIP3D - bool "Winchip-2A/Winchip-3" + bool "Winchip-2/Winchip-2A/Winchip-3" depends on X86_32 help - Select this for an IDT Winchip-2A or 3. Linux and GCC + Select this for an IDT Winchip-2, 2A or 3. Linux and GCC treat this chip as a 586TSC with some extended instructions and alignment requirements. Also enable out of order memory stores for this CPU, which can increase performance of some @@ -318,7 +309,7 @@ config X86_L1_CACHE_SHIFT int default "7" if MPENTIUM4 || X86_GENERIC || GENERIC_CPU || MPSC default "4" if X86_ELAN || M486 || M386 || MGEODEGX1 - default "5" if MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX + default "5" if MWINCHIP3D || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX default "6" if MK7 || MK8 || MPENTIUMM || MCORE2 || MVIAC7 config X86_XADD @@ -360,7 +351,7 @@ config X86_POPAD_OK config X86_ALIGNMENT_16 def_bool y - depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 || MGEODEGX1 + depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || X86_ELAN || MK6 || M586MMX || M586TSC || M586 || M486 || MVIAC3_2 || MGEODEGX1 config X86_INTEL_USERCOPY def_bool y @@ -368,7 +359,7 @@ config X86_INTEL_USERCOPY config X86_USE_PPRO_CHECKSUM def_bool y - depends on MWINCHIP3D || MWINCHIP2 || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MK8 || MVIAC3_2 || MEFFICEON || MGEODE_LX || MCORE2 + depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MK8 || MVIAC3_2 || MEFFICEON || MGEODE_LX || MCORE2 config X86_USE_3DNOW def_bool y @@ -376,7 +367,7 @@ config X86_USE_3DNOW config X86_OOSTORE def_bool y - depends on (MWINCHIP3D || MWINCHIP2 || MWINCHIPC6) && MTRR + depends on (MWINCHIP3D || MWINCHIPC6) && MTRR # # P6_NOPs are a relatively minor optimization that require a family >= @@ -396,7 +387,7 @@ config X86_P6_NOP config X86_TSC def_bool y - depends on ((MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64 + depends on ((MWINCHIP3D || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64 config X86_CMPXCHG64 def_bool y @@ -406,7 +397,7 @@ config X86_CMPXCHG64 # generates cmov. config X86_CMOV def_bool y - depends on (MK7 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || X86_64) + depends on (MK8 || MK7 || MCORE2 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || MCRUSOE || MEFFICEON || X86_64) config X86_MINIMUM_CPU_FAMILY int @@ -417,10 +408,9 @@ config X86_MINIMUM_CPU_FAMILY config X86_DEBUGCTLMSR def_bool y - depends on !(MK6 || MWINCHIPC6 || MWINCHIP2 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386) + depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486 || M386) menuconfig PROCESSOR_SELECT - default y bool "Supported processor vendors" if EMBEDDED help This lets you choose what x86 vendor support code your kernel @@ -430,48 +420,97 @@ config CPU_SUP_INTEL default y bool "Support Intel processors" if PROCESSOR_SELECT help - This enables extended support for Intel processors + This enables detection, tunings and quirks for Intel processors + + You need this enabled if you want your kernel to run on an + Intel CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on an Intel + CPU might render the kernel unbootable. + + If unsure, say N. config CPU_SUP_CYRIX_32 default y bool "Support Cyrix processors" if PROCESSOR_SELECT depends on !64BIT help - This enables extended support for Cyrix processors + This enables detection, tunings and quirks for Cyrix processors + + You need this enabled if you want your kernel to run on a + Cyrix CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Cyrix + CPU might render the kernel unbootable. + + If unsure, say N. config CPU_SUP_AMD default y bool "Support AMD processors" if PROCESSOR_SELECT help - This enables extended support for AMD processors + This enables detection, tunings and quirks for AMD processors + + You need this enabled if you want your kernel to run on an + AMD CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on an AMD + CPU might render the kernel unbootable. + + If unsure, say N. config CPU_SUP_CENTAUR_32 default y bool "Support Centaur processors" if PROCESSOR_SELECT depends on !64BIT help - This enables extended support for Centaur processors + This enables detection, tunings and quirks for Centaur processors + + You need this enabled if you want your kernel to run on a + Centaur CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Centaur + CPU might render the kernel unbootable. + + If unsure, say N. config CPU_SUP_CENTAUR_64 default y bool "Support Centaur processors" if PROCESSOR_SELECT depends on 64BIT help - This enables extended support for Centaur processors + This enables detection, tunings and quirks for Centaur processors + + You need this enabled if you want your kernel to run on a + Centaur CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Centaur + CPU might render the kernel unbootable. + + If unsure, say N. config CPU_SUP_TRANSMETA_32 default y bool "Support Transmeta processors" if PROCESSOR_SELECT depends on !64BIT help - This enables extended support for Transmeta processors + This enables detection, tunings and quirks for Transmeta processors + + You need this enabled if you want your kernel to run on a + Transmeta CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a Transmeta + CPU might render the kernel unbootable. + + If unsure, say N. config CPU_SUP_UMC_32 default y bool "Support UMC processors" if PROCESSOR_SELECT depends on !64BIT help - This enables extended support for UMC processors + This enables detection, tunings and quirks for UMC processors + + You need this enabled if you want your kernel to run on a + UMC CPU. Disabling this option on other types of CPUs + makes the kernel a tiny bit smaller. Disabling it on a UMC + CPU might render the kernel unbootable. + + If unsure, say N. config X86_DS bool "Debug Store support" diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 092f019e033..2a3dfbd5e67 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug @@ -43,6 +43,19 @@ config EARLY_PRINTK with klogd/syslogd or the X server. You should normally N here, unless you want to debug such a crash. +config EARLY_PRINTK_DBGP + bool "Early printk via EHCI debug port" + default n + depends on EARLY_PRINTK && PCI + help + Write kernel log output directly into the EHCI debug port. + + This is useful for kernel debugging when your machine crashes very + early before the console code is initialized. For normal operation + it is not recommended because it looks ugly and doesn't cooperate + with klogd/syslogd or the X server. You should normally N here, + unless you want to debug such a crash. You need usb debug device. + config DEBUG_STACKOVERFLOW bool "Check for stack overflows" depends on DEBUG_KERNEL diff --git a/arch/x86/Makefile_32.cpu b/arch/x86/Makefile_32.cpu index e372b584e91..80177ec052f 100644 --- a/arch/x86/Makefile_32.cpu +++ b/arch/x86/Makefile_32.cpu @@ -28,7 +28,6 @@ cflags-$(CONFIG_MK8) += $(call cc-option,-march=k8,-march=athlon) cflags-$(CONFIG_MCRUSOE) += -march=i686 $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0 cflags-$(CONFIG_MEFFICEON) += -march=i686 $(call tune,pentium3) $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0 cflags-$(CONFIG_MWINCHIPC6) += $(call cc-option,-march=winchip-c6,-march=i586) -cflags-$(CONFIG_MWINCHIP2) += $(call cc-option,-march=winchip2,-march=i586) cflags-$(CONFIG_MWINCHIP3D) += $(call cc-option,-march=winchip2,-march=i586) cflags-$(CONFIG_MCYRIXIII) += $(call cc-option,-march=c3,-march=i486) $(align)-functions=0 $(align)-jumps=0 $(align)-loops=0 cflags-$(CONFIG_MVIAC3_2) += $(call cc-option,-march=c3-2,-march=i686) @@ -45,3 +44,8 @@ cflags-$(CONFIG_MGEODEGX1) += -march=pentium-mmx # cpu entries cflags-$(CONFIG_X86_GENERIC) += $(call tune,generic,$(call tune,i686)) +# Bug fix for binutils: this option is required in order to keep +# binutils from generating NOPL instructions against our will. +ifneq ($(CONFIG_X86_P6_NOP),y) +cflags-y += $(call cc-option,-Wa$(comma)-mtune=generic32,) +endif diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile index 7ee102f9c4f..cd48c721001 100644 --- a/arch/x86/boot/Makefile +++ b/arch/x86/boot/Makefile @@ -72,9 +72,7 @@ KBUILD_CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D__KERNEL__ \ KBUILD_CFLAGS += $(call cc-option,-m32) KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ -$(obj)/zImage: IMAGE_OFFSET := 0x1000 $(obj)/zImage: asflags-y := $(SVGA_MODE) $(RAMDISK) -$(obj)/bzImage: IMAGE_OFFSET := 0x100000 $(obj)/bzImage: ccflags-y := -D__BIG_KERNEL__ $(obj)/bzImage: asflags-y := $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__ $(obj)/bzImage: BUILDFLAGS := -b @@ -117,7 +115,7 @@ $(obj)/setup.bin: $(obj)/setup.elf FORCE $(call if_changed,objcopy) $(obj)/compressed/vmlinux: FORCE - $(Q)$(MAKE) $(build)=$(obj)/compressed IMAGE_OFFSET=$(IMAGE_OFFSET) $@ + $(Q)$(MAKE) $(build)=$(obj)/compressed $@ # Set this if you want to pass append arguments to the zdisk/fdimage/isoimage kernel FDARGS = @@ -181,6 +179,7 @@ isoimage: $(BOOTIMAGE) mkisofs -J -r -o $(obj)/image.iso -b isolinux.bin -c boot.cat \ -no-emul-boot -boot-load-size 4 -boot-info-table \ $(obj)/isoimage + isohybrid $(obj)/image.iso 2>/dev/null || true rm -rf $(obj)/isoimage zlilo: $(BOOTIMAGE) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 92fdd35bd93..1771c804e02 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -27,9 +27,8 @@ $(obj)/vmlinux.bin: vmlinux FORCE $(call if_changed,objcopy) -ifeq ($(CONFIG_X86_32),y) -targets += vmlinux.bin.all vmlinux.relocs -hostprogs-y := relocs +targets += vmlinux.bin.all vmlinux.relocs relocs +hostprogs-$(CONFIG_X86_32) += relocs quiet_cmd_relocs = RELOCS $@ cmd_relocs = $(obj)/relocs $< > $@;$(obj)/relocs --abs-relocs $< @@ -43,6 +42,8 @@ quiet_cmd_relocbin = BUILD $@ $(obj)/vmlinux.bin.all: $(vmlinux.bin.all-y) FORCE $(call if_changed,relocbin) +ifeq ($(CONFIG_X86_32),y) + ifdef CONFIG_RELOCATABLE $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin.all FORCE $(call if_changed,gzip) @@ -59,6 +60,5 @@ $(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE LDFLAGS_piggy.o := -r --format binary --oformat elf64-x86-64 -T endif - $(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE $(call if_changed,ld) diff --git a/arch/x86/boot/edd.c b/arch/x86/boot/edd.c index d93cbc6464d..1aae8f3e5ca 100644 --- a/arch/x86/boot/edd.c +++ b/arch/x86/boot/edd.c @@ -41,6 +41,7 @@ static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) char *mbrbuf_ptr, *mbrbuf_end; u32 buf_base, mbr_base; extern char _end[]; + u16 mbr_magic; sector_size = ei->params.bytes_per_sector; if (!sector_size) @@ -58,11 +59,15 @@ static u32 read_mbr_sig(u8 devno, struct edd_info *ei, u32 *mbrsig) if (mbrbuf_end > (char *)(size_t)boot_params.hdr.heap_end_ptr) return -1; + memset(mbrbuf_ptr, 0, sector_size); if (read_mbr(devno, mbrbuf_ptr)) return -1; *mbrsig = *(u32 *)&mbrbuf_ptr[EDD_MBR_SIG_OFFSET]; - return 0; + mbr_magic = *(u16 *)&mbrbuf_ptr[510]; + + /* check for valid MBR magic */ + return mbr_magic == 0xAA55 ? 0 : -1; } static int get_edd_info(u8 devno, struct edd_info *ei) diff --git a/arch/x86/boot/video-vesa.c b/arch/x86/boot/video-vesa.c index 401ad998ad0..1e6fe0214c8 100644 --- a/arch/x86/boot/video-vesa.c +++ b/arch/x86/boot/video-vesa.c @@ -224,7 +224,7 @@ static void vesa_store_pm_info(void) static void vesa_store_mode_params_graphics(void) { /* Tell the kernel we're in VESA graphics mode */ - boot_params.screen_info.orig_video_isVGA = 0x23; + boot_params.screen_info.orig_video_isVGA = VIDEO_TYPE_VLFB; /* Mode parameters */ boot_params.screen_info.vesa_attributes = vminfo.mode_attr; diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index ef9a52005ec..52d0359719d 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -213,7 +213,6 @@ CONFIG_M686=y # CONFIG_MCRUSOE is not set # CONFIG_MEFFICEON is not set # CONFIG_MWINCHIPC6 is not set -# CONFIG_MWINCHIP2 is not set # CONFIG_MWINCHIP3D is not set # CONFIG_MGEODEGX1 is not set # CONFIG_MGEODE_LX is not set @@ -1535,7 +1534,6 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_VGA_CONSOLE=y CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 -CONFIG_VIDEO_SELECT=y CONFIG_DUMMY_CONSOLE=y # CONFIG_FRAMEBUFFER_CONSOLE is not set CONFIG_LOGO=y diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index e620ea6e2a7..f0a03d7a7d6 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -210,7 +210,6 @@ CONFIG_X86_PC=y # CONFIG_MCRUSOE is not set # CONFIG_MEFFICEON is not set # CONFIG_MWINCHIPC6 is not set -# CONFIG_MWINCHIP2 is not set # CONFIG_MWINCHIP3D is not set # CONFIG_MGEODEGX1 is not set # CONFIG_MGEODE_LX is not set @@ -1505,7 +1504,6 @@ CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_VGA_CONSOLE=y CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64 -CONFIG_VIDEO_SELECT=y CONFIG_DUMMY_CONSOLE=y # CONFIG_FRAMEBUFFER_CONSOLE is not set CONFIG_LOGO=y diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index 8d64c1bc847..4bc02b23674 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -351,31 +351,28 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc, savesegment(es, tmp); err |= __put_user(tmp, (unsigned int __user *)&sc->es); - err |= __put_user((u32)regs->di, &sc->di); - err |= __put_user((u32)regs->si, &sc->si); - err |= __put_user((u32)regs->bp, &sc->bp); - err |= __put_user((u32)regs->sp, &sc->sp); - err |= __put_user((u32)regs->bx, &sc->bx); - err |= __put_user((u32)regs->dx, &sc->dx); - err |= __put_user((u32)regs->cx, &sc->cx); - err |= __put_user((u32)regs->ax, &sc->ax); - err |= __put_user((u32)regs->cs, &sc->cs); - err |= __put_user((u32)regs->ss, &sc->ss); + err |= __put_user(regs->di, &sc->di); + err |= __put_user(regs->si, &sc->si); + err |= __put_user(regs->bp, &sc->bp); + err |= __put_user(regs->sp, &sc->sp); + err |= __put_user(regs->bx, &sc->bx); + err |= __put_user(regs->dx, &sc->dx); + err |= __put_user(regs->cx, &sc->cx); + err |= __put_user(regs->ax, &sc->ax); + err |= __put_user(regs->cs, &sc->cs); + err |= __put_user(regs->ss, &sc->ss); err |= __put_user(current->thread.trap_no, &sc->trapno); err |= __put_user(current->thread.error_code, &sc->err); - err |= __put_user((u32)regs->ip, &sc->ip); - err |= __put_user((u32)regs->flags, &sc->flags); - err |= __put_user((u32)regs->sp, &sc->sp_at_signal); + err |= __put_user(regs->ip, &sc->ip); + err |= __put_user(regs->flags, &sc->flags); + err |= __put_user(regs->sp, &sc->sp_at_signal); tmp = save_i387_xstate_ia32(fpstate); if (tmp < 0) err = -EFAULT; - else { - clear_used_math(); - stts(); + else err |= __put_user(ptr_to_compat(tmp ? fpstate : NULL), &sc->fpstate); - } /* non-iBCS2 extensions.. */ err |= __put_user(mask, &sc->oldmask); @@ -444,21 +441,18 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - err |= __put_user(sig, &frame->sig); - if (err) - goto give_sigsegv; + if (__put_user(sig, &frame->sig)) + return -EFAULT; - err |= ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]); - if (err) - goto give_sigsegv; + if (ia32_setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) + return -EFAULT; if (_COMPAT_NSIG_WORDS > 1) { - err |= __copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - if (err) - goto give_sigsegv; + if (__copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + return -EFAULT; } if (ka->sa.sa_flags & SA_RESTORER) { @@ -479,7 +473,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, */ err |= __copy_to_user(frame->retcode, &code, 8); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; @@ -502,10 +496,6 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka, #endif return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, @@ -533,14 +523,14 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; err |= __put_user(sig, &frame->sig); err |= __put_user(ptr_to_compat(&frame->info), &frame->pinfo); err |= __put_user(ptr_to_compat(&frame->uc), &frame->puc); err |= copy_siginfo_to_user32(&frame->info, info); if (err) - goto give_sigsegv; + return -EFAULT; /* Create the ucontext. */ if (cpu_has_xsave) @@ -556,7 +546,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) - goto give_sigsegv; + return -EFAULT; if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; @@ -571,7 +561,7 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, */ err |= __copy_to_user(frame->retcode, &code, 8); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; @@ -599,8 +589,4 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, #endif return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index ffc1bb4fed7..eb4314768bf 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -39,11 +39,11 @@ .endm /* clobbers %eax */ - .macro CLEAR_RREGS + .macro CLEAR_RREGS _r9=rax xorl %eax,%eax movq %rax,R11(%rsp) movq %rax,R10(%rsp) - movq %rax,R9(%rsp) + movq %\_r9,R9(%rsp) movq %rax,R8(%rsp) .endm @@ -52,11 +52,10 @@ * We don't reload %eax because syscall_trace_enter() returned * the value it wants us to use in the table lookup. */ - .macro LOAD_ARGS32 offset - movl \offset(%rsp),%r11d - movl \offset+8(%rsp),%r10d + .macro LOAD_ARGS32 offset, _r9=0 + .if \_r9 movl \offset+16(%rsp),%r9d - movl \offset+24(%rsp),%r8d + .endif movl \offset+40(%rsp),%ecx movl \offset+48(%rsp),%edx movl \offset+56(%rsp),%esi @@ -145,7 +144,7 @@ ENTRY(ia32_sysenter_target) SAVE_ARGS 0,0,1 /* no need to do an access_ok check here because rbp has been 32bit zero extended */ -1: movl (%rbp),%r9d +1: movl (%rbp),%ebp .section __ex_table,"a" .quad 1b,ia32_badarg .previous @@ -157,7 +156,7 @@ ENTRY(ia32_sysenter_target) cmpl $(IA32_NR_syscalls-1),%eax ja ia32_badsys sysenter_do_call: - IA32_ARG_FIXUP 1 + IA32_ARG_FIXUP sysenter_dispatch: call *ia32_sys_call_table(,%rax,8) movq %rax,RAX-ARGOFFSET(%rsp) @@ -234,20 +233,17 @@ sysexit_audit: #endif sysenter_tracesys: - xchgl %r9d,%ebp #ifdef CONFIG_AUDITSYSCALL testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT),TI_flags(%r10) jz sysenter_auditsys #endif SAVE_REST CLEAR_RREGS - movq %r9,R9(%rsp) movq $-ENOSYS,RAX(%rsp)/* ptrace can change this for a bad syscall */ movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace_enter LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */ RESTORE_REST - xchgl %ebp,%r9d cmpl $(IA32_NR_syscalls-1),%eax ja int_ret_from_sys_call /* sysenter_tracesys has set RAX(%rsp) */ jmp sysenter_do_call @@ -314,9 +310,9 @@ ENTRY(ia32_cstar_target) testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%r10) CFI_REMEMBER_STATE jnz cstar_tracesys -cstar_do_call: cmpl $IA32_NR_syscalls-1,%eax ja ia32_badsys +cstar_do_call: IA32_ARG_FIXUP 1 cstar_dispatch: call *ia32_sys_call_table(,%rax,8) @@ -357,15 +353,13 @@ cstar_tracesys: #endif xchgl %r9d,%ebp SAVE_REST - CLEAR_RREGS - movq %r9,R9(%rsp) + CLEAR_RREGS r9 movq $-ENOSYS,RAX(%rsp) /* ptrace can change this for a bad syscall */ movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace_enter - LOAD_ARGS32 ARGOFFSET /* reload args from stack in case ptrace changed it */ + LOAD_ARGS32 ARGOFFSET, 1 /* reload args from stack in case ptrace changed it */ RESTORE_REST xchgl %ebp,%r9d - movl RSP-ARGOFFSET(%rsp), %r8d cmpl $(IA32_NR_syscalls-1),%eax ja int_ret_from_sys_call /* cstar_tracesys has set RAX(%rsp) */ jmp cstar_do_call diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index c9be69fedb7..0d41f0343dc 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -10,7 +10,7 @@ ifdef CONFIG_FTRACE # Do not profile debug and lowlevel utilities CFLAGS_REMOVE_tsc.o = -pg CFLAGS_REMOVE_rtc.o = -pg -CFLAGS_REMOVE_paravirt.o = -pg +CFLAGS_REMOVE_paravirt-spinlocks.o = -pg endif # @@ -23,7 +23,7 @@ CFLAGS_hpet.o := $(nostackp) CFLAGS_tsc.o := $(nostackp) obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o -obj-y += traps_$(BITS).o irq_$(BITS).o +obj-y += traps.o irq_$(BITS).o dumpstack_$(BITS).o obj-y += time_$(BITS).o ioport.o ldt.o obj-y += setup.o i8259.o irqinit_$(BITS).o setup_percpu.o obj-$(CONFIG_X86_VISWS) += visws_quirks.o @@ -51,7 +51,6 @@ obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o obj-$(CONFIG_MCA) += mca_32.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o -obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_PCI) += early-quirks.o apm-y := apm_32.o obj-$(CONFIG_APM) += apm.o @@ -90,7 +89,7 @@ obj-$(CONFIG_DEBUG_NX_TEST) += test_nx.o obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o obj-$(CONFIG_KVM_GUEST) += kvm.o obj-$(CONFIG_KVM_CLOCK) += kvmclock.o -obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o +obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o paravirt-spinlocks.o obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o @@ -100,6 +99,11 @@ scx200-y += scx200_32.o obj-$(CONFIG_OLPC) += olpc.o +microcode-y := microcode_core.o +microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o +microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o +obj-$(CONFIG_MICROCODE) += microcode.o + ### # 64 bit specific files ifeq ($(CONFIG_X86_64),y) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index c2ac1b4515a..eb875cdc736 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1418,8 +1418,16 @@ static int __init force_acpi_ht(const struct dmi_system_id *d) */ static int __init dmi_ignore_irq0_timer_override(const struct dmi_system_id *d) { - pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n", d->ident); - acpi_skip_timer_override = 1; + /* + * The ati_ixp4x0_rev() early PCI quirk should have set + * the acpi_skip_timer_override flag already: + */ + if (!acpi_skip_timer_override) { + WARN(1, KERN_ERR "ati_ixp4x0 quirk not complete.\n"); + pr_notice("%s detected: Ignoring BIOS IRQ0 pin2 override\n", + d->ident); + acpi_skip_timer_override = 1; + } return 0; } diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index fb04e49776b..a84ac7b570e 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -444,7 +444,7 @@ void __init alternative_instructions(void) _text, _etext); /* Only switch to UP mode if we don't immediately boot others */ - if (num_possible_cpus() == 1 || setup_max_cpus <= 1) + if (num_present_cpus() == 1 || setup_max_cpus <= 1) alternatives_smp_switch(0); } #endif diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c index 148fcfe22f1..4cd8083c58b 100644 --- a/arch/x86/kernel/amd_iommu_init.c +++ b/arch/x86/kernel/amd_iommu_init.c @@ -723,9 +723,7 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h) init_iommu_from_acpi(iommu, h); init_iommu_devices(iommu); - pci_enable_device(iommu->dev); - - return 0; + return pci_enable_device(iommu->dev); } /* diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c index a91c57cb666..21c831d96af 100644 --- a/arch/x86/kernel/apic_32.c +++ b/arch/x86/kernel/apic_32.c @@ -295,6 +295,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) * * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and * MCE interrupts are supported. Thus MCE offset must be set to 0. + * + * If mask=1, the LVT entry does not generate interrupts while mask=0 + * enables the vector. See also the BKDGs. */ #define APIC_EILVT_LVTOFF_MCE 0 @@ -319,6 +322,7 @@ u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask) setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); return APIC_EILVT_LVTOFF_IBS; } +EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); /* * Program the next event, relative to now diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c index 53898b65a6a..94ddb69ae15 100644 --- a/arch/x86/kernel/apic_64.c +++ b/arch/x86/kernel/apic_64.c @@ -307,6 +307,9 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) * * Vector mappings are hard coded. On K8 only offset 0 (APIC500) and * MCE interrupts are supported. Thus MCE offset must be set to 0. + * + * If mask=1, the LVT entry does not generate interrupts while mask=0 + * enables the vector. See also the BKDGs. */ #define APIC_EILVT_LVTOFF_MCE 0 @@ -331,6 +334,7 @@ u8 setup_APIC_eilvt_ibs(u8 vector, u8 msg_type, u8 mask) setup_APIC_eilvt(APIC_EILVT_LVTOFF_IBS, vector, msg_type, mask); return APIC_EILVT_LVTOFF_IBS; } +EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs); /* * Program the next event, relative to now diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 7581b62df18..25581dcb280 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -124,18 +124,25 @@ static inline int flag_is_changeable_p(u32 flag) { u32 f1, f2; - asm("pushfl\n\t" - "pushfl\n\t" - "popl %0\n\t" - "movl %0,%1\n\t" - "xorl %2,%0\n\t" - "pushl %0\n\t" - "popfl\n\t" - "pushfl\n\t" - "popl %0\n\t" - "popfl\n\t" - : "=&r" (f1), "=&r" (f2) - : "ir" (flag)); + /* + * Cyrix and IDT cpus allow disabling of CPUID + * so the code below may return different results + * when it is executed before and after enabling + * the CPUID. Add "volatile" to not allow gcc to + * optimize the subsequent calls to this function. + */ + asm volatile ("pushfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "movl %0,%1\n\t" + "xorl %2,%0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "popfl\n\t" + : "=&r" (f1), "=&r" (f2) + : "ir" (flag)); return ((f1^f2) & flag) != 0; } @@ -719,12 +726,24 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c) #endif } +#ifdef CONFIG_X86_64 +static void vgetcpu_set_mode(void) +{ + if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) + vgetcpu_mode = VGETCPU_RDTSCP; + else + vgetcpu_mode = VGETCPU_LSL; +} +#endif + void __init identify_boot_cpu(void) { identify_cpu(&boot_cpu_data); #ifdef CONFIG_X86_32 sysenter_setup(); enable_sep_cpu(); +#else + vgetcpu_set_mode(); #endif } @@ -797,7 +816,7 @@ void __cpuinit print_cpu_info(struct cpuinfo_x86 *c) else if (c->cpuid_level >= 0) vendor = c->x86_vendor_id; - if (vendor && strncmp(c->x86_model_id, vendor, strlen(vendor))) + if (vendor && !strstr(c->x86_model_id, vendor)) printk(KERN_CONT "%s ", vendor); if (c->x86_model_id[0]) @@ -1121,16 +1140,5 @@ void __cpuinit cpu_init(void) xsave_init(); } -#ifdef CONFIG_HOTPLUG_CPU -void __cpuinit cpu_uninit(void) -{ - int cpu = raw_smp_processor_id(); - cpu_clear(cpu, cpu_initialized); - - /* lazy TLB state */ - per_cpu(cpu_tlbstate, cpu).state = 0; - per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm; -} -#endif #endif diff --git a/arch/x86/kernel/doublefault_32.c b/arch/x86/kernel/doublefault_32.c index a47798b59f0..b4f14c6c09d 100644 --- a/arch/x86/kernel/doublefault_32.c +++ b/arch/x86/kernel/doublefault_32.c @@ -66,6 +66,6 @@ struct tss_struct doublefault_tss __cacheline_aligned = { .ds = __USER_DS, .fs = __KERNEL_PERCPU, - .__cr3 = __pa(swapper_pg_dir) + .__cr3 = __pa_nodebug(swapper_pg_dir), } }; diff --git a/arch/x86/kernel/dumpstack_32.c b/arch/x86/kernel/dumpstack_32.c new file mode 100644 index 00000000000..201ee359a1a --- /dev/null +++ b/arch/x86/kernel/dumpstack_32.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs + */ +#include <linux/kallsyms.h> +#include <linux/kprobes.h> +#include <linux/uaccess.h> +#include <linux/utsname.h> +#include <linux/hardirq.h> +#include <linux/kdebug.h> +#include <linux/module.h> +#include <linux/ptrace.h> +#include <linux/kexec.h> +#include <linux/bug.h> +#include <linux/nmi.h> + +#include <asm/stacktrace.h> + +#define STACKSLOTS_PER_LINE 8 +#define get_bp(bp) asm("movl %%ebp, %0" : "=r" (bp) :) + +int panic_on_unrecovered_nmi; +int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE; +static unsigned int code_bytes = 64; +static int die_counter; + +void printk_address(unsigned long address, int reliable) +{ + printk(" [<%p>] %s%pS\n", (void *) address, + reliable ? "" : "? ", (void *) address); +} + +static inline int valid_stack_ptr(struct thread_info *tinfo, + void *p, unsigned int size, void *end) +{ + void *t = tinfo; + if (end) { + if (p < end && p >= (end-THREAD_SIZE)) + return 1; + else + return 0; + } + return p > t && p < t + THREAD_SIZE - size; +} + +/* The form of the top of the frame on the stack */ +struct stack_frame { + struct stack_frame *next_frame; + unsigned long return_address; +}; + +static inline unsigned long +print_context_stack(struct thread_info *tinfo, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end) +{ + struct stack_frame *frame = (struct stack_frame *)bp; + + while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { + unsigned long addr; + + addr = *stack; + if (__kernel_text_address(addr)) { + if ((unsigned long) stack == bp + sizeof(long)) { + ops->address(data, addr, 1); + frame = frame->next_frame; + bp = (unsigned long) frame; + } else { + ops->address(data, addr, bp == 0); + } + } + stack++; + } + return bp; +} + +void dump_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data) +{ + if (!task) + task = current; + + if (!stack) { + unsigned long dummy; + stack = &dummy; + if (task && task != current) + stack = (unsigned long *)task->thread.sp; + } + +#ifdef CONFIG_FRAME_POINTER + if (!bp) { + if (task == current) { + /* Grab bp right from our regs */ + get_bp(bp); + } else { + /* bp is the last reg pushed by switch_to */ + bp = *(unsigned long *) task->thread.sp; + } + } +#endif + + for (;;) { + struct thread_info *context; + + context = (struct thread_info *) + ((unsigned long)stack & (~(THREAD_SIZE - 1))); + bp = print_context_stack(context, stack, bp, ops, data, NULL); + + stack = (unsigned long *)context->previous_esp; + if (!stack) + break; + if (ops->stack(data, "IRQ") < 0) + break; + touch_nmi_watchdog(); + } +} +EXPORT_SYMBOL(dump_trace); + +static void +print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) +{ + printk(data); + print_symbol(msg, symbol); + printk("\n"); +} + +static void print_trace_warning(void *data, char *msg) +{ + printk("%s%s\n", (char *)data, msg); +} + +static int print_trace_stack(void *data, char *name) +{ + printk("%s <%s> ", (char *)data, name); + return 0; +} + +/* + * Print one address/symbol entries per line. + */ +static void print_trace_address(void *data, unsigned long addr, int reliable) +{ + touch_nmi_watchdog(); + printk(data); + printk_address(addr, reliable); +} + +static const struct stacktrace_ops print_trace_ops = { + .warning = print_trace_warning, + .warning_symbol = print_trace_warning_symbol, + .stack = print_trace_stack, + .address = print_trace_address, +}; + +static void +show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, char *log_lvl) +{ + printk("%sCall Trace:\n", log_lvl); + dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); +} + +void show_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp) +{ + show_trace_log_lvl(task, regs, stack, bp, ""); +} + +static void +show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *sp, unsigned long bp, char *log_lvl) +{ + unsigned long *stack; + int i; + + if (sp == NULL) { + if (task) + sp = (unsigned long *)task->thread.sp; + else + sp = (unsigned long *)&sp; + } + + stack = sp; + for (i = 0; i < kstack_depth_to_print; i++) { + if (kstack_end(stack)) + break; + if (i && ((i % STACKSLOTS_PER_LINE) == 0)) + printk("\n%s", log_lvl); + printk(" %08lx", *stack++); + touch_nmi_watchdog(); + } + printk("\n"); + show_trace_log_lvl(task, regs, sp, bp, log_lvl); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + show_stack_log_lvl(task, NULL, sp, 0, ""); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long bp = 0; + unsigned long stack; + +#ifdef CONFIG_FRAME_POINTER + if (!bp) + get_bp(bp); +#endif + + printk("Pid: %d, comm: %.20s %s %s %.*s\n", + current->pid, current->comm, print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + show_trace(NULL, NULL, &stack, bp); +} + +EXPORT_SYMBOL(dump_stack); + +void show_registers(struct pt_regs *regs) +{ + int i; + + print_modules(); + __show_regs(regs, 0); + + printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)\n", + TASK_COMM_LEN, current->comm, task_pid_nr(current), + current_thread_info(), current, task_thread_info(current)); + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode_vm(regs)) { + unsigned int code_prologue = code_bytes * 43 / 64; + unsigned int code_len = code_bytes; + unsigned char c; + u8 *ip; + + printk(KERN_EMERG "Stack:\n"); + show_stack_log_lvl(NULL, regs, ®s->sp, + 0, KERN_EMERG); + + printk(KERN_EMERG "Code: "); + + ip = (u8 *)regs->ip - code_prologue; + if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { + /* try starting at IP */ + ip = (u8 *)regs->ip; + code_len = code_len - code_prologue + 1; + } + for (i = 0; i < code_len; i++, ip++) { + if (ip < (u8 *)PAGE_OFFSET || + probe_kernel_address(ip, c)) { + printk(" Bad EIP value."); + break; + } + if (ip == (u8 *)regs->ip) + printk("<%02x> ", c); + else + printk("%02x ", c); + } + } + printk("\n"); +} + +int is_valid_bugaddr(unsigned long ip) +{ + unsigned short ud2; + + if (ip < PAGE_OFFSET) + return 0; + if (probe_kernel_address((unsigned short *)ip, ud2)) + return 0; + + return ud2 == 0x0b0f; +} + +static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; +static int die_owner = -1; +static unsigned int die_nest_count; + +unsigned __kprobes long oops_begin(void) +{ + unsigned long flags; + + oops_enter(); + + if (die_owner != raw_smp_processor_id()) { + console_verbose(); + raw_local_irq_save(flags); + __raw_spin_lock(&die_lock); + die_owner = smp_processor_id(); + die_nest_count = 0; + bust_spinlocks(1); + } else { + raw_local_irq_save(flags); + } + die_nest_count++; + return flags; +} + +void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) +{ + bust_spinlocks(0); + die_owner = -1; + add_taint(TAINT_DIE); + __raw_spin_unlock(&die_lock); + raw_local_irq_restore(flags); + + if (!regs) + return; + + if (kexec_should_crash(current)) + crash_kexec(regs); + if (in_interrupt()) + panic("Fatal exception in interrupt"); + if (panic_on_oops) + panic("Fatal exception"); + oops_exit(); + do_exit(signr); +} + +int __kprobes __die(const char *str, struct pt_regs *regs, long err) +{ + unsigned short ss; + unsigned long sp; + + printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); +#endif +#ifdef CONFIG_SMP + printk("SMP "); +#endif +#ifdef CONFIG_DEBUG_PAGEALLOC + printk("DEBUG_PAGEALLOC"); +#endif + printk("\n"); + if (notify_die(DIE_OOPS, str, regs, err, + current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) + return 1; + + show_registers(regs); + /* Executive summary in case the oops scrolled away */ + sp = (unsigned long) (®s->sp); + savesegment(ss, ss); + if (user_mode(regs)) { + sp = regs->sp; + ss = regs->ss & 0xffff; + } + printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip); + print_symbol("%s", regs->ip); + printk(" SS:ESP %04x:%08lx\n", ss, sp); + return 0; +} + +/* + * This is gone through when something in the kernel has done something bad + * and is about to be terminated: + */ +void die(const char *str, struct pt_regs *regs, long err) +{ + unsigned long flags = oops_begin(); + + if (die_nest_count < 3) { + report_bug(regs->ip, regs); + + if (__die(str, regs, err)) + regs = NULL; + } else { + printk(KERN_EMERG "Recursive die() failure, output suppressed\n"); + } + + oops_end(flags, regs, SIGSEGV); +} + +static DEFINE_SPINLOCK(nmi_print_lock); + +void notrace __kprobes +die_nmi(char *str, struct pt_regs *regs, int do_panic) +{ + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) + return; + + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out: + */ + bust_spinlocks(1); + printk(KERN_EMERG "%s", str); + printk(" on CPU%d, ip %08lx, registers:\n", + smp_processor_id(), regs->ip); + show_registers(regs); + if (do_panic) + panic("Non maskable interrupt"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + + /* + * If we are in kernel we are probably nested up pretty bad + * and might aswell get out now while we still can: + */ + if (!user_mode_vm(regs)) { + current->thread.trap_no = 2; + crash_kexec(regs); + } + + do_exit(SIGSEGV); +} + +static int __init oops_setup(char *s) +{ + if (!s) + return -EINVAL; + if (!strcmp(s, "panic")) + panic_on_oops = 1; + return 0; +} +early_param("oops", oops_setup); + +static int __init kstack_setup(char *s) +{ + if (!s) + return -EINVAL; + kstack_depth_to_print = simple_strtoul(s, NULL, 0); + return 0; +} +early_param("kstack", kstack_setup); + +static int __init code_bytes_setup(char *s) +{ + code_bytes = simple_strtoul(s, NULL, 0); + if (code_bytes > 8192) + code_bytes = 8192; + + return 1; +} +__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/dumpstack_64.c b/arch/x86/kernel/dumpstack_64.c new file mode 100644 index 00000000000..086cc8118e3 --- /dev/null +++ b/arch/x86/kernel/dumpstack_64.c @@ -0,0 +1,573 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs + */ +#include <linux/kallsyms.h> +#include <linux/kprobes.h> +#include <linux/uaccess.h> +#include <linux/utsname.h> +#include <linux/hardirq.h> +#include <linux/kdebug.h> +#include <linux/module.h> +#include <linux/ptrace.h> +#include <linux/kexec.h> +#include <linux/bug.h> +#include <linux/nmi.h> + +#include <asm/stacktrace.h> + +#define STACKSLOTS_PER_LINE 4 +#define get_bp(bp) asm("movq %%rbp, %0" : "=r" (bp) :) + +int panic_on_unrecovered_nmi; +int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE; +static unsigned int code_bytes = 64; +static int die_counter; + +void printk_address(unsigned long address, int reliable) +{ + printk(" [<%p>] %s%pS\n", (void *) address, + reliable ? "" : "? ", (void *) address); +} + +static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, + unsigned *usedp, char **idp) +{ + static char ids[][8] = { + [DEBUG_STACK - 1] = "#DB", + [NMI_STACK - 1] = "NMI", + [DOUBLEFAULT_STACK - 1] = "#DF", + [STACKFAULT_STACK - 1] = "#SS", + [MCE_STACK - 1] = "#MC", +#if DEBUG_STKSZ > EXCEPTION_STKSZ + [N_EXCEPTION_STACKS ... + N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]" +#endif + }; + unsigned k; + + /* + * Iterate over all exception stacks, and figure out whether + * 'stack' is in one of them: + */ + for (k = 0; k < N_EXCEPTION_STACKS; k++) { + unsigned long end = per_cpu(orig_ist, cpu).ist[k]; + /* + * Is 'stack' above this exception frame's end? + * If yes then skip to the next frame. + */ + if (stack >= end) + continue; + /* + * Is 'stack' above this exception frame's start address? + * If yes then we found the right frame. + */ + if (stack >= end - EXCEPTION_STKSZ) { + /* + * Make sure we only iterate through an exception + * stack once. If it comes up for the second time + * then there's something wrong going on - just + * break out and return NULL: + */ + if (*usedp & (1U << k)) + break; + *usedp |= 1U << k; + *idp = ids[k]; + return (unsigned long *)end; + } + /* + * If this is a debug stack, and if it has a larger size than + * the usual exception stacks, then 'stack' might still + * be within the lower portion of the debug stack: + */ +#if DEBUG_STKSZ > EXCEPTION_STKSZ + if (k == DEBUG_STACK - 1 && stack >= end - DEBUG_STKSZ) { + unsigned j = N_EXCEPTION_STACKS - 1; + + /* + * Black magic. A large debug stack is composed of + * multiple exception stack entries, which we + * iterate through now. Dont look: + */ + do { + ++j; + end -= EXCEPTION_STKSZ; + ids[j][4] = '1' + (j - N_EXCEPTION_STACKS); + } while (stack < end - EXCEPTION_STKSZ); + if (*usedp & (1U << j)) + break; + *usedp |= 1U << j; + *idp = ids[j]; + return (unsigned long *)end; + } +#endif + } + return NULL; +} + +/* + * x86-64 can have up to three kernel stacks: + * process stack + * interrupt stack + * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack + */ + +static inline int valid_stack_ptr(struct thread_info *tinfo, + void *p, unsigned int size, void *end) +{ + void *t = tinfo; + if (end) { + if (p < end && p >= (end-THREAD_SIZE)) + return 1; + else + return 0; + } + return p > t && p < t + THREAD_SIZE - size; +} + +/* The form of the top of the frame on the stack */ +struct stack_frame { + struct stack_frame *next_frame; + unsigned long return_address; +}; + +static inline unsigned long +print_context_stack(struct thread_info *tinfo, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data, + unsigned long *end) +{ + struct stack_frame *frame = (struct stack_frame *)bp; + + while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { + unsigned long addr; + + addr = *stack; + if (__kernel_text_address(addr)) { + if ((unsigned long) stack == bp + sizeof(long)) { + ops->address(data, addr, 1); + frame = frame->next_frame; + bp = (unsigned long) frame; + } else { + ops->address(data, addr, bp == 0); + } + } + stack++; + } + return bp; +} + +void dump_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, + const struct stacktrace_ops *ops, void *data) +{ + const unsigned cpu = get_cpu(); + unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; + unsigned used = 0; + struct thread_info *tinfo; + + if (!task) + task = current; + + if (!stack) { + unsigned long dummy; + stack = &dummy; + if (task && task != current) + stack = (unsigned long *)task->thread.sp; + } + +#ifdef CONFIG_FRAME_POINTER + if (!bp) { + if (task == current) { + /* Grab bp right from our regs */ + get_bp(bp); + } else { + /* bp is the last reg pushed by switch_to */ + bp = *(unsigned long *) task->thread.sp; + } + } +#endif + + /* + * Print function call entries in all stacks, starting at the + * current stack address. If the stacks consist of nested + * exceptions + */ + tinfo = task_thread_info(task); + for (;;) { + char *id; + unsigned long *estack_end; + estack_end = in_exception_stack(cpu, (unsigned long)stack, + &used, &id); + + if (estack_end) { + if (ops->stack(data, id) < 0) + break; + + bp = print_context_stack(tinfo, stack, bp, ops, + data, estack_end); + ops->stack(data, "<EOE>"); + /* + * We link to the next stack via the + * second-to-last pointer (index -2 to end) in the + * exception stack: + */ + stack = (unsigned long *) estack_end[-2]; + continue; + } + if (irqstack_end) { + unsigned long *irqstack; + irqstack = irqstack_end - + (IRQSTACKSIZE - 64) / sizeof(*irqstack); + + if (stack >= irqstack && stack < irqstack_end) { + if (ops->stack(data, "IRQ") < 0) + break; + bp = print_context_stack(tinfo, stack, bp, + ops, data, irqstack_end); + /* + * We link to the next stack (which would be + * the process stack normally) the last + * pointer (index -1 to end) in the IRQ stack: + */ + stack = (unsigned long *) (irqstack_end[-1]); + irqstack_end = NULL; + ops->stack(data, "EOI"); + continue; + } + } + break; + } + + /* + * This handles the process stack: + */ + bp = print_context_stack(tinfo, stack, bp, ops, data, NULL); + put_cpu(); +} +EXPORT_SYMBOL(dump_trace); + +static void +print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) +{ + printk(data); + print_symbol(msg, symbol); + printk("\n"); +} + +static void print_trace_warning(void *data, char *msg) +{ + printk("%s%s\n", (char *)data, msg); +} + +static int print_trace_stack(void *data, char *name) +{ + printk("%s <%s> ", (char *)data, name); + return 0; +} + +/* + * Print one address/symbol entries per line. + */ +static void print_trace_address(void *data, unsigned long addr, int reliable) +{ + touch_nmi_watchdog(); + printk(data); + printk_address(addr, reliable); +} + +static const struct stacktrace_ops print_trace_ops = { + .warning = print_trace_warning, + .warning_symbol = print_trace_warning_symbol, + .stack = print_trace_stack, + .address = print_trace_address, +}; + +static void +show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp, char *log_lvl) +{ + printk("%sCall Trace:\n", log_lvl); + dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); +} + +void show_trace(struct task_struct *task, struct pt_regs *regs, + unsigned long *stack, unsigned long bp) +{ + show_trace_log_lvl(task, regs, stack, bp, ""); +} + +static void +show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, + unsigned long *sp, unsigned long bp, char *log_lvl) +{ + unsigned long *stack; + int i; + const int cpu = smp_processor_id(); + unsigned long *irqstack_end = + (unsigned long *) (cpu_pda(cpu)->irqstackptr); + unsigned long *irqstack = + (unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE); + + /* + * debugging aid: "show_stack(NULL, NULL);" prints the + * back trace for this cpu. + */ + + if (sp == NULL) { + if (task) + sp = (unsigned long *)task->thread.sp; + else + sp = (unsigned long *)&sp; + } + + stack = sp; + for (i = 0; i < kstack_depth_to_print; i++) { + if (stack >= irqstack && stack <= irqstack_end) { + if (stack == irqstack_end) { + stack = (unsigned long *) (irqstack_end[-1]); + printk(" <EOI> "); + } + } else { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + } + if (i && ((i % STACKSLOTS_PER_LINE) == 0)) + printk("\n%s", log_lvl); + printk(" %016lx", *stack++); + touch_nmi_watchdog(); + } + printk("\n"); + show_trace_log_lvl(task, regs, sp, bp, log_lvl); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + show_stack_log_lvl(task, NULL, sp, 0, ""); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + unsigned long bp = 0; + unsigned long stack; + +#ifdef CONFIG_FRAME_POINTER + if (!bp) + get_bp(bp); +#endif + + printk("Pid: %d, comm: %.20s %s %s %.*s\n", + current->pid, current->comm, print_tainted(), + init_utsname()->release, + (int)strcspn(init_utsname()->version, " "), + init_utsname()->version); + show_trace(NULL, NULL, &stack, bp); +} +EXPORT_SYMBOL(dump_stack); + +void show_registers(struct pt_regs *regs) +{ + int i; + unsigned long sp; + const int cpu = smp_processor_id(); + struct task_struct *cur = cpu_pda(cpu)->pcurrent; + + sp = regs->sp; + printk("CPU %d ", cpu); + __show_regs(regs, 1); + printk("Process %s (pid: %d, threadinfo %p, task %p)\n", + cur->comm, cur->pid, task_thread_info(cur), cur); + + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (!user_mode(regs)) { + unsigned int code_prologue = code_bytes * 43 / 64; + unsigned int code_len = code_bytes; + unsigned char c; + u8 *ip; + + printk(KERN_EMERG "Stack:\n"); + show_stack_log_lvl(NULL, regs, (unsigned long *)sp, + regs->bp, KERN_EMERG); + + printk(KERN_EMERG "Code: "); + + ip = (u8 *)regs->ip - code_prologue; + if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { + /* try starting at IP */ + ip = (u8 *)regs->ip; + code_len = code_len - code_prologue + 1; + } + for (i = 0; i < code_len; i++, ip++) { + if (ip < (u8 *)PAGE_OFFSET || + probe_kernel_address(ip, c)) { + printk(" Bad RIP value."); + break; + } + if (ip == (u8 *)regs->ip) + printk("<%02x> ", c); + else + printk("%02x ", c); + } + } + printk("\n"); +} + +int is_valid_bugaddr(unsigned long ip) +{ + unsigned short ud2; + + if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2))) + return 0; + + return ud2 == 0x0b0f; +} + +static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; +static int die_owner = -1; +static unsigned int die_nest_count; + +unsigned __kprobes long oops_begin(void) +{ + int cpu; + unsigned long flags; + + oops_enter(); + + /* racy, but better than risking deadlock. */ + raw_local_irq_save(flags); + cpu = smp_processor_id(); + if (!__raw_spin_trylock(&die_lock)) { + if (cpu == die_owner) + /* nested oops. should stop eventually */; + else + __raw_spin_lock(&die_lock); + } + die_nest_count++; + die_owner = cpu; + console_verbose(); + bust_spinlocks(1); + return flags; +} + +void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) +{ + die_owner = -1; + bust_spinlocks(0); + die_nest_count--; + if (!die_nest_count) + /* Nest count reaches zero, release the lock. */ + __raw_spin_unlock(&die_lock); + raw_local_irq_restore(flags); + if (!regs) { + oops_exit(); + return; + } + if (in_interrupt()) + panic("Fatal exception in interrupt"); + if (panic_on_oops) + panic("Fatal exception"); + oops_exit(); + do_exit(signr); +} + +int __kprobes __die(const char *str, struct pt_regs *regs, long err) +{ + printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); +#ifdef CONFIG_PREEMPT + printk("PREEMPT "); +#endif +#ifdef CONFIG_SMP + printk("SMP "); +#endif +#ifdef CONFIG_DEBUG_PAGEALLOC + printk("DEBUG_PAGEALLOC"); +#endif + printk("\n"); + if (notify_die(DIE_OOPS, str, regs, err, + current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) + return 1; + + show_registers(regs); + add_taint(TAINT_DIE); + /* Executive summary in case the oops scrolled away */ + printk(KERN_ALERT "RIP "); + printk_address(regs->ip, 1); + printk(" RSP <%016lx>\n", regs->sp); + if (kexec_should_crash(current)) + crash_kexec(regs); + return 0; +} + +void die(const char *str, struct pt_regs *regs, long err) +{ + unsigned long flags = oops_begin(); + + if (!user_mode(regs)) + report_bug(regs->ip, regs); + + if (__die(str, regs, err)) + regs = NULL; + oops_end(flags, regs, SIGSEGV); +} + +notrace __kprobes void +die_nmi(char *str, struct pt_regs *regs, int do_panic) +{ + unsigned long flags; + + if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) + return; + + flags = oops_begin(); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + printk(KERN_EMERG "%s", str); + printk(" on CPU%d, ip %08lx, registers:\n", + smp_processor_id(), regs->ip); + show_registers(regs); + if (kexec_should_crash(current)) + crash_kexec(regs); + if (do_panic || panic_on_oops) + panic("Non maskable interrupt"); + oops_end(flags, NULL, SIGBUS); + nmi_exit(); + local_irq_enable(); + do_exit(SIGBUS); +} + +static int __init oops_setup(char *s) +{ + if (!s) + return -EINVAL; + if (!strcmp(s, "panic")) + panic_on_oops = 1; + return 0; +} +early_param("oops", oops_setup); + +static int __init kstack_setup(char *s) +{ + if (!s) + return -EINVAL; + kstack_depth_to_print = simple_strtoul(s, NULL, 0); + return 0; +} +early_param("kstack", kstack_setup); + +static int __init code_bytes_setup(char *s) +{ + code_bytes = simple_strtoul(s, NULL, 0); + if (code_bytes > 8192) + code_bytes = 8192; + + return 1; +} +__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 24bb5faf5ef..733c4f8d42e 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -95,6 +95,52 @@ static void __init nvidia_bugs(int num, int slot, int func) } +static u32 ati_ixp4x0_rev(int num, int slot, int func) +{ + u32 d; + u8 b; + + b = read_pci_config_byte(num, slot, func, 0xac); + b &= ~(1<<5); + write_pci_config_byte(num, slot, func, 0xac, b); + + d = read_pci_config(num, slot, func, 0x70); + d |= 1<<8; + write_pci_config(num, slot, func, 0x70, d); + + d = read_pci_config(num, slot, func, 0x8); + d &= 0xff; + return d; +} + +static void __init ati_bugs(int num, int slot, int func) +{ +#if defined(CONFIG_ACPI) && defined (CONFIG_X86_IO_APIC) + u32 d; + u8 b; + + if (acpi_use_timer_override) + return; + + d = ati_ixp4x0_rev(num, slot, func); + if (d < 0x82) + acpi_skip_timer_override = 1; + else { + /* check for IRQ0 interrupt swap */ + outb(0x72, 0xcd6); b = inb(0xcd7); + if (!(b & 0x2)) + acpi_skip_timer_override = 1; + } + + if (acpi_skip_timer_override) { + printk(KERN_INFO "SB4X0 revision 0x%x\n", d); + printk(KERN_INFO "Ignoring ACPI timer override.\n"); + printk(KERN_INFO "If you got timer trouble " + "try acpi_use_timer_override\n"); + } +#endif +} + #ifdef CONFIG_DMAR static void __init intel_g33_dmar(int num, int slot, int func) { @@ -128,6 +174,8 @@ static struct chipset early_qrk[] __initdata = { PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config }, + { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS, + PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs }, #ifdef CONFIG_DMAR { PCI_VENDOR_ID_INTEL, 0x29c0, PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, intel_g33_dmar }, diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index ff9e7350da5..34ad997d383 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -3,11 +3,19 @@ #include <linux/init.h> #include <linux/string.h> #include <linux/screen_info.h> +#include <linux/usb/ch9.h> +#include <linux/pci_regs.h> +#include <linux/pci_ids.h> +#include <linux/errno.h> #include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> #include <asm/setup.h> #include <xen/hvc-console.h> +#include <asm/pci-direct.h> +#include <asm/pgtable.h> +#include <asm/fixmap.h> +#include <linux/usb/ehci_def.h> /* Simple VGA output */ #define VGABASE (__ISA_IO_base + 0xb8000) @@ -78,6 +86,7 @@ static int early_serial_base = 0x3f8; /* ttyS0 */ static int early_serial_putc(unsigned char ch) { unsigned timeout = 0xffff; + while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout) cpu_relax(); outb(ch, early_serial_base + TXR); @@ -111,7 +120,7 @@ static __init void early_serial_init(char *s) if (!strncmp(s, "0x", 2)) { early_serial_base = simple_strtoul(s, &e, 16); } else { - static int bases[] = { 0x3f8, 0x2f8 }; + static const int __initconst bases[] = { 0x3f8, 0x2f8 }; if (!strncmp(s, "ttyS", 4)) s += 4; @@ -151,6 +160,721 @@ static struct console early_serial_console = { .index = -1, }; +#ifdef CONFIG_EARLY_PRINTK_DBGP + +static struct ehci_caps __iomem *ehci_caps; +static struct ehci_regs __iomem *ehci_regs; +static struct ehci_dbg_port __iomem *ehci_debug; +static unsigned int dbgp_endpoint_out; + +struct ehci_dev { + u32 bus; + u32 slot; + u32 func; +}; + +static struct ehci_dev ehci_dev; + +#define USB_DEBUG_DEVNUM 127 + +#define DBGP_DATA_TOGGLE 0x8800 + +static inline u32 dbgp_pid_update(u32 x, u32 tok) +{ + return ((x ^ DBGP_DATA_TOGGLE) & 0xffff00) | (tok & 0xff); +} + +static inline u32 dbgp_len_update(u32 x, u32 len) +{ + return (x & ~0x0f) | (len & 0x0f); +} + +/* + * USB Packet IDs (PIDs) + */ + +/* token */ +#define USB_PID_OUT 0xe1 +#define USB_PID_IN 0x69 +#define USB_PID_SOF 0xa5 +#define USB_PID_SETUP 0x2d +/* handshake */ +#define USB_PID_ACK 0xd2 +#define USB_PID_NAK 0x5a +#define USB_PID_STALL 0x1e +#define USB_PID_NYET 0x96 +/* data */ +#define USB_PID_DATA0 0xc3 +#define USB_PID_DATA1 0x4b +#define USB_PID_DATA2 0x87 +#define USB_PID_MDATA 0x0f +/* Special */ +#define USB_PID_PREAMBLE 0x3c +#define USB_PID_ERR 0x3c +#define USB_PID_SPLIT 0x78 +#define USB_PID_PING 0xb4 +#define USB_PID_UNDEF_0 0xf0 + +#define USB_PID_DATA_TOGGLE 0x88 +#define DBGP_CLAIM (DBGP_OWNER | DBGP_ENABLED | DBGP_INUSE) + +#define PCI_CAP_ID_EHCI_DEBUG 0xa + +#define HUB_ROOT_RESET_TIME 50 /* times are in msec */ +#define HUB_SHORT_RESET_TIME 10 +#define HUB_LONG_RESET_TIME 200 +#define HUB_RESET_TIMEOUT 500 + +#define DBGP_MAX_PACKET 8 + +static int dbgp_wait_until_complete(void) +{ + u32 ctrl; + int loop = 0x100000; + + do { + ctrl = readl(&ehci_debug->control); + /* Stop when the transaction is finished */ + if (ctrl & DBGP_DONE) + break; + } while (--loop > 0); + + if (!loop) + return -1; + + /* + * Now that we have observed the completed transaction, + * clear the done bit. + */ + writel(ctrl | DBGP_DONE, &ehci_debug->control); + return (ctrl & DBGP_ERROR) ? -DBGP_ERRCODE(ctrl) : DBGP_LEN(ctrl); +} + +static void dbgp_mdelay(int ms) +{ + int i; + + while (ms--) { + for (i = 0; i < 1000; i++) + outb(0x1, 0x80); + } +} + +static void dbgp_breath(void) +{ + /* Sleep to give the debug port a chance to breathe */ +} + +static int dbgp_wait_until_done(unsigned ctrl) +{ + u32 pids, lpid; + int ret; + int loop = 3; + +retry: + writel(ctrl | DBGP_GO, &ehci_debug->control); + ret = dbgp_wait_until_complete(); + pids = readl(&ehci_debug->pids); + lpid = DBGP_PID_GET(pids); + + if (ret < 0) + return ret; + + /* + * If the port is getting full or it has dropped data + * start pacing ourselves, not necessary but it's friendly. + */ + if ((lpid == USB_PID_NAK) || (lpid == USB_PID_NYET)) + dbgp_breath(); + + /* If I get a NACK reissue the transmission */ + if (lpid == USB_PID_NAK) { + if (--loop > 0) + goto retry; + } + + return ret; +} + +static void dbgp_set_data(const void *buf, int size) +{ + const unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = hi = 0; + for (i = 0; i < 4 && i < size; i++) + lo |= bytes[i] << (8*i); + for (; i < 8 && i < size; i++) + hi |= bytes[i] << (8*(i - 4)); + writel(lo, &ehci_debug->data03); + writel(hi, &ehci_debug->data47); +} + +static void dbgp_get_data(void *buf, int size) +{ + unsigned char *bytes = buf; + u32 lo, hi; + int i; + + lo = readl(&ehci_debug->data03); + hi = readl(&ehci_debug->data47); + for (i = 0; i < 4 && i < size; i++) + bytes[i] = (lo >> (8*i)) & 0xff; + for (; i < 8 && i < size; i++) + bytes[i] = (hi >> (8*(i - 4))) & 0xff; +} + +static int dbgp_bulk_write(unsigned devnum, unsigned endpoint, + const char *bytes, int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_OUT); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + dbgp_set_data(bytes, size); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + return ret; +} + +static int dbgp_bulk_read(unsigned devnum, unsigned endpoint, void *data, + int size) +{ + u32 pids, addr, ctrl; + int ret; + + if (size > DBGP_MAX_PACKET) + return -1; + + addr = DBGP_EPADDR(devnum, endpoint); + + pids = readl(&ehci_debug->pids); + pids = dbgp_pid_update(pids, USB_PID_IN); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, size); + ctrl &= ~DBGP_OUT; + ctrl |= DBGP_GO; + + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + if (size > ret) + size = ret; + dbgp_get_data(data, size); + return ret; +} + +static int dbgp_control_msg(unsigned devnum, int requesttype, int request, + int value, int index, void *data, int size) +{ + u32 pids, addr, ctrl; + struct usb_ctrlrequest req; + int read; + int ret; + + read = (requesttype & USB_DIR_IN) != 0; + if (size > (read ? DBGP_MAX_PACKET:0)) + return -1; + + /* Compute the control message */ + req.bRequestType = requesttype; + req.bRequest = request; + req.wValue = cpu_to_le16(value); + req.wIndex = cpu_to_le16(index); + req.wLength = cpu_to_le16(size); + + pids = DBGP_PID_SET(USB_PID_DATA0, USB_PID_SETUP); + addr = DBGP_EPADDR(devnum, 0); + + ctrl = readl(&ehci_debug->control); + ctrl = dbgp_len_update(ctrl, sizeof(req)); + ctrl |= DBGP_OUT; + ctrl |= DBGP_GO; + + /* Send the setup message */ + dbgp_set_data(&req, sizeof(req)); + writel(addr, &ehci_debug->address); + writel(pids, &ehci_debug->pids); + ret = dbgp_wait_until_done(ctrl); + if (ret < 0) + return ret; + + /* Read the result */ + return dbgp_bulk_read(devnum, 0, data, size); +} + + +/* Find a PCI capability */ +static u32 __init find_cap(u32 num, u32 slot, u32 func, int cap) +{ + u8 pos; + int bytes; + + if (!(read_pci_config_16(num, slot, func, PCI_STATUS) & + PCI_STATUS_CAP_LIST)) + return 0; + + pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST); + for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) { + u8 id; + + pos &= ~3; + id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID); + if (id == 0xff) + break; + if (id == cap) + return pos; + + pos = read_pci_config_byte(num, slot, func, + pos+PCI_CAP_LIST_NEXT); + } + return 0; +} + +static u32 __init __find_dbgp(u32 bus, u32 slot, u32 func) +{ + u32 class; + + class = read_pci_config(bus, slot, func, PCI_CLASS_REVISION); + if ((class >> 8) != PCI_CLASS_SERIAL_USB_EHCI) + return 0; + + return find_cap(bus, slot, func, PCI_CAP_ID_EHCI_DEBUG); +} + +static u32 __init find_dbgp(int ehci_num, u32 *rbus, u32 *rslot, u32 *rfunc) +{ + u32 bus, slot, func; + + for (bus = 0; bus < 256; bus++) { + for (slot = 0; slot < 32; slot++) { + for (func = 0; func < 8; func++) { + unsigned cap; + + cap = __find_dbgp(bus, slot, func); + + if (!cap) + continue; + if (ehci_num-- != 0) + continue; + *rbus = bus; + *rslot = slot; + *rfunc = func; + return cap; + } + } + } + return 0; +} + +static int ehci_reset_port(int port) +{ + u32 portsc; + u32 delay_time, delay; + int loop; + + /* Reset the usb debug port */ + portsc = readl(&ehci_regs->port_status[port - 1]); + portsc &= ~PORT_PE; + portsc |= PORT_RESET; + writel(portsc, &ehci_regs->port_status[port - 1]); + + delay = HUB_ROOT_RESET_TIME; + for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; + delay_time += delay) { + dbgp_mdelay(delay); + + portsc = readl(&ehci_regs->port_status[port - 1]); + if (portsc & PORT_RESET) { + /* force reset to complete */ + loop = 2; + writel(portsc & ~(PORT_RWC_BITS | PORT_RESET), + &ehci_regs->port_status[port - 1]); + do { + portsc = readl(&ehci_regs->port_status[port-1]); + } while ((portsc & PORT_RESET) && (--loop > 0)); + } + + /* Device went away? */ + if (!(portsc & PORT_CONNECT)) + return -ENOTCONN; + + /* bomb out completely if something weird happend */ + if ((portsc & PORT_CSC)) + return -EINVAL; + + /* If we've finished resetting, then break out of the loop */ + if (!(portsc & PORT_RESET) && (portsc & PORT_PE)) + return 0; + } + return -EBUSY; +} + +static int ehci_wait_for_port(int port) +{ + u32 status; + int ret, reps; + + for (reps = 0; reps < 3; reps++) { + dbgp_mdelay(100); + status = readl(&ehci_regs->status); + if (status & STS_PCD) { + ret = ehci_reset_port(port); + if (ret == 0) + return 0; + } + } + return -ENOTCONN; +} + +#ifdef DBGP_DEBUG +# define dbgp_printk early_printk +#else +static inline void dbgp_printk(const char *fmt, ...) { } +#endif + +typedef void (*set_debug_port_t)(int port); + +static void default_set_debug_port(int port) +{ +} + +static set_debug_port_t set_debug_port = default_set_debug_port; + +static void nvidia_set_debug_port(int port) +{ + u32 dword; + dword = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x74); + dword &= ~(0x0f<<12); + dword |= ((port & 0x0f)<<12); + write_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, 0x74, + dword); + dbgp_printk("set debug port to %d\n", port); +} + +static void __init detect_set_debug_port(void) +{ + u32 vendorid; + + vendorid = read_pci_config(ehci_dev.bus, ehci_dev.slot, ehci_dev.func, + 0x00); + + if ((vendorid & 0xffff) == 0x10de) { + dbgp_printk("using nvidia set_debug_port\n"); + set_debug_port = nvidia_set_debug_port; + } +} + +static int __init ehci_setup(void) +{ + struct usb_debug_descriptor dbgp_desc; + u32 cmd, ctrl, status, portsc, hcs_params; + u32 debug_port, new_debug_port = 0, n_ports; + u32 devnum; + int ret, i; + int loop; + int port_map_tried; + int playtimes = 3; + +try_next_time: + port_map_tried = 0; + +try_next_port: + + hcs_params = readl(&ehci_caps->hcs_params); + debug_port = HCS_DEBUG_PORT(hcs_params); + n_ports = HCS_N_PORTS(hcs_params); + + dbgp_printk("debug_port: %d\n", debug_port); + dbgp_printk("n_ports: %d\n", n_ports); + + for (i = 1; i <= n_ports; i++) { + portsc = readl(&ehci_regs->port_status[i-1]); + dbgp_printk("portstatus%d: %08x\n", i, portsc); + } + + if (port_map_tried && (new_debug_port != debug_port)) { + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + return -1; + } + + loop = 10; + /* Reset the EHCI controller */ + cmd = readl(&ehci_regs->command); + cmd |= CMD_RESET; + writel(cmd, &ehci_regs->command); + do { + cmd = readl(&ehci_regs->command); + } while ((cmd & CMD_RESET) && (--loop > 0)); + + if (!loop) { + dbgp_printk("can not reset ehci\n"); + return -1; + } + dbgp_printk("ehci reset done\n"); + + /* Claim ownership, but do not enable yet */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_OWNER; + ctrl &= ~(DBGP_ENABLED | DBGP_INUSE); + writel(ctrl, &ehci_debug->control); + + /* Start the ehci running */ + cmd = readl(&ehci_regs->command); + cmd &= ~(CMD_LRESET | CMD_IAAD | CMD_PSE | CMD_ASE | CMD_RESET); + cmd |= CMD_RUN; + writel(cmd, &ehci_regs->command); + + /* Ensure everything is routed to the EHCI */ + writel(FLAG_CF, &ehci_regs->configured_flag); + + /* Wait until the controller is no longer halted */ + loop = 10; + do { + status = readl(&ehci_regs->status); + } while ((status & STS_HALT) && (--loop > 0)); + + if (!loop) { + dbgp_printk("ehci can be started\n"); + return -1; + } + dbgp_printk("ehci started\n"); + + /* Wait for a device to show up in the debug port */ + ret = ehci_wait_for_port(debug_port); + if (ret < 0) { + dbgp_printk("No device found in debug port\n"); + goto next_debug_port; + } + dbgp_printk("ehci wait for port done\n"); + + /* Enable the debug port */ + ctrl = readl(&ehci_debug->control); + ctrl |= DBGP_CLAIM; + writel(ctrl, &ehci_debug->control); + ctrl = readl(&ehci_debug->control); + if ((ctrl & DBGP_CLAIM) != DBGP_CLAIM) { + dbgp_printk("No device in debug port\n"); + writel(ctrl & ~DBGP_CLAIM, &ehci_debug->control); + goto err; + } + dbgp_printk("debug ported enabled\n"); + + /* Completely transfer the debug device to the debug controller */ + portsc = readl(&ehci_regs->port_status[debug_port - 1]); + portsc &= ~PORT_PE; + writel(portsc, &ehci_regs->port_status[debug_port - 1]); + + dbgp_mdelay(100); + + /* Find the debug device and make it device number 127 */ + for (devnum = 0; devnum <= 127; devnum++) { + ret = dbgp_control_msg(devnum, + USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_GET_DESCRIPTOR, (USB_DT_DEBUG << 8), 0, + &dbgp_desc, sizeof(dbgp_desc)); + if (ret > 0) + break; + } + if (devnum > 127) { + dbgp_printk("Could not find attached debug device\n"); + goto err; + } + if (ret < 0) { + dbgp_printk("Attached device is not a debug device\n"); + goto err; + } + dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint; + + /* Move the device to 127 if it isn't already there */ + if (devnum != USB_DEBUG_DEVNUM) { + ret = dbgp_control_msg(devnum, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_ADDRESS, USB_DEBUG_DEVNUM, 0, NULL, 0); + if (ret < 0) { + dbgp_printk("Could not move attached device to %d\n", + USB_DEBUG_DEVNUM); + goto err; + } + devnum = USB_DEBUG_DEVNUM; + dbgp_printk("debug device renamed to 127\n"); + } + + /* Enable the debug interface */ + ret = dbgp_control_msg(USB_DEBUG_DEVNUM, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_REQ_SET_FEATURE, USB_DEVICE_DEBUG_MODE, 0, NULL, 0); + if (ret < 0) { + dbgp_printk(" Could not enable the debug device\n"); + goto err; + } + dbgp_printk("debug interface enabled\n"); + + /* Perform a small write to get the even/odd data state in sync + */ + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, dbgp_endpoint_out, " ", 1); + if (ret < 0) { + dbgp_printk("dbgp_bulk_write failed: %d\n", ret); + goto err; + } + dbgp_printk("small write doned\n"); + + return 0; +err: + /* Things didn't work so remove my claim */ + ctrl = readl(&ehci_debug->control); + ctrl &= ~(DBGP_CLAIM | DBGP_OUT); + writel(ctrl, &ehci_debug->control); + return -1; + +next_debug_port: + port_map_tried |= (1<<(debug_port - 1)); + new_debug_port = ((debug_port-1+1)%n_ports) + 1; + if (port_map_tried != ((1<<n_ports) - 1)) { + set_debug_port(new_debug_port); + goto try_next_port; + } + if (--playtimes) { + set_debug_port(new_debug_port); + goto try_next_time; + } + + return -1; +} + +static int __init early_dbgp_init(char *s) +{ + u32 debug_port, bar, offset; + u32 bus, slot, func, cap; + void __iomem *ehci_bar; + u32 dbgp_num; + u32 bar_val; + char *e; + int ret; + u8 byte; + + if (!early_pci_allowed()) + return -1; + + dbgp_num = 0; + if (*s) + dbgp_num = simple_strtoul(s, &e, 10); + dbgp_printk("dbgp_num: %d\n", dbgp_num); + + cap = find_dbgp(dbgp_num, &bus, &slot, &func); + if (!cap) + return -1; + + dbgp_printk("Found EHCI debug port on %02x:%02x.%1x\n", bus, slot, + func); + + debug_port = read_pci_config(bus, slot, func, cap); + bar = (debug_port >> 29) & 0x7; + bar = (bar * 4) + 0xc; + offset = (debug_port >> 16) & 0xfff; + dbgp_printk("bar: %02x offset: %03x\n", bar, offset); + if (bar != PCI_BASE_ADDRESS_0) { + dbgp_printk("only debug ports on bar 1 handled.\n"); + + return -1; + } + + bar_val = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); + dbgp_printk("bar_val: %02x offset: %03x\n", bar_val, offset); + if (bar_val & ~PCI_BASE_ADDRESS_MEM_MASK) { + dbgp_printk("only simple 32bit mmio bars supported\n"); + + return -1; + } + + /* double check if the mem space is enabled */ + byte = read_pci_config_byte(bus, slot, func, 0x04); + if (!(byte & 0x2)) { + byte |= 0x02; + write_pci_config_byte(bus, slot, func, 0x04, byte); + dbgp_printk("mmio for ehci enabled\n"); + } + + /* + * FIXME I don't have the bar size so just guess PAGE_SIZE is more + * than enough. 1K is the biggest I have seen. + */ + set_fixmap_nocache(FIX_DBGP_BASE, bar_val & PAGE_MASK); + ehci_bar = (void __iomem *)__fix_to_virt(FIX_DBGP_BASE); + ehci_bar += bar_val & ~PAGE_MASK; + dbgp_printk("ehci_bar: %p\n", ehci_bar); + + ehci_caps = ehci_bar; + ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_debug = ehci_bar + offset; + ehci_dev.bus = bus; + ehci_dev.slot = slot; + ehci_dev.func = func; + + detect_set_debug_port(); + + ret = ehci_setup(); + if (ret < 0) { + dbgp_printk("ehci_setup failed\n"); + ehci_debug = NULL; + + return -1; + } + + return 0; +} + +static void early_dbgp_write(struct console *con, const char *str, u32 n) +{ + int chunk, ret; + + if (!ehci_debug) + return; + while (n > 0) { + chunk = n; + if (chunk > DBGP_MAX_PACKET) + chunk = DBGP_MAX_PACKET; + ret = dbgp_bulk_write(USB_DEBUG_DEVNUM, + dbgp_endpoint_out, str, chunk); + str += chunk; + n -= chunk; + } +} + +static struct console early_dbgp_console = { + .name = "earlydbg", + .write = early_dbgp_write, + .flags = CON_PRINTBUFFER, + .index = -1, +}; +#endif + /* Console interface to a host file on AMD's SimNow! */ static int simnow_fd; @@ -165,6 +889,7 @@ enum { static noinline long simnow(long cmd, long a, long b, long c) { long ret; + asm volatile("cpuid" : "=a" (ret) : "b" (a), "c" (b), "d" (c), "0" (MAGIC1), "D" (cmd + MAGIC2)); @@ -174,6 +899,7 @@ static noinline long simnow(long cmd, long a, long b, long c) static void __init simnow_init(char *str) { char *fn = "klog"; + if (*str == '=') fn = ++str; /* error ignored */ @@ -194,7 +920,7 @@ static struct console simnow_console = { /* Direct interface for emergencies */ static struct console *early_console = &early_vga_console; -static int early_console_initialized; +static int __initdata early_console_initialized; asmlinkage void early_printk(const char *fmt, ...) { @@ -208,10 +934,11 @@ asmlinkage void early_printk(const char *fmt, ...) va_end(ap); } -static int __initdata keep_early; static int __init setup_early_printk(char *buf) { + int keep_early; + if (!buf) return 0; @@ -219,8 +946,7 @@ static int __init setup_early_printk(char *buf) return 0; early_console_initialized = 1; - if (strstr(buf, "keep")) - keep_early = 1; + keep_early = (strstr(buf, "keep") != NULL); if (!strncmp(buf, "serial", 6)) { early_serial_init(buf + 6); @@ -238,6 +964,17 @@ static int __init setup_early_printk(char *buf) simnow_init(buf + 6); early_console = &simnow_console; keep_early = 1; +#ifdef CONFIG_EARLY_PRINTK_DBGP + } else if (!strncmp(buf, "dbgp", 4)) { + if (early_dbgp_init(buf+4) < 0) + return 0; + early_console = &early_dbgp_console; + /* + * usb subsys will reset ehci controller, so don't keep + * that early console + */ + keep_early = 0; +#endif #ifdef CONFIG_HVC_XEN } else if (!strncmp(buf, "xen", 3)) { early_console = &xenboot_console; @@ -251,4 +988,5 @@ static int __init setup_early_printk(char *buf) register_console(early_console); return 0; } + early_param("earlyprintk", setup_early_printk); diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index 109792bc7cf..b21fbfaffe3 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S @@ -730,6 +730,7 @@ error_code: movl $(__USER_DS), %ecx movl %ecx, %ds movl %ecx, %es + TRACE_IRQS_OFF movl %esp,%eax # pt_regs pointer call *%edi jmp ret_from_exception @@ -760,20 +761,9 @@ ENTRY(device_not_available) RING0_INT_FRAME pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 - SAVE_ALL - GET_CR0_INTO_EAX - testl $0x4, %eax # EM (math emulation bit) - jne device_not_available_emulate - preempt_stop(CLBR_ANY) - call math_state_restore - jmp ret_from_exception -device_not_available_emulate: - pushl $0 # temporary storage for ORIG_EIP + pushl $do_device_not_available CFI_ADJUST_CFA_OFFSET 4 - call math_emulate - addl $4, %esp - CFI_ADJUST_CFA_OFFSET -4 - jmp ret_from_exception + jmp error_code CFI_ENDPROC END(device_not_available) @@ -814,6 +804,7 @@ debug_stack_correct: pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF xorl %edx,%edx # error code 0 movl %esp,%eax # pt_regs pointer call do_debug @@ -858,6 +849,7 @@ nmi_stack_correct: pushl %eax CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF xorl %edx,%edx # zero error code movl %esp,%eax # pt_regs pointer call do_nmi @@ -898,6 +890,7 @@ nmi_espfix_stack: pushl %eax CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF FIXUP_ESPFIX_STACK # %eax == %esp xorl %edx,%edx # zero error code call do_nmi @@ -928,6 +921,7 @@ KPROBE_ENTRY(int3) pushl $-1 # mark this as an int CFI_ADJUST_CFA_OFFSET 4 SAVE_ALL + TRACE_IRQS_OFF xorl %edx,%edx # zero error code movl %esp,%eax # pt_regs pointer call do_int3 @@ -1030,7 +1024,7 @@ ENTRY(machine_check) RING0_INT_FRAME pushl $0 CFI_ADJUST_CFA_OFFSET 4 - pushl machine_check_vector + pushl $do_machine_check CFI_ADJUST_CFA_OFFSET 4 jmp error_code CFI_ENDPROC diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index cf3a0b2d005..1db6ce4314e 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -667,6 +667,13 @@ END(stub_rt_sigreturn) SAVE_ARGS leaq -ARGOFFSET(%rsp),%rdi # arg1 for handler pushq %rbp + /* + * Save rbp twice: One is for marking the stack frame, as usual, and the + * other, to fill pt_regs properly. This is because bx comes right + * before the last saved register in that structure, and not bp. If the + * base pointer were in the place bx is today, this would not be needed. + */ + movq %rbp, -8(%rsp) CFI_ADJUST_CFA_OFFSET 8 CFI_REL_OFFSET rbp, 0 movq %rsp,%rbp @@ -932,6 +939,9 @@ END(spurious_interrupt) .if \ist movq %gs:pda_data_offset, %rbp .endif + .if \irqtrace + TRACE_IRQS_OFF + .endif movq %rsp,%rdi movq ORIG_RAX(%rsp),%rsi movq $-1,ORIG_RAX(%rsp) @@ -1058,7 +1068,8 @@ KPROBE_ENTRY(error_entry) je error_kernelspace error_swapgs: SWAPGS -error_sti: +error_sti: + TRACE_IRQS_OFF movq %rdi,RDI(%rsp) CFI_REL_OFFSET rdi,RDI movq %rsp,%rdi @@ -1232,7 +1243,7 @@ ENTRY(simd_coprocessor_error) END(simd_coprocessor_error) ENTRY(device_not_available) - zeroentry math_state_restore + zeroentry do_device_not_available END(device_not_available) /* runs on exception stack */ diff --git a/arch/x86/kernel/es7000_32.c b/arch/x86/kernel/es7000_32.c index 849e5cd485b..f454c78fcef 100644 --- a/arch/x86/kernel/es7000_32.c +++ b/arch/x86/kernel/es7000_32.c @@ -109,6 +109,7 @@ struct oem_table { }; extern int find_unisys_acpi_oem_table(unsigned long *oem_addr); +extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr); #endif struct mip_reg { @@ -243,21 +244,38 @@ parse_unisys_oem (char *oemptr) } #ifdef CONFIG_ACPI -int __init -find_unisys_acpi_oem_table(unsigned long *oem_addr) +static unsigned long oem_addrX; +static unsigned long oem_size; +int __init find_unisys_acpi_oem_table(unsigned long *oem_addr) { struct acpi_table_header *header = NULL; int i = 0; - while (ACPI_SUCCESS(acpi_get_table("OEM1", i++, &header))) { + acpi_size tbl_size; + + while (ACPI_SUCCESS(acpi_get_table_with_size("OEM1", i++, &header, &tbl_size))) { if (!memcmp((char *) &header->oem_id, "UNISYS", 6)) { struct oem_table *t = (struct oem_table *)header; - *oem_addr = (unsigned long)__acpi_map_table(t->OEMTableAddr, - t->OEMTableSize); + + oem_addrX = t->OEMTableAddr; + oem_size = t->OEMTableSize; + early_acpi_os_unmap_memory(header, tbl_size); + + *oem_addr = (unsigned long)__acpi_map_table(oem_addrX, + oem_size); return 0; } + early_acpi_os_unmap_memory(header, tbl_size); } return -1; } + +void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr) +{ + if (!oem_addr) + return; + + __acpi_unmap_table((char *)oem_addr, oem_size); +} #endif static void diff --git a/arch/x86/kernel/genx2apic_uv_x.c b/arch/x86/kernel/genx2apic_uv_x.c index ae2ffc8a400..33581d94a90 100644 --- a/arch/x86/kernel/genx2apic_uv_x.c +++ b/arch/x86/kernel/genx2apic_uv_x.c @@ -114,7 +114,7 @@ static void uv_send_IPI_one(int cpu, int vector) unsigned long val, apicid, lapicid; int pnode; - apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */ + apicid = per_cpu(x86_cpu_to_apicid, cpu); lapicid = apicid & 0x3f; /* ZZZ macro needed */ pnode = uv_apicid_to_pnode(apicid); val = @@ -202,12 +202,10 @@ static unsigned int phys_pkg_id(int index_msb) return uv_read_apic_id() >> index_msb; } -#ifdef ZZZ /* Needs x2apic patch */ static void uv_send_IPI_self(int vector) { apic_write(APIC_SELF_IPI, vector); } -#endif struct genapic apic_x2apic_uv_x = { .name = "UV large system", @@ -215,15 +213,15 @@ struct genapic apic_x2apic_uv_x = { .int_delivery_mode = dest_Fixed, .int_dest_mode = (APIC_DEST_PHYSICAL != 0), .target_cpus = uv_target_cpus, - .vector_allocation_domain = uv_vector_allocation_domain,/* Fixme ZZZ */ + .vector_allocation_domain = uv_vector_allocation_domain, .apic_id_registered = uv_apic_id_registered, .init_apic_ldr = uv_init_apic_ldr, .send_IPI_all = uv_send_IPI_all, .send_IPI_allbutself = uv_send_IPI_allbutself, .send_IPI_mask = uv_send_IPI_mask, - /* ZZZ.send_IPI_self = uv_send_IPI_self, */ + .send_IPI_self = uv_send_IPI_self, .cpu_mask_to_apicid = uv_cpu_mask_to_apicid, - .phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */ + .phys_pkg_id = phys_pkg_id, .get_apic_id = get_apic_id, .set_apic_id = set_apic_id, .apic_id_mask = (0xFFFFFFFFu), @@ -286,12 +284,13 @@ static __init void map_low_mmrs(void) enum map_type {map_wb, map_uc}; -static __init void map_high(char *id, unsigned long base, int shift, enum map_type map_type) +static __init void map_high(char *id, unsigned long base, int shift, + int max_pnode, enum map_type map_type) { unsigned long bytes, paddr; paddr = base << shift; - bytes = (1UL << shift); + bytes = (1UL << shift) * (max_pnode + 1); printk(KERN_INFO "UV: Map %s_HI 0x%lx - 0x%lx\n", id, paddr, paddr + bytes); if (map_type == map_uc) @@ -307,7 +306,7 @@ static __init void map_gru_high(int max_pnode) gru.v = uv_read_local_mmr(UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR); if (gru.s.enable) - map_high("GRU", gru.s.base, shift, map_wb); + map_high("GRU", gru.s.base, shift, max_pnode, map_wb); } static __init void map_config_high(int max_pnode) @@ -317,7 +316,7 @@ static __init void map_config_high(int max_pnode) cfg.v = uv_read_local_mmr(UVH_RH_GAM_CFG_OVERLAY_CONFIG_MMR); if (cfg.s.enable) - map_high("CONFIG", cfg.s.base, shift, map_uc); + map_high("CONFIG", cfg.s.base, shift, max_pnode, map_uc); } static __init void map_mmr_high(int max_pnode) @@ -327,7 +326,7 @@ static __init void map_mmr_high(int max_pnode) mmr.v = uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR); if (mmr.s.enable) - map_high("MMR", mmr.s.base, shift, map_uc); + map_high("MMR", mmr.s.base, shift, max_pnode, map_uc); } static __init void map_mmioh_high(int max_pnode) @@ -337,7 +336,7 @@ static __init void map_mmioh_high(int max_pnode) mmioh.v = uv_read_local_mmr(UVH_RH_GAM_MMIOH_OVERLAY_CONFIG_MMR); if (mmioh.s.enable) - map_high("MMIOH", mmioh.s.base, shift, map_uc); + map_high("MMIOH", mmioh.s.base, shift, max_pnode, map_uc); } static __init void uv_rtc_init(void) diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c index 3e66bd364a9..1dcb0f13897 100644 --- a/arch/x86/kernel/head.c +++ b/arch/x86/kernel/head.c @@ -35,6 +35,7 @@ void __init reserve_ebda_region(void) /* start of EBDA area */ ebda_addr = get_bios_ebda(); + printk(KERN_INFO "BIOS EBDA/lowmem at: %08x/%08x\n", ebda_addr, lowmem); /* Fixup: bios puts an EBDA in the top 64K segment */ /* of conventional memory, but does not adjust lowmem. */ diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 73deaffadd0..acf62fc233d 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -115,13 +115,17 @@ static void hpet_reserve_platform_timers(unsigned long id) hd.hd_phys_address = hpet_address; hd.hd_address = hpet; hd.hd_nirqs = nrtimers; - hd.hd_flags = HPET_DATA_PLATFORM; hpet_reserve_timer(&hd, 0); #ifdef CONFIG_HPET_EMULATE_RTC hpet_reserve_timer(&hd, 1); #endif + /* + * NOTE that hd_irq[] reflects IOAPIC input pins (LEGACY_8254 + * is wrong for i8259!) not the output IRQ. Many BIOS writers + * don't bother configuring *any* comparator interrupts. + */ hd.hd_irq[0] = HPET_LEGACY_8254; hd.hd_irq[1] = HPET_LEGACY_RTC; diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c index 45723f1fe19..1f20608d4ca 100644 --- a/arch/x86/kernel/i387.c +++ b/arch/x86/kernel/i387.c @@ -468,9 +468,23 @@ static int save_i387_fxsave(struct _fpstate_ia32 __user *buf) static int save_i387_xsave(void __user *buf) { + struct task_struct *tsk = current; struct _fpstate_ia32 __user *fx = buf; int err = 0; + /* + * For legacy compatible, we always set FP/SSE bits in the bit + * vector while saving the state to the user context. + * This will enable us capturing any changes(during sigreturn) to + * the FP/SSE bits by the legacy applications which don't touch + * xstate_bv in the xsave header. + * + * xsave aware applications can change the xstate_bv in the xsave + * header as well as change any contents in the memory layout. + * xrestore as part of sigreturn will capture all the changes. + */ + tsk->thread.xstate->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE; + if (save_i387_fxsave(fx) < 0) return -1; diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c index a1bec2969c6..02063ae042f 100644 --- a/arch/x86/kernel/io_apic_64.c +++ b/arch/x86/kernel/io_apic_64.c @@ -1281,8 +1281,8 @@ __apicdebuginit(void) print_local_APIC(void *dummy) printk(KERN_DEBUG "... APIC ESR: %08x\n", v); icr = apic_icr_read(); - printk(KERN_DEBUG "... APIC ICR: %08x\n", icr); - printk(KERN_DEBUG "... APIC ICR2: %08x\n", icr >> 32); + printk(KERN_DEBUG "... APIC ICR: %08x\n", (u32)icr); + printk(KERN_DEBUG "... APIC ICR2: %08x\n", (u32)(icr >> 32)); v = apic_read(APIC_LVTT); printk(KERN_DEBUG "... APIC LVTT: %08x\n", v); diff --git a/arch/x86/kernel/irqinit_64.c b/arch/x86/kernel/irqinit_64.c index 1f26fd9ec4f..5b5be9d43c2 100644 --- a/arch/x86/kernel/irqinit_64.c +++ b/arch/x86/kernel/irqinit_64.c @@ -135,7 +135,7 @@ DEFINE_PER_CPU(vector_irq_t, vector_irq) = { [IRQ15_VECTOR + 1 ... NR_VECTORS - 1] = -1 }; -static void __init init_ISA_irqs (void) +void __init init_ISA_irqs(void) { int i; @@ -164,22 +164,8 @@ static void __init init_ISA_irqs (void) void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ"))); -void __init native_init_IRQ(void) +static void __init smp_intr_init(void) { - int i; - - init_ISA_irqs(); - /* - * Cover the whole vector space, no vector can escape - * us. (some of these will be overridden and become - * 'special' SMP interrupts) - */ - for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { - int vector = FIRST_EXTERNAL_VECTOR + i; - if (vector != IA32_SYSCALL_VECTOR) - set_intr_gate(vector, interrupt[i]); - } - #ifdef CONFIG_SMP /* * The reschedule interrupt is a CPU-to-CPU reschedule-helper @@ -207,6 +193,12 @@ void __init native_init_IRQ(void) /* Low priority IPI to cleanup after moving an irq */ set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); #endif +} + +static void __init apic_intr_init(void) +{ + smp_intr_init(); + alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt); alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt); @@ -216,6 +208,25 @@ void __init native_init_IRQ(void) /* IPI vectors for APIC spurious and error interrupts */ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt); alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt); +} + +void __init native_init_IRQ(void) +{ + int i; + + init_ISA_irqs(); + /* + * Cover the whole vector space, no vector can escape + * us. (some of these will be overridden and become + * 'special' SMP interrupts) + */ + for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) { + int vector = FIRST_EXTERNAL_VECTOR + i; + if (vector != IA32_SYSCALL_VECTOR) + set_intr_gate(vector, interrupt[i]); + } + + apic_intr_init(); if (!acpi_ioapic) setup_irq(2, &irq2); diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c index 0ed5f939b90..eee32b43fee 100644 --- a/arch/x86/kernel/ldt.c +++ b/arch/x86/kernel/ldt.c @@ -52,6 +52,8 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) memset(newldt + oldsize * LDT_ENTRY_SIZE, 0, (mincount - oldsize) * LDT_ENTRY_SIZE); + paravirt_alloc_ldt(newldt, mincount); + #ifdef CONFIG_X86_64 /* CHECKME: Do we really need this ? */ wmb(); @@ -74,6 +76,7 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) #endif } if (oldsize) { + paravirt_free_ldt(oldldt, oldsize); if (oldsize * LDT_ENTRY_SIZE > PAGE_SIZE) vfree(oldldt); else @@ -85,10 +88,13 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload) static inline int copy_ldt(mm_context_t *new, mm_context_t *old) { int err = alloc_ldt(new, old->size, 0); + int i; if (err < 0) return err; - memcpy(new->ldt, old->ldt, old->size * LDT_ENTRY_SIZE); + + for(i = 0; i < old->size; i++) + write_ldt_entry(new->ldt, i, old->ldt + i * LDT_ENTRY_SIZE); return 0; } @@ -125,6 +131,7 @@ void destroy_context(struct mm_struct *mm) if (mm == current->active_mm) clear_LDT(); #endif + paravirt_free_ldt(mm->context.ldt, mm->context.size); if (mm->context.size * LDT_ENTRY_SIZE > PAGE_SIZE) vfree(mm->context.ldt); else diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c deleted file mode 100644 index 652fa5c38eb..00000000000 --- a/arch/x86/kernel/microcode.c +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Intel CPU Microcode Update Driver for Linux - * - * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> - * 2006 Shaohua Li <shaohua.li@intel.com> - * - * This driver allows to upgrade microcode on Intel processors - * belonging to IA-32 family - PentiumPro, Pentium II, - * Pentium III, Xeon, Pentium 4, etc. - * - * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture - * Software Developer's Manual - * Order Number 253668 or free download from: - * - * http://developer.intel.com/design/pentium4/manuals/253668.htm - * - * For more information, go to http://www.urbanmyth.org/microcode - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Initial release. - * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Added read() support + cleanups. - * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Added 'device trimming' support. open(O_WRONLY) zeroes - * and frees the saved copy of applied microcode. - * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> - * Made to use devfs (/dev/cpu/microcode) + cleanups. - * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> - * Added misc device support (now uses both devfs and misc). - * Added MICROCODE_IOCFREE ioctl to clear memory. - * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> - * Messages for error cases (non Intel & no suitable microcode). - * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> - * Removed ->release(). Removed exclusive open and status bitmap. - * Added microcode_rwsem to serialize read()/write()/ioctl(). - * Removed global kernel lock usage. - * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> - * Write 0 to 0x8B msr and then cpuid before reading revision, - * so that it works even if there were no update done by the - * BIOS. Otherwise, reading from 0x8B gives junk (which happened - * to be 0 on my machine which is why it worked even when I - * disabled update by the BIOS) - * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. - * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and - * Tigran Aivazian <tigran@veritas.com> - * Intel Pentium 4 processor support and bugfixes. - * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> - * Bugfix for HT (Hyper-Threading) enabled processors - * whereby processor resources are shared by all logical processors - * in a single CPU package. - * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and - * Tigran Aivazian <tigran@veritas.com>, - * Serialize updates as required on HT processors due to speculative - * nature of implementation. - * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> - * Fix the panic when writing zero-length microcode chunk. - * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, - * Jun Nakajima <jun.nakajima@intel.com> - * Support for the microcode updates in the new format. - * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> - * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl - * because we no longer hold a copy of applied microcode - * in kernel memory. - * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> - * Fix sigmatch() macro to handle old CPUs with pf == 0. - * Thanks to Stuart Swales for pointing out this bug. - */ - -//#define DEBUG /* pr_debug */ -#include <linux/capability.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/cpumask.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/miscdevice.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/mutex.h> -#include <linux/cpu.h> -#include <linux/firmware.h> -#include <linux/platform_device.h> - -#include <asm/msr.h> -#include <asm/uaccess.h> -#include <asm/processor.h> - -MODULE_DESCRIPTION("Intel CPU (IA-32) Microcode Update Driver"); -MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); -MODULE_LICENSE("GPL"); - -#define MICROCODE_VERSION "1.14a" - -#define DEFAULT_UCODE_DATASIZE (2000) /* 2000 bytes */ -#define MC_HEADER_SIZE (sizeof (microcode_header_t)) /* 48 bytes */ -#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 2048 bytes */ -#define EXT_HEADER_SIZE (sizeof (struct extended_sigtable)) /* 20 bytes */ -#define EXT_SIGNATURE_SIZE (sizeof (struct extended_signature)) /* 12 bytes */ -#define DWSIZE (sizeof (u32)) -#define get_totalsize(mc) \ - (((microcode_t *)mc)->hdr.totalsize ? \ - ((microcode_t *)mc)->hdr.totalsize : DEFAULT_UCODE_TOTALSIZE) -#define get_datasize(mc) \ - (((microcode_t *)mc)->hdr.datasize ? \ - ((microcode_t *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) - -#define sigmatch(s1, s2, p1, p2) \ - (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) - -#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) - -/* serialize access to the physical write to MSR 0x79 */ -static DEFINE_SPINLOCK(microcode_update_lock); - -/* no concurrent ->write()s are allowed on /dev/cpu/microcode */ -static DEFINE_MUTEX(microcode_mutex); - -static struct ucode_cpu_info { - int valid; - unsigned int sig; - unsigned int pf; - unsigned int rev; - microcode_t *mc; -} ucode_cpu_info[NR_CPUS]; - -static void collect_cpu_info(int cpu_num) -{ - struct cpuinfo_x86 *c = &cpu_data(cpu_num); - struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - unsigned int val[2]; - - /* We should bind the task to the CPU */ - BUG_ON(raw_smp_processor_id() != cpu_num); - uci->pf = uci->rev = 0; - uci->mc = NULL; - uci->valid = 1; - - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || - cpu_has(c, X86_FEATURE_IA64)) { - printk(KERN_ERR "microcode: CPU%d not a capable Intel " - "processor\n", cpu_num); - uci->valid = 0; - return; - } - - uci->sig = cpuid_eax(0x00000001); - - if ((c->x86_model >= 5) || (c->x86 > 6)) { - /* get processor flags from MSR 0x17 */ - rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); - uci->pf = 1 << ((val[1] >> 18) & 7); - } - - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - /* see notes above for revision 1.07. Apparent chip bug */ - sync_core(); - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], uci->rev); - pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n", - uci->sig, uci->pf, uci->rev); -} - -static inline int microcode_update_match(int cpu_num, - microcode_header_t *mc_header, int sig, int pf) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - - if (!sigmatch(sig, uci->sig, pf, uci->pf) - || mc_header->rev <= uci->rev) - return 0; - return 1; -} - -static int microcode_sanity_check(void *mc) -{ - microcode_header_t *mc_header = mc; - struct extended_sigtable *ext_header = NULL; - struct extended_signature *ext_sig; - unsigned long total_size, data_size, ext_table_size; - int sum, orig_sum, ext_sigcount = 0, i; - - total_size = get_totalsize(mc_header); - data_size = get_datasize(mc_header); - if (data_size + MC_HEADER_SIZE > total_size) { - printk(KERN_ERR "microcode: error! " - "Bad data size in microcode data file\n"); - return -EINVAL; - } - - if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { - printk(KERN_ERR "microcode: error! " - "Unknown microcode update format\n"); - return -EINVAL; - } - ext_table_size = total_size - (MC_HEADER_SIZE + data_size); - if (ext_table_size) { - if ((ext_table_size < EXT_HEADER_SIZE) - || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { - printk(KERN_ERR "microcode: error! " - "Small exttable size in microcode data file\n"); - return -EINVAL; - } - ext_header = mc + MC_HEADER_SIZE + data_size; - if (ext_table_size != exttable_size(ext_header)) { - printk(KERN_ERR "microcode: error! " - "Bad exttable size in microcode data file\n"); - return -EFAULT; - } - ext_sigcount = ext_header->count; - } - - /* check extended table checksum */ - if (ext_table_size) { - int ext_table_sum = 0; - int *ext_tablep = (int *)ext_header; - - i = ext_table_size / DWSIZE; - while (i--) - ext_table_sum += ext_tablep[i]; - if (ext_table_sum) { - printk(KERN_WARNING "microcode: aborting, " - "bad extended signature table checksum\n"); - return -EINVAL; - } - } - - /* calculate the checksum */ - orig_sum = 0; - i = (MC_HEADER_SIZE + data_size) / DWSIZE; - while (i--) - orig_sum += ((int *)mc)[i]; - if (orig_sum) { - printk(KERN_ERR "microcode: aborting, bad checksum\n"); - return -EINVAL; - } - if (!ext_table_size) - return 0; - /* check extended signature checksum */ - for (i = 0; i < ext_sigcount; i++) { - ext_sig = (void *)ext_header + EXT_HEADER_SIZE + - EXT_SIGNATURE_SIZE * i; - sum = orig_sum - - (mc_header->sig + mc_header->pf + mc_header->cksum) - + (ext_sig->sig + ext_sig->pf + ext_sig->cksum); - if (sum) { - printk(KERN_ERR "microcode: aborting, bad checksum\n"); - return -EINVAL; - } - } - return 0; -} - -/* - * return 0 - no update found - * return 1 - found update - * return < 0 - error - */ -static int get_maching_microcode(void *mc, int cpu) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - microcode_header_t *mc_header = mc; - struct extended_sigtable *ext_header; - unsigned long total_size = get_totalsize(mc_header); - int ext_sigcount, i; - struct extended_signature *ext_sig; - void *new_mc; - - if (microcode_update_match(cpu, mc_header, - mc_header->sig, mc_header->pf)) - goto find; - - if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) - return 0; - - ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; - ext_sigcount = ext_header->count; - ext_sig = (void *)ext_header + EXT_HEADER_SIZE; - for (i = 0; i < ext_sigcount; i++) { - if (microcode_update_match(cpu, mc_header, - ext_sig->sig, ext_sig->pf)) - goto find; - ext_sig++; - } - return 0; -find: - pr_debug("microcode: CPU%d found a matching microcode update with" - " version 0x%x (current=0x%x)\n", cpu, mc_header->rev,uci->rev); - new_mc = vmalloc(total_size); - if (!new_mc) { - printk(KERN_ERR "microcode: error! Can not allocate memory\n"); - return -ENOMEM; - } - - /* free previous update file */ - vfree(uci->mc); - - memcpy(new_mc, mc, total_size); - uci->mc = new_mc; - return 1; -} - -static void apply_microcode(int cpu) -{ - unsigned long flags; - unsigned int val[2]; - int cpu_num = raw_smp_processor_id(); - struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; - - /* We should bind the task to the CPU */ - BUG_ON(cpu_num != cpu); - - if (uci->mc == NULL) - return; - - /* serialize access to the physical write to MSR 0x79 */ - spin_lock_irqsave(µcode_update_lock, flags); - - /* write microcode via MSR 0x79 */ - wrmsr(MSR_IA32_UCODE_WRITE, - (unsigned long) uci->mc->bits, - (unsigned long) uci->mc->bits >> 16 >> 16); - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - - /* see notes above for revision 1.07. Apparent chip bug */ - sync_core(); - - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); - - spin_unlock_irqrestore(µcode_update_lock, flags); - if (val[1] != uci->mc->hdr.rev) { - printk(KERN_ERR "microcode: CPU%d update from revision " - "0x%x to 0x%x failed\n", cpu_num, uci->rev, val[1]); - return; - } - printk(KERN_INFO "microcode: CPU%d updated from revision " - "0x%x to 0x%x, date = %08x \n", - cpu_num, uci->rev, val[1], uci->mc->hdr.date); - uci->rev = val[1]; -} - -#ifdef CONFIG_MICROCODE_OLD_INTERFACE -static void __user *user_buffer; /* user area microcode data buffer */ -static unsigned int user_buffer_size; /* it's size */ - -static long get_next_ucode(void **mc, long offset) -{ - microcode_header_t mc_header; - unsigned long total_size; - - /* No more data */ - if (offset >= user_buffer_size) - return 0; - if (copy_from_user(&mc_header, user_buffer + offset, MC_HEADER_SIZE)) { - printk(KERN_ERR "microcode: error! Can not read user data\n"); - return -EFAULT; - } - total_size = get_totalsize(&mc_header); - if (offset + total_size > user_buffer_size) { - printk(KERN_ERR "microcode: error! Bad total size in microcode " - "data file\n"); - return -EINVAL; - } - *mc = vmalloc(total_size); - if (!*mc) - return -ENOMEM; - if (copy_from_user(*mc, user_buffer + offset, total_size)) { - printk(KERN_ERR "microcode: error! Can not read user data\n"); - vfree(*mc); - return -EFAULT; - } - return offset + total_size; -} - -static int do_microcode_update (void) -{ - long cursor = 0; - int error = 0; - void *new_mc = NULL; - int cpu; - cpumask_t old; - - old = current->cpus_allowed; - - while ((cursor = get_next_ucode(&new_mc, cursor)) > 0) { - error = microcode_sanity_check(new_mc); - if (error) - goto out; - /* - * It's possible the data file has multiple matching ucode, - * lets keep searching till the latest version - */ - for_each_online_cpu(cpu) { - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - if (!uci->valid) - continue; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - error = get_maching_microcode(new_mc, cpu); - if (error < 0) - goto out; - if (error == 1) - apply_microcode(cpu); - } - vfree(new_mc); - } -out: - if (cursor > 0) - vfree(new_mc); - if (cursor < 0) - error = cursor; - set_cpus_allowed_ptr(current, &old); - return error; -} - -static int microcode_open (struct inode *unused1, struct file *unused2) -{ - cycle_kernel_lock(); - return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; -} - -static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos) -{ - ssize_t ret; - - if ((len >> PAGE_SHIFT) > num_physpages) { - printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages); - return -EINVAL; - } - - get_online_cpus(); - mutex_lock(µcode_mutex); - - user_buffer = (void __user *) buf; - user_buffer_size = (int) len; - - ret = do_microcode_update(); - if (!ret) - ret = (ssize_t)len; - - mutex_unlock(µcode_mutex); - put_online_cpus(); - - return ret; -} - -static const struct file_operations microcode_fops = { - .owner = THIS_MODULE, - .write = microcode_write, - .open = microcode_open, -}; - -static struct miscdevice microcode_dev = { - .minor = MICROCODE_MINOR, - .name = "microcode", - .fops = µcode_fops, -}; - -static int __init microcode_dev_init (void) -{ - int error; - - error = misc_register(µcode_dev); - if (error) { - printk(KERN_ERR - "microcode: can't misc_register on minor=%d\n", - MICROCODE_MINOR); - return error; - } - - return 0; -} - -static void microcode_dev_exit (void) -{ - misc_deregister(µcode_dev); -} - -MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); -#else -#define microcode_dev_init() 0 -#define microcode_dev_exit() do { } while(0) -#endif - -static long get_next_ucode_from_buffer(void **mc, const u8 *buf, - unsigned long size, long offset) -{ - microcode_header_t *mc_header; - unsigned long total_size; - - /* No more data */ - if (offset >= size) - return 0; - mc_header = (microcode_header_t *)(buf + offset); - total_size = get_totalsize(mc_header); - - if (offset + total_size > size) { - printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); - return -EINVAL; - } - - *mc = vmalloc(total_size); - if (!*mc) { - printk(KERN_ERR "microcode: error! Can not allocate memory\n"); - return -ENOMEM; - } - memcpy(*mc, buf + offset, total_size); - return offset + total_size; -} - -/* fake device for request_firmware */ -static struct platform_device *microcode_pdev; - -static int cpu_request_microcode(int cpu) -{ - char name[30]; - struct cpuinfo_x86 *c = &cpu_data(cpu); - const struct firmware *firmware; - const u8 *buf; - unsigned long size; - long offset = 0; - int error; - void *mc; - - /* We should bind the task to the CPU */ - BUG_ON(cpu != raw_smp_processor_id()); - sprintf(name,"intel-ucode/%02x-%02x-%02x", - c->x86, c->x86_model, c->x86_mask); - error = request_firmware(&firmware, name, µcode_pdev->dev); - if (error) { - pr_debug("microcode: data file %s load failed\n", name); - return error; - } - buf = firmware->data; - size = firmware->size; - while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) - > 0) { - error = microcode_sanity_check(mc); - if (error) - break; - error = get_maching_microcode(mc, cpu); - if (error < 0) - break; - /* - * It's possible the data file has multiple matching ucode, - * lets keep searching till the latest version - */ - if (error == 1) { - apply_microcode(cpu); - error = 0; - } - vfree(mc); - } - if (offset > 0) - vfree(mc); - if (offset < 0) - error = offset; - release_firmware(firmware); - - return error; -} - -static int apply_microcode_check_cpu(int cpu) -{ - struct cpuinfo_x86 *c = &cpu_data(cpu); - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - cpumask_t old; - unsigned int val[2]; - int err = 0; - - /* Check if the microcode is available */ - if (!uci->mc) - return 0; - - old = current->cpus_allowed; - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - - /* Check if the microcode we have in memory matches the CPU */ - if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || - cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001)) - err = -EINVAL; - - if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) { - /* get processor flags from MSR 0x17 */ - rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); - if (uci->pf != (1 << ((val[1] >> 18) & 7))) - err = -EINVAL; - } - - if (!err) { - wrmsr(MSR_IA32_UCODE_REV, 0, 0); - /* see notes above for revision 1.07. Apparent chip bug */ - sync_core(); - /* get the current revision from MSR 0x8B */ - rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); - if (uci->rev != val[1]) - err = -EINVAL; - } - - if (!err) - apply_microcode(cpu); - else - printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:" - " sig=0x%x, pf=0x%x, rev=0x%x\n", - cpu, uci->sig, uci->pf, uci->rev); - - set_cpus_allowed_ptr(current, &old); - return err; -} - -static void microcode_init_cpu(int cpu, int resume) -{ - cpumask_t old; - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - old = current->cpus_allowed; - - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - mutex_lock(µcode_mutex); - collect_cpu_info(cpu); - if (uci->valid && system_state == SYSTEM_RUNNING && !resume) - cpu_request_microcode(cpu); - mutex_unlock(µcode_mutex); - set_cpus_allowed_ptr(current, &old); -} - -static void microcode_fini_cpu(int cpu) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - mutex_lock(µcode_mutex); - uci->valid = 0; - vfree(uci->mc); - uci->mc = NULL; - mutex_unlock(µcode_mutex); -} - -static ssize_t reload_store(struct sys_device *dev, - struct sysdev_attribute *attr, - const char *buf, size_t sz) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; - char *end; - unsigned long val = simple_strtoul(buf, &end, 0); - int err = 0; - int cpu = dev->id; - - if (end == buf) - return -EINVAL; - if (val == 1) { - cpumask_t old = current->cpus_allowed; - - get_online_cpus(); - set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); - - mutex_lock(µcode_mutex); - if (uci->valid) - err = cpu_request_microcode(cpu); - mutex_unlock(µcode_mutex); - put_online_cpus(); - set_cpus_allowed_ptr(current, &old); - } - if (err) - return err; - return sz; -} - -static ssize_t version_show(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; - - return sprintf(buf, "0x%x\n", uci->rev); -} - -static ssize_t pf_show(struct sys_device *dev, - struct sysdev_attribute *attr, char *buf) -{ - struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; - - return sprintf(buf, "0x%x\n", uci->pf); -} - -static SYSDEV_ATTR(reload, 0200, NULL, reload_store); -static SYSDEV_ATTR(version, 0400, version_show, NULL); -static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL); - -static struct attribute *mc_default_attrs[] = { - &attr_reload.attr, - &attr_version.attr, - &attr_processor_flags.attr, - NULL -}; - -static struct attribute_group mc_attr_group = { - .attrs = mc_default_attrs, - .name = "microcode", -}; - -static int __mc_sysdev_add(struct sys_device *sys_dev, int resume) -{ - int err, cpu = sys_dev->id; - struct ucode_cpu_info *uci = ucode_cpu_info + cpu; - - if (!cpu_online(cpu)) - return 0; - - pr_debug("microcode: CPU%d added\n", cpu); - memset(uci, 0, sizeof(*uci)); - - err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); - if (err) - return err; - - microcode_init_cpu(cpu, resume); - - return 0; -} - -static int mc_sysdev_add(struct sys_device *sys_dev) -{ - return __mc_sysdev_add(sys_dev, 0); -} - -static int mc_sysdev_remove(struct sys_device *sys_dev) -{ - int cpu = sys_dev->id; - - if (!cpu_online(cpu)) - return 0; - - pr_debug("microcode: CPU%d removed\n", cpu); - microcode_fini_cpu(cpu); - sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); - return 0; -} - -static int mc_sysdev_resume(struct sys_device *dev) -{ - int cpu = dev->id; - - if (!cpu_online(cpu)) - return 0; - pr_debug("microcode: CPU%d resumed\n", cpu); - /* only CPU 0 will apply ucode here */ - apply_microcode(0); - return 0; -} - -static struct sysdev_driver mc_sysdev_driver = { - .add = mc_sysdev_add, - .remove = mc_sysdev_remove, - .resume = mc_sysdev_resume, -}; - -static __cpuinit int -mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long)hcpu; - struct sys_device *sys_dev; - - sys_dev = get_cpu_sysdev(cpu); - switch (action) { - case CPU_UP_CANCELED_FROZEN: - /* The CPU refused to come up during a system resume */ - microcode_fini_cpu(cpu); - break; - case CPU_ONLINE: - case CPU_DOWN_FAILED: - mc_sysdev_add(sys_dev); - break; - case CPU_ONLINE_FROZEN: - /* System-wide resume is in progress, try to apply microcode */ - if (apply_microcode_check_cpu(cpu)) { - /* The application of microcode failed */ - microcode_fini_cpu(cpu); - __mc_sysdev_add(sys_dev, 1); - break; - } - case CPU_DOWN_FAILED_FROZEN: - if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) - printk(KERN_ERR "microcode: Failed to create the sysfs " - "group for CPU%d\n", cpu); - break; - case CPU_DOWN_PREPARE: - mc_sysdev_remove(sys_dev); - break; - case CPU_DOWN_PREPARE_FROZEN: - /* Suspend is in progress, only remove the interface */ - sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); - break; - } - return NOTIFY_OK; -} - -static struct notifier_block __refdata mc_cpu_notifier = { - .notifier_call = mc_cpu_callback, -}; - -static int __init microcode_init (void) -{ - int error; - - printk(KERN_INFO - "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n"); - - error = microcode_dev_init(); - if (error) - return error; - microcode_pdev = platform_device_register_simple("microcode", -1, - NULL, 0); - if (IS_ERR(microcode_pdev)) { - microcode_dev_exit(); - return PTR_ERR(microcode_pdev); - } - - get_online_cpus(); - error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); - put_online_cpus(); - if (error) { - microcode_dev_exit(); - platform_device_unregister(microcode_pdev); - return error; - } - - register_hotcpu_notifier(&mc_cpu_notifier); - return 0; -} - -static void __exit microcode_exit (void) -{ - microcode_dev_exit(); - - unregister_hotcpu_notifier(&mc_cpu_notifier); - - get_online_cpus(); - sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); - put_online_cpus(); - - platform_device_unregister(microcode_pdev); -} - -module_init(microcode_init) -module_exit(microcode_exit) diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c new file mode 100644 index 00000000000..7a1f8eeac2c --- /dev/null +++ b/arch/x86/kernel/microcode_amd.c @@ -0,0 +1,435 @@ +/* + * AMD CPU Microcode Update Driver for Linux + * Copyright (C) 2008 Advanced Micro Devices Inc. + * + * Author: Peter Oruba <peter.oruba@amd.com> + * + * Based on work by: + * Tigran Aivazian <tigran@aivazian.fsnet.co.uk> + * + * This driver allows to upgrade microcode on AMD + * family 0x10 and 0x11 processors. + * + * Licensed unter the terms of the GNU General Public + * License version 2. See file COPYING for details. +*/ + +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/cpu.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/microcode.h> + +MODULE_DESCRIPTION("AMD Microcode Update Driver"); +MODULE_AUTHOR("Peter Oruba <peter.oruba@amd.com>"); +MODULE_LICENSE("GPL v2"); + +#define UCODE_MAGIC 0x00414d44 +#define UCODE_EQUIV_CPU_TABLE_TYPE 0x00000000 +#define UCODE_UCODE_TYPE 0x00000001 + +struct equiv_cpu_entry { + unsigned int installed_cpu; + unsigned int fixed_errata_mask; + unsigned int fixed_errata_compare; + unsigned int equiv_cpu; +}; + +struct microcode_header_amd { + unsigned int data_code; + unsigned int patch_id; + unsigned char mc_patch_data_id[2]; + unsigned char mc_patch_data_len; + unsigned char init_flag; + unsigned int mc_patch_data_checksum; + unsigned int nb_dev_id; + unsigned int sb_dev_id; + unsigned char processor_rev_id[2]; + unsigned char nb_rev_id; + unsigned char sb_rev_id; + unsigned char bios_api_rev; + unsigned char reserved1[3]; + unsigned int match_reg[8]; +}; + +struct microcode_amd { + struct microcode_header_amd hdr; + unsigned int mpb[0]; +}; + +#define UCODE_MAX_SIZE (2048) +#define DEFAULT_UCODE_DATASIZE (896) +#define MC_HEADER_SIZE (sizeof(struct microcode_header_amd)) +#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) +#define DWSIZE (sizeof(u32)) +/* For now we support a fixed ucode total size only */ +#define get_totalsize(mc) \ + ((((struct microcode_amd *)mc)->hdr.mc_patch_data_len * 28) \ + + MC_HEADER_SIZE) + +/* serialize access to the physical write */ +static DEFINE_SPINLOCK(microcode_update_lock); + +static struct equiv_cpu_entry *equiv_cpu_table; + +static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu); + + memset(csig, 0, sizeof(*csig)); + + if (c->x86_vendor != X86_VENDOR_AMD || c->x86 < 0x10) { + printk(KERN_ERR "microcode: CPU%d not a capable AMD processor\n", + cpu); + return -1; + } + + asm volatile("movl %1, %%ecx; rdmsr" + : "=a" (csig->rev) + : "i" (0x0000008B) : "ecx"); + + printk(KERN_INFO "microcode: collect_cpu_info_amd : patch_id=0x%x\n", + csig->rev); + + return 0; +} + +static int get_matching_microcode(int cpu, void *mc, int rev) +{ + struct microcode_header_amd *mc_header = mc; + struct pci_dev *nb_pci_dev, *sb_pci_dev; + unsigned int current_cpu_id; + unsigned int equiv_cpu_id = 0x00; + unsigned int i = 0; + + BUG_ON(equiv_cpu_table == NULL); + current_cpu_id = cpuid_eax(0x00000001); + + while (equiv_cpu_table[i].installed_cpu != 0) { + if (current_cpu_id == equiv_cpu_table[i].installed_cpu) { + equiv_cpu_id = equiv_cpu_table[i].equiv_cpu; + break; + } + i++; + } + + if (!equiv_cpu_id) { + printk(KERN_ERR "microcode: CPU%d cpu_id " + "not found in equivalent cpu table \n", cpu); + return 0; + } + + if ((mc_header->processor_rev_id[0]) != (equiv_cpu_id & 0xff)) { + printk(KERN_ERR + "microcode: CPU%d patch does not match " + "(patch is %x, cpu extended is %x) \n", + cpu, mc_header->processor_rev_id[0], + (equiv_cpu_id & 0xff)); + return 0; + } + + if ((mc_header->processor_rev_id[1]) != ((equiv_cpu_id >> 16) & 0xff)) { + printk(KERN_ERR "microcode: CPU%d patch does not match " + "(patch is %x, cpu base id is %x) \n", + cpu, mc_header->processor_rev_id[1], + ((equiv_cpu_id >> 16) & 0xff)); + + return 0; + } + + /* ucode may be northbridge specific */ + if (mc_header->nb_dev_id) { + nb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, + (mc_header->nb_dev_id & 0xff), + NULL); + if ((!nb_pci_dev) || + (mc_header->nb_rev_id != nb_pci_dev->revision)) { + printk(KERN_ERR "microcode: CPU%d NB mismatch \n", cpu); + pci_dev_put(nb_pci_dev); + return 0; + } + pci_dev_put(nb_pci_dev); + } + + /* ucode may be southbridge specific */ + if (mc_header->sb_dev_id) { + sb_pci_dev = pci_get_device(PCI_VENDOR_ID_AMD, + (mc_header->sb_dev_id & 0xff), + NULL); + if ((!sb_pci_dev) || + (mc_header->sb_rev_id != sb_pci_dev->revision)) { + printk(KERN_ERR "microcode: CPU%d SB mismatch \n", cpu); + pci_dev_put(sb_pci_dev); + return 0; + } + pci_dev_put(sb_pci_dev); + } + + if (mc_header->patch_id <= rev) + return 0; + + return 1; +} + +static void apply_microcode_amd(int cpu) +{ + unsigned long flags; + unsigned int eax, edx; + unsigned int rev; + int cpu_num = raw_smp_processor_id(); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num; + struct microcode_amd *mc_amd = uci->mc; + unsigned long addr; + + /* We should bind the task to the CPU */ + BUG_ON(cpu_num != cpu); + + if (mc_amd == NULL) + return; + + spin_lock_irqsave(µcode_update_lock, flags); + + addr = (unsigned long)&mc_amd->hdr.data_code; + edx = (unsigned int)(((unsigned long)upper_32_bits(addr))); + eax = (unsigned int)(((unsigned long)lower_32_bits(addr))); + + asm volatile("movl %0, %%ecx; wrmsr" : + : "i" (0xc0010020), "a" (eax), "d" (edx) : "ecx"); + + /* get patch id after patching */ + asm volatile("movl %1, %%ecx; rdmsr" + : "=a" (rev) + : "i" (0x0000008B) : "ecx"); + + spin_unlock_irqrestore(µcode_update_lock, flags); + + /* check current patch id and patch's id for match */ + if (rev != mc_amd->hdr.patch_id) { + printk(KERN_ERR "microcode: CPU%d update from revision " + "0x%x to 0x%x failed\n", cpu_num, + mc_amd->hdr.patch_id, rev); + return; + } + + printk(KERN_INFO "microcode: CPU%d updated from revision " + "0x%x to 0x%x \n", + cpu_num, uci->cpu_sig.rev, mc_amd->hdr.patch_id); + + uci->cpu_sig.rev = rev; +} + +static void * get_next_ucode(u8 *buf, unsigned int size, + int (*get_ucode_data)(void *, const void *, size_t), + unsigned int *mc_size) +{ + unsigned int total_size; +#define UCODE_CONTAINER_SECTION_HDR 8 + u8 section_hdr[UCODE_CONTAINER_SECTION_HDR]; + void *mc; + + if (get_ucode_data(section_hdr, buf, UCODE_CONTAINER_SECTION_HDR)) + return NULL; + + if (section_hdr[0] != UCODE_UCODE_TYPE) { + printk(KERN_ERR "microcode: error! " + "Wrong microcode payload type field\n"); + return NULL; + } + + total_size = (unsigned long) (section_hdr[4] + (section_hdr[5] << 8)); + + printk(KERN_INFO "microcode: size %u, total_size %u\n", + size, total_size); + + if (total_size > size || total_size > UCODE_MAX_SIZE) { + printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); + return NULL; + } + + mc = vmalloc(UCODE_MAX_SIZE); + if (mc) { + memset(mc, 0, UCODE_MAX_SIZE); + if (get_ucode_data(mc, buf + UCODE_CONTAINER_SECTION_HDR, total_size)) { + vfree(mc); + mc = NULL; + } else + *mc_size = total_size + UCODE_CONTAINER_SECTION_HDR; + } +#undef UCODE_CONTAINER_SECTION_HDR + return mc; +} + + +static int install_equiv_cpu_table(u8 *buf, + int (*get_ucode_data)(void *, const void *, size_t)) +{ +#define UCODE_CONTAINER_HEADER_SIZE 12 + u8 *container_hdr[UCODE_CONTAINER_HEADER_SIZE]; + unsigned int *buf_pos = (unsigned int *)container_hdr; + unsigned long size; + + if (get_ucode_data(&container_hdr, buf, UCODE_CONTAINER_HEADER_SIZE)) + return 0; + + size = buf_pos[2]; + + if (buf_pos[1] != UCODE_EQUIV_CPU_TABLE_TYPE || !size) { + printk(KERN_ERR "microcode: error! " + "Wrong microcode equivalnet cpu table\n"); + return 0; + } + + equiv_cpu_table = (struct equiv_cpu_entry *) vmalloc(size); + if (!equiv_cpu_table) { + printk(KERN_ERR "microcode: error, can't allocate memory for equiv CPU table\n"); + return 0; + } + + buf += UCODE_CONTAINER_HEADER_SIZE; + if (get_ucode_data(equiv_cpu_table, buf, size)) { + vfree(equiv_cpu_table); + return 0; + } + + return size + UCODE_CONTAINER_HEADER_SIZE; /* add header length */ +#undef UCODE_CONTAINER_HEADER_SIZE +} + +static void free_equiv_cpu_table(void) +{ + if (equiv_cpu_table) { + vfree(equiv_cpu_table); + equiv_cpu_table = NULL; + } +} + +static int generic_load_microcode(int cpu, void *data, size_t size, + int (*get_ucode_data)(void *, const void *, size_t)) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + u8 *ucode_ptr = data, *new_mc = NULL, *mc; + int new_rev = uci->cpu_sig.rev; + unsigned int leftover; + unsigned long offset; + + offset = install_equiv_cpu_table(ucode_ptr, get_ucode_data); + if (!offset) { + printk(KERN_ERR "microcode: installing equivalent cpu table failed\n"); + return -EINVAL; + } + + ucode_ptr += offset; + leftover = size - offset; + + while (leftover) { + unsigned int uninitialized_var(mc_size); + struct microcode_header_amd *mc_header; + + mc = get_next_ucode(ucode_ptr, leftover, get_ucode_data, &mc_size); + if (!mc) + break; + + mc_header = (struct microcode_header_amd *)mc; + if (get_matching_microcode(cpu, mc, new_rev)) { + if (new_mc) + vfree(new_mc); + new_rev = mc_header->patch_id; + new_mc = mc; + } else + vfree(mc); + + ucode_ptr += mc_size; + leftover -= mc_size; + } + + if (new_mc) { + if (!leftover) { + if (uci->mc) + vfree(uci->mc); + uci->mc = new_mc; + pr_debug("microcode: CPU%d found a matching microcode update with" + " version 0x%x (current=0x%x)\n", + cpu, new_rev, uci->cpu_sig.rev); + } else + vfree(new_mc); + } + + free_equiv_cpu_table(); + + return (int)leftover; +} + +static int get_ucode_fw(void *to, const void *from, size_t n) +{ + memcpy(to, from, n); + return 0; +} + +static int request_microcode_fw(int cpu, struct device *device) +{ + const char *fw_name = "amd-ucode/microcode_amd.bin"; + const struct firmware *firmware; + int ret; + + /* We should bind the task to the CPU */ + BUG_ON(cpu != raw_smp_processor_id()); + + ret = request_firmware(&firmware, fw_name, device); + if (ret) { + printk(KERN_ERR "microcode: ucode data file %s load failed\n", fw_name); + return ret; + } + + ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size, + &get_ucode_fw); + + release_firmware(firmware); + + return ret; +} + +static int request_microcode_user(int cpu, const void __user *buf, size_t size) +{ + printk(KERN_WARNING "microcode: AMD microcode update via /dev/cpu/microcode" + "is not supported\n"); + return -1; +} + +static void microcode_fini_cpu_amd(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + vfree(uci->mc); + uci->mc = NULL; +} + +static struct microcode_ops microcode_amd_ops = { + .request_microcode_user = request_microcode_user, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info_amd, + .apply_microcode = apply_microcode_amd, + .microcode_fini_cpu = microcode_fini_cpu_amd, +}; + +struct microcode_ops * __init init_amd_microcode(void) +{ + return µcode_amd_ops; +} diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c new file mode 100644 index 00000000000..936d8d55f23 --- /dev/null +++ b/arch/x86/kernel/microcode_core.c @@ -0,0 +1,508 @@ +/* + * Intel CPU Microcode Update Driver for Linux + * + * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> + * 2006 Shaohua Li <shaohua.li@intel.com> + * + * This driver allows to upgrade microcode on Intel processors + * belonging to IA-32 family - PentiumPro, Pentium II, + * Pentium III, Xeon, Pentium 4, etc. + * + * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture + * Software Developer's Manual + * Order Number 253668 or free download from: + * + * http://developer.intel.com/design/pentium4/manuals/253668.htm + * + * For more information, go to http://www.urbanmyth.org/microcode + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Initial release. + * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added read() support + cleanups. + * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added 'device trimming' support. open(O_WRONLY) zeroes + * and frees the saved copy of applied microcode. + * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Made to use devfs (/dev/cpu/microcode) + cleanups. + * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> + * Added misc device support (now uses both devfs and misc). + * Added MICROCODE_IOCFREE ioctl to clear memory. + * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> + * Messages for error cases (non Intel & no suitable microcode). + * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> + * Removed ->release(). Removed exclusive open and status bitmap. + * Added microcode_rwsem to serialize read()/write()/ioctl(). + * Removed global kernel lock usage. + * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> + * Write 0 to 0x8B msr and then cpuid before reading revision, + * so that it works even if there were no update done by the + * BIOS. Otherwise, reading from 0x8B gives junk (which happened + * to be 0 on my machine which is why it worked even when I + * disabled update by the BIOS) + * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. + * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and + * Tigran Aivazian <tigran@veritas.com> + * Intel Pentium 4 processor support and bugfixes. + * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> + * Bugfix for HT (Hyper-Threading) enabled processors + * whereby processor resources are shared by all logical processors + * in a single CPU package. + * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and + * Tigran Aivazian <tigran@veritas.com>, + * Serialize updates as required on HT processors due to + * speculative nature of implementation. + * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> + * Fix the panic when writing zero-length microcode chunk. + * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, + * Jun Nakajima <jun.nakajima@intel.com> + * Support for the microcode updates in the new format. + * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> + * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl + * because we no longer hold a copy of applied microcode + * in kernel memory. + * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> + * Fix sigmatch() macro to handle old CPUs with pf == 0. + * Thanks to Stuart Swales for pointing out this bug. + */ +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/cpu.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/microcode.h> + +MODULE_DESCRIPTION("Microcode Update Driver"); +MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); +MODULE_LICENSE("GPL"); + +#define MICROCODE_VERSION "2.00" + +struct microcode_ops *microcode_ops; + +/* no concurrent ->write()s are allowed on /dev/cpu/microcode */ +static DEFINE_MUTEX(microcode_mutex); + +struct ucode_cpu_info ucode_cpu_info[NR_CPUS]; +EXPORT_SYMBOL_GPL(ucode_cpu_info); + +#ifdef CONFIG_MICROCODE_OLD_INTERFACE +static int do_microcode_update(const void __user *buf, size_t size) +{ + cpumask_t old; + int error = 0; + int cpu; + + old = current->cpus_allowed; + + for_each_online_cpu(cpu) { + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + if (!uci->valid) + continue; + + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + error = microcode_ops->request_microcode_user(cpu, buf, size); + if (error < 0) + goto out; + if (!error) + microcode_ops->apply_microcode(cpu); + } +out: + set_cpus_allowed_ptr(current, &old); + return error; +} + +static int microcode_open(struct inode *unused1, struct file *unused2) +{ + cycle_kernel_lock(); + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static ssize_t microcode_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + ssize_t ret; + + if ((len >> PAGE_SHIFT) > num_physpages) { + printk(KERN_ERR "microcode: too much data (max %ld pages)\n", + num_physpages); + return -EINVAL; + } + + get_online_cpus(); + mutex_lock(µcode_mutex); + + ret = do_microcode_update(buf, len); + if (!ret) + ret = (ssize_t)len; + + mutex_unlock(µcode_mutex); + put_online_cpus(); + + return ret; +} + +static const struct file_operations microcode_fops = { + .owner = THIS_MODULE, + .write = microcode_write, + .open = microcode_open, +}; + +static struct miscdevice microcode_dev = { + .minor = MICROCODE_MINOR, + .name = "microcode", + .fops = µcode_fops, +}; + +static int __init microcode_dev_init(void) +{ + int error; + + error = misc_register(µcode_dev); + if (error) { + printk(KERN_ERR + "microcode: can't misc_register on minor=%d\n", + MICROCODE_MINOR); + return error; + } + + return 0; +} + +static void microcode_dev_exit(void) +{ + misc_deregister(µcode_dev); +} + +MODULE_ALIAS_MISCDEV(MICROCODE_MINOR); +#else +#define microcode_dev_init() 0 +#define microcode_dev_exit() do { } while (0) +#endif + +/* fake device for request_firmware */ +struct platform_device *microcode_pdev; + +static ssize_t reload_store(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t sz) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; + char *end; + unsigned long val = simple_strtoul(buf, &end, 0); + int err = 0; + int cpu = dev->id; + + if (end == buf) + return -EINVAL; + if (val == 1) { + cpumask_t old = current->cpus_allowed; + + get_online_cpus(); + if (cpu_online(cpu)) { + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + mutex_lock(µcode_mutex); + if (uci->valid) { + err = microcode_ops->request_microcode_fw(cpu, + µcode_pdev->dev); + if (!err) + microcode_ops->apply_microcode(cpu); + } + mutex_unlock(µcode_mutex); + set_cpus_allowed_ptr(current, &old); + } + put_online_cpus(); + } + if (err) + return err; + return sz; +} + +static ssize_t version_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; + + return sprintf(buf, "0x%x\n", uci->cpu_sig.rev); +} + +static ssize_t pf_show(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; + + return sprintf(buf, "0x%x\n", uci->cpu_sig.pf); +} + +static SYSDEV_ATTR(reload, 0200, NULL, reload_store); +static SYSDEV_ATTR(version, 0400, version_show, NULL); +static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL); + +static struct attribute *mc_default_attrs[] = { + &attr_reload.attr, + &attr_version.attr, + &attr_processor_flags.attr, + NULL +}; + +static struct attribute_group mc_attr_group = { + .attrs = mc_default_attrs, + .name = "microcode", +}; + +static void microcode_fini_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + mutex_lock(µcode_mutex); + microcode_ops->microcode_fini_cpu(cpu); + uci->valid = 0; + mutex_unlock(µcode_mutex); +} + +static void collect_cpu_info(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + memset(uci, 0, sizeof(*uci)); + if (!microcode_ops->collect_cpu_info(cpu, &uci->cpu_sig)) + uci->valid = 1; +} + +static int microcode_resume_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct cpu_signature nsig; + + pr_debug("microcode: CPU%d resumed\n", cpu); + + if (!uci->mc) + return 1; + + /* + * Let's verify that the 'cached' ucode does belong + * to this cpu (a bit of paranoia): + */ + if (microcode_ops->collect_cpu_info(cpu, &nsig)) { + microcode_fini_cpu(cpu); + return -1; + } + + if (memcmp(&nsig, &uci->cpu_sig, sizeof(nsig))) { + microcode_fini_cpu(cpu); + /* Should we look for a new ucode here? */ + return 1; + } + + return 0; +} + +void microcode_update_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + int err = 0; + + /* + * Check if the system resume is in progress (uci->valid != NULL), + * otherwise just request a firmware: + */ + if (uci->valid) { + err = microcode_resume_cpu(cpu); + } else { + collect_cpu_info(cpu); + if (uci->valid && system_state == SYSTEM_RUNNING) + err = microcode_ops->request_microcode_fw(cpu, + µcode_pdev->dev); + } + if (!err) + microcode_ops->apply_microcode(cpu); +} + +static void microcode_init_cpu(int cpu) +{ + cpumask_t old = current->cpus_allowed; + + set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu)); + /* We should bind the task to the CPU */ + BUG_ON(raw_smp_processor_id() != cpu); + + mutex_lock(µcode_mutex); + microcode_update_cpu(cpu); + mutex_unlock(µcode_mutex); + + set_cpus_allowed_ptr(current, &old); +} + +static int mc_sysdev_add(struct sys_device *sys_dev) +{ + int err, cpu = sys_dev->id; + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + if (!cpu_online(cpu)) + return 0; + + pr_debug("microcode: CPU%d added\n", cpu); + memset(uci, 0, sizeof(*uci)); + + err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); + if (err) + return err; + + microcode_init_cpu(cpu); + return 0; +} + +static int mc_sysdev_remove(struct sys_device *sys_dev) +{ + int cpu = sys_dev->id; + + if (!cpu_online(cpu)) + return 0; + + pr_debug("microcode: CPU%d removed\n", cpu); + microcode_fini_cpu(cpu); + sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); + return 0; +} + +static int mc_sysdev_resume(struct sys_device *dev) +{ + int cpu = dev->id; + + if (!cpu_online(cpu)) + return 0; + + /* only CPU 0 will apply ucode here */ + microcode_update_cpu(0); + return 0; +} + +static struct sysdev_driver mc_sysdev_driver = { + .add = mc_sysdev_add, + .remove = mc_sysdev_remove, + .resume = mc_sysdev_resume, +}; + +static __cpuinit int +mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + struct sys_device *sys_dev; + + sys_dev = get_cpu_sysdev(cpu); + switch (action) { + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + microcode_init_cpu(cpu); + case CPU_DOWN_FAILED: + case CPU_DOWN_FAILED_FROZEN: + pr_debug("microcode: CPU%d added\n", cpu); + if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) + printk(KERN_ERR "microcode: Failed to create the sysfs " + "group for CPU%d\n", cpu); + break; + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + /* Suspend is in progress, only remove the interface */ + sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); + pr_debug("microcode: CPU%d removed\n", cpu); + break; + case CPU_DEAD: + case CPU_UP_CANCELED_FROZEN: + /* The CPU refused to come up during a system resume */ + microcode_fini_cpu(cpu); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block __refdata mc_cpu_notifier = { + .notifier_call = mc_cpu_callback, +}; + +static int __init microcode_init(void) +{ + struct cpuinfo_x86 *c = &cpu_data(0); + int error; + + if (c->x86_vendor == X86_VENDOR_INTEL) + microcode_ops = init_intel_microcode(); + else if (c->x86_vendor == X86_VENDOR_AMD) + microcode_ops = init_amd_microcode(); + + if (!microcode_ops) { + printk(KERN_ERR "microcode: no support for this CPU vendor\n"); + return -ENODEV; + } + + error = microcode_dev_init(); + if (error) + return error; + microcode_pdev = platform_device_register_simple("microcode", -1, + NULL, 0); + if (IS_ERR(microcode_pdev)) { + microcode_dev_exit(); + return PTR_ERR(microcode_pdev); + } + + get_online_cpus(); + error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); + put_online_cpus(); + if (error) { + microcode_dev_exit(); + platform_device_unregister(microcode_pdev); + return error; + } + + register_hotcpu_notifier(&mc_cpu_notifier); + + printk(KERN_INFO + "Microcode Update Driver: v" MICROCODE_VERSION + " <tigran@aivazian.fsnet.co.uk>" + " <peter.oruba@amd.com>\n"); + + return 0; +} + +static void __exit microcode_exit(void) +{ + microcode_dev_exit(); + + unregister_hotcpu_notifier(&mc_cpu_notifier); + + get_online_cpus(); + sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); + put_online_cpus(); + + platform_device_unregister(microcode_pdev); + + microcode_ops = NULL; + + printk(KERN_INFO + "Microcode Update Driver: v" MICROCODE_VERSION " removed.\n"); +} + +module_init(microcode_init); +module_exit(microcode_exit); diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c new file mode 100644 index 00000000000..622dc4a2178 --- /dev/null +++ b/arch/x86/kernel/microcode_intel.c @@ -0,0 +1,480 @@ +/* + * Intel CPU Microcode Update Driver for Linux + * + * Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk> + * 2006 Shaohua Li <shaohua.li@intel.com> + * + * This driver allows to upgrade microcode on Intel processors + * belonging to IA-32 family - PentiumPro, Pentium II, + * Pentium III, Xeon, Pentium 4, etc. + * + * Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture + * Software Developer's Manual + * Order Number 253668 or free download from: + * + * http://developer.intel.com/design/pentium4/manuals/253668.htm + * + * For more information, go to http://www.urbanmyth.org/microcode + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 1.0 16 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Initial release. + * 1.01 18 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added read() support + cleanups. + * 1.02 21 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Added 'device trimming' support. open(O_WRONLY) zeroes + * and frees the saved copy of applied microcode. + * 1.03 29 Feb 2000, Tigran Aivazian <tigran@sco.com> + * Made to use devfs (/dev/cpu/microcode) + cleanups. + * 1.04 06 Jun 2000, Simon Trimmer <simon@veritas.com> + * Added misc device support (now uses both devfs and misc). + * Added MICROCODE_IOCFREE ioctl to clear memory. + * 1.05 09 Jun 2000, Simon Trimmer <simon@veritas.com> + * Messages for error cases (non Intel & no suitable microcode). + * 1.06 03 Aug 2000, Tigran Aivazian <tigran@veritas.com> + * Removed ->release(). Removed exclusive open and status bitmap. + * Added microcode_rwsem to serialize read()/write()/ioctl(). + * Removed global kernel lock usage. + * 1.07 07 Sep 2000, Tigran Aivazian <tigran@veritas.com> + * Write 0 to 0x8B msr and then cpuid before reading revision, + * so that it works even if there were no update done by the + * BIOS. Otherwise, reading from 0x8B gives junk (which happened + * to be 0 on my machine which is why it worked even when I + * disabled update by the BIOS) + * Thanks to Eric W. Biederman <ebiederman@lnxi.com> for the fix. + * 1.08 11 Dec 2000, Richard Schaal <richard.schaal@intel.com> and + * Tigran Aivazian <tigran@veritas.com> + * Intel Pentium 4 processor support and bugfixes. + * 1.09 30 Oct 2001, Tigran Aivazian <tigran@veritas.com> + * Bugfix for HT (Hyper-Threading) enabled processors + * whereby processor resources are shared by all logical processors + * in a single CPU package. + * 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and + * Tigran Aivazian <tigran@veritas.com>, + * Serialize updates as required on HT processors due to + * speculative nature of implementation. + * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com> + * Fix the panic when writing zero-length microcode chunk. + * 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>, + * Jun Nakajima <jun.nakajima@intel.com> + * Support for the microcode updates in the new format. + * 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com> + * Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl + * because we no longer hold a copy of applied microcode + * in kernel memory. + * 1.14 25 Jun 2004 Tigran Aivazian <tigran@veritas.com> + * Fix sigmatch() macro to handle old CPUs with pf == 0. + * Thanks to Stuart Swales for pointing out this bug. + */ +#include <linux/capability.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/smp_lock.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/mutex.h> +#include <linux/cpu.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> + +#include <asm/msr.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/microcode.h> + +MODULE_DESCRIPTION("Microcode Update Driver"); +MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>"); +MODULE_LICENSE("GPL"); + +struct microcode_header_intel { + unsigned int hdrver; + unsigned int rev; + unsigned int date; + unsigned int sig; + unsigned int cksum; + unsigned int ldrver; + unsigned int pf; + unsigned int datasize; + unsigned int totalsize; + unsigned int reserved[3]; +}; + +struct microcode_intel { + struct microcode_header_intel hdr; + unsigned int bits[0]; +}; + +/* microcode format is extended from prescott processors */ +struct extended_signature { + unsigned int sig; + unsigned int pf; + unsigned int cksum; +}; + +struct extended_sigtable { + unsigned int count; + unsigned int cksum; + unsigned int reserved[3]; + struct extended_signature sigs[0]; +}; + +#define DEFAULT_UCODE_DATASIZE (2000) +#define MC_HEADER_SIZE (sizeof(struct microcode_header_intel)) +#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) +#define EXT_HEADER_SIZE (sizeof(struct extended_sigtable)) +#define EXT_SIGNATURE_SIZE (sizeof(struct extended_signature)) +#define DWSIZE (sizeof(u32)) +#define get_totalsize(mc) \ + (((struct microcode_intel *)mc)->hdr.totalsize ? \ + ((struct microcode_intel *)mc)->hdr.totalsize : \ + DEFAULT_UCODE_TOTALSIZE) + +#define get_datasize(mc) \ + (((struct microcode_intel *)mc)->hdr.datasize ? \ + ((struct microcode_intel *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE) + +#define sigmatch(s1, s2, p1, p2) \ + (((s1) == (s2)) && (((p1) & (p2)) || (((p1) == 0) && ((p2) == 0)))) + +#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE) + +/* serialize access to the physical write to MSR 0x79 */ +static DEFINE_SPINLOCK(microcode_update_lock); + +static int collect_cpu_info(int cpu_num, struct cpu_signature *csig) +{ + struct cpuinfo_x86 *c = &cpu_data(cpu_num); + unsigned int val[2]; + + memset(csig, 0, sizeof(*csig)); + + if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || + cpu_has(c, X86_FEATURE_IA64)) { + printk(KERN_ERR "microcode: CPU%d not a capable Intel " + "processor\n", cpu_num); + return -1; + } + + csig->sig = cpuid_eax(0x00000001); + + if ((c->x86_model >= 5) || (c->x86 > 6)) { + /* get processor flags from MSR 0x17 */ + rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); + csig->pf = 1 << ((val[1] >> 18) & 7); + } + + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + /* see notes above for revision 1.07. Apparent chip bug */ + sync_core(); + /* get the current revision from MSR 0x8B */ + rdmsr(MSR_IA32_UCODE_REV, val[0], csig->rev); + pr_debug("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n", + csig->sig, csig->pf, csig->rev); + + return 0; +} + +static inline int update_match_cpu(struct cpu_signature *csig, int sig, int pf) +{ + return (!sigmatch(sig, csig->sig, pf, csig->pf)) ? 0 : 1; +} + +static inline int +update_match_revision(struct microcode_header_intel *mc_header, int rev) +{ + return (mc_header->rev <= rev) ? 0 : 1; +} + +static int microcode_sanity_check(void *mc) +{ + struct microcode_header_intel *mc_header = mc; + struct extended_sigtable *ext_header = NULL; + struct extended_signature *ext_sig; + unsigned long total_size, data_size, ext_table_size; + int sum, orig_sum, ext_sigcount = 0, i; + + total_size = get_totalsize(mc_header); + data_size = get_datasize(mc_header); + if (data_size + MC_HEADER_SIZE > total_size) { + printk(KERN_ERR "microcode: error! " + "Bad data size in microcode data file\n"); + return -EINVAL; + } + + if (mc_header->ldrver != 1 || mc_header->hdrver != 1) { + printk(KERN_ERR "microcode: error! " + "Unknown microcode update format\n"); + return -EINVAL; + } + ext_table_size = total_size - (MC_HEADER_SIZE + data_size); + if (ext_table_size) { + if ((ext_table_size < EXT_HEADER_SIZE) + || ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) { + printk(KERN_ERR "microcode: error! " + "Small exttable size in microcode data file\n"); + return -EINVAL; + } + ext_header = mc + MC_HEADER_SIZE + data_size; + if (ext_table_size != exttable_size(ext_header)) { + printk(KERN_ERR "microcode: error! " + "Bad exttable size in microcode data file\n"); + return -EFAULT; + } + ext_sigcount = ext_header->count; + } + + /* check extended table checksum */ + if (ext_table_size) { + int ext_table_sum = 0; + int *ext_tablep = (int *)ext_header; + + i = ext_table_size / DWSIZE; + while (i--) + ext_table_sum += ext_tablep[i]; + if (ext_table_sum) { + printk(KERN_WARNING "microcode: aborting, " + "bad extended signature table checksum\n"); + return -EINVAL; + } + } + + /* calculate the checksum */ + orig_sum = 0; + i = (MC_HEADER_SIZE + data_size) / DWSIZE; + while (i--) + orig_sum += ((int *)mc)[i]; + if (orig_sum) { + printk(KERN_ERR "microcode: aborting, bad checksum\n"); + return -EINVAL; + } + if (!ext_table_size) + return 0; + /* check extended signature checksum */ + for (i = 0; i < ext_sigcount; i++) { + ext_sig = (void *)ext_header + EXT_HEADER_SIZE + + EXT_SIGNATURE_SIZE * i; + sum = orig_sum + - (mc_header->sig + mc_header->pf + mc_header->cksum) + + (ext_sig->sig + ext_sig->pf + ext_sig->cksum); + if (sum) { + printk(KERN_ERR "microcode: aborting, bad checksum\n"); + return -EINVAL; + } + } + return 0; +} + +/* + * return 0 - no update found + * return 1 - found update + */ +static int +get_matching_microcode(struct cpu_signature *cpu_sig, void *mc, int rev) +{ + struct microcode_header_intel *mc_header = mc; + struct extended_sigtable *ext_header; + unsigned long total_size = get_totalsize(mc_header); + int ext_sigcount, i; + struct extended_signature *ext_sig; + + if (!update_match_revision(mc_header, rev)) + return 0; + + if (update_match_cpu(cpu_sig, mc_header->sig, mc_header->pf)) + return 1; + + /* Look for ext. headers: */ + if (total_size <= get_datasize(mc_header) + MC_HEADER_SIZE) + return 0; + + ext_header = mc + get_datasize(mc_header) + MC_HEADER_SIZE; + ext_sigcount = ext_header->count; + ext_sig = (void *)ext_header + EXT_HEADER_SIZE; + + for (i = 0; i < ext_sigcount; i++) { + if (update_match_cpu(cpu_sig, ext_sig->sig, ext_sig->pf)) + return 1; + ext_sig++; + } + return 0; +} + +static void apply_microcode(int cpu) +{ + unsigned long flags; + unsigned int val[2]; + int cpu_num = raw_smp_processor_id(); + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct microcode_intel *mc_intel = uci->mc; + + /* We should bind the task to the CPU */ + BUG_ON(cpu_num != cpu); + + if (mc_intel == NULL) + return; + + /* serialize access to the physical write to MSR 0x79 */ + spin_lock_irqsave(µcode_update_lock, flags); + + /* write microcode via MSR 0x79 */ + wrmsr(MSR_IA32_UCODE_WRITE, + (unsigned long) mc_intel->bits, + (unsigned long) mc_intel->bits >> 16 >> 16); + wrmsr(MSR_IA32_UCODE_REV, 0, 0); + + /* see notes above for revision 1.07. Apparent chip bug */ + sync_core(); + + /* get the current revision from MSR 0x8B */ + rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); + + spin_unlock_irqrestore(µcode_update_lock, flags); + if (val[1] != mc_intel->hdr.rev) { + printk(KERN_ERR "microcode: CPU%d update from revision " + "0x%x to 0x%x failed\n", cpu_num, uci->cpu_sig.rev, val[1]); + return; + } + printk(KERN_INFO "microcode: CPU%d updated from revision " + "0x%x to 0x%x, date = %04x-%02x-%02x \n", + cpu_num, uci->cpu_sig.rev, val[1], + mc_intel->hdr.date & 0xffff, + mc_intel->hdr.date >> 24, + (mc_intel->hdr.date >> 16) & 0xff); + uci->cpu_sig.rev = val[1]; +} + +static int generic_load_microcode(int cpu, void *data, size_t size, + int (*get_ucode_data)(void *, const void *, size_t)) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + u8 *ucode_ptr = data, *new_mc = NULL, *mc; + int new_rev = uci->cpu_sig.rev; + unsigned int leftover = size; + + while (leftover) { + struct microcode_header_intel mc_header; + unsigned int mc_size; + + if (get_ucode_data(&mc_header, ucode_ptr, sizeof(mc_header))) + break; + + mc_size = get_totalsize(&mc_header); + if (!mc_size || mc_size > leftover) { + printk(KERN_ERR "microcode: error!" + "Bad data in microcode data file\n"); + break; + } + + mc = vmalloc(mc_size); + if (!mc) + break; + + if (get_ucode_data(mc, ucode_ptr, mc_size) || + microcode_sanity_check(mc) < 0) { + vfree(mc); + break; + } + + if (get_matching_microcode(&uci->cpu_sig, mc, new_rev)) { + if (new_mc) + vfree(new_mc); + new_rev = mc_header.rev; + new_mc = mc; + } else + vfree(mc); + + ucode_ptr += mc_size; + leftover -= mc_size; + } + + if (new_mc) { + if (!leftover) { + if (uci->mc) + vfree(uci->mc); + uci->mc = (struct microcode_intel *)new_mc; + pr_debug("microcode: CPU%d found a matching microcode update with" + " version 0x%x (current=0x%x)\n", + cpu, new_rev, uci->cpu_sig.rev); + } else + vfree(new_mc); + } + + return (int)leftover; +} + +static int get_ucode_fw(void *to, const void *from, size_t n) +{ + memcpy(to, from, n); + return 0; +} + +static int request_microcode_fw(int cpu, struct device *device) +{ + char name[30]; + struct cpuinfo_x86 *c = &cpu_data(cpu); + const struct firmware *firmware; + int ret; + + /* We should bind the task to the CPU */ + BUG_ON(cpu != raw_smp_processor_id()); + sprintf(name, "intel-ucode/%02x-%02x-%02x", + c->x86, c->x86_model, c->x86_mask); + ret = request_firmware(&firmware, name, device); + if (ret) { + pr_debug("microcode: data file %s load failed\n", name); + return ret; + } + + ret = generic_load_microcode(cpu, (void*)firmware->data, firmware->size, + &get_ucode_fw); + + release_firmware(firmware); + + return ret; +} + +static int get_ucode_user(void *to, const void *from, size_t n) +{ + return copy_from_user(to, from, n); +} + +static int request_microcode_user(int cpu, const void __user *buf, size_t size) +{ + /* We should bind the task to the CPU */ + BUG_ON(cpu != raw_smp_processor_id()); + + return generic_load_microcode(cpu, (void*)buf, size, &get_ucode_user); +} + +static void microcode_fini_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + vfree(uci->mc); + uci->mc = NULL; +} + +struct microcode_ops microcode_intel_ops = { + .request_microcode_user = request_microcode_user, + .request_microcode_fw = request_microcode_fw, + .collect_cpu_info = collect_cpu_info, + .apply_microcode = apply_microcode, + .microcode_fini_cpu = microcode_fini_cpu, +}; + +struct microcode_ops * __init init_intel_microcode(void) +{ + return µcode_intel_ops; +} + diff --git a/arch/x86/kernel/paravirt-spinlocks.c b/arch/x86/kernel/paravirt-spinlocks.c new file mode 100644 index 00000000000..0e9f1982b1d --- /dev/null +++ b/arch/x86/kernel/paravirt-spinlocks.c @@ -0,0 +1,37 @@ +/* + * Split spinlock implementation out into its own file, so it can be + * compiled in a FTRACE-compatible way. + */ +#include <linux/spinlock.h> +#include <linux/module.h> + +#include <asm/paravirt.h> + +static void default_spin_lock_flags(struct raw_spinlock *lock, unsigned long flags) +{ + __raw_spin_lock(lock); +} + +struct pv_lock_ops pv_lock_ops = { +#ifdef CONFIG_SMP + .spin_is_locked = __ticket_spin_is_locked, + .spin_is_contended = __ticket_spin_is_contended, + + .spin_lock = __ticket_spin_lock, + .spin_lock_flags = default_spin_lock_flags, + .spin_trylock = __ticket_spin_trylock, + .spin_unlock = __ticket_spin_unlock, +#endif +}; +EXPORT_SYMBOL(pv_lock_ops); + +void __init paravirt_use_bytelocks(void) +{ +#ifdef CONFIG_SMP + pv_lock_ops.spin_is_locked = __byte_spin_is_locked; + pv_lock_ops.spin_is_contended = __byte_spin_is_contended; + pv_lock_ops.spin_lock = __byte_spin_lock; + pv_lock_ops.spin_trylock = __byte_spin_trylock; + pv_lock_ops.spin_unlock = __byte_spin_unlock; +#endif +} diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 6b0bb73998d..e4c8fb60887 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -268,17 +268,6 @@ enum paravirt_lazy_mode paravirt_get_lazy_mode(void) return __get_cpu_var(paravirt_lazy_mode); } -void __init paravirt_use_bytelocks(void) -{ -#ifdef CONFIG_SMP - pv_lock_ops.spin_is_locked = __byte_spin_is_locked; - pv_lock_ops.spin_is_contended = __byte_spin_is_contended; - pv_lock_ops.spin_lock = __byte_spin_lock; - pv_lock_ops.spin_trylock = __byte_spin_trylock; - pv_lock_ops.spin_unlock = __byte_spin_unlock; -#endif -} - struct pv_info pv_info = { .name = "bare hardware", .paravirt_enabled = 0, @@ -349,6 +338,10 @@ struct pv_cpu_ops pv_cpu_ops = { .write_ldt_entry = native_write_ldt_entry, .write_gdt_entry = native_write_gdt_entry, .write_idt_entry = native_write_idt_entry, + + .alloc_ldt = paravirt_nop, + .free_ldt = paravirt_nop, + .load_sp0 = native_load_sp0, #if defined(CONFIG_X86_32) || defined(CONFIG_IA32_EMULATION) @@ -460,18 +453,6 @@ struct pv_mmu_ops pv_mmu_ops = { .set_fixmap = native_set_fixmap, }; -struct pv_lock_ops pv_lock_ops = { -#ifdef CONFIG_SMP - .spin_is_locked = __ticket_spin_is_locked, - .spin_is_contended = __ticket_spin_is_contended, - - .spin_lock = __ticket_spin_lock, - .spin_trylock = __ticket_spin_trylock, - .spin_unlock = __ticket_spin_unlock, -#endif -}; -EXPORT_SYMBOL(pv_lock_ops); - EXPORT_SYMBOL_GPL(pv_time_ops); EXPORT_SYMBOL (pv_cpu_ops); EXPORT_SYMBOL (pv_mmu_ops); diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 205188db962..0a1302fe6d4 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c @@ -76,47 +76,12 @@ unsigned long thread_saved_pc(struct task_struct *tsk) return ((unsigned long *)tsk->thread.sp)[3]; } -#ifdef CONFIG_HOTPLUG_CPU -#include <asm/nmi.h> - -static void cpu_exit_clear(void) -{ - int cpu = raw_smp_processor_id(); - - idle_task_exit(); - - cpu_uninit(); - irq_ctx_exit(cpu); - - cpu_clear(cpu, cpu_callout_map); - cpu_clear(cpu, cpu_callin_map); - - numa_remove_cpu(cpu); - c1e_remove_cpu(cpu); -} - -/* We don't actually take CPU down, just spin without interrupts. */ -static inline void play_dead(void) -{ - /* This must be done before dead CPU ack */ - cpu_exit_clear(); - mb(); - /* Ack it */ - __get_cpu_var(cpu_state) = CPU_DEAD; - - /* - * With physical CPU hotplug, we should halt the cpu - */ - local_irq_disable(); - /* mask all interrupts, flush any and all caches, and halt */ - wbinvd_halt(); -} -#else +#ifndef CONFIG_SMP static inline void play_dead(void) { BUG(); } -#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* * The idle thread. There's no useful work to be @@ -158,7 +123,7 @@ void cpu_idle(void) } } -void __show_registers(struct pt_regs *regs, int all) +void __show_regs(struct pt_regs *regs, int all) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; unsigned long d0, d1, d2, d3, d6, d7; @@ -224,7 +189,7 @@ void __show_registers(struct pt_regs *regs, int all) void show_regs(struct pt_regs *regs) { - __show_registers(regs, 1); + __show_regs(regs, 1); show_trace(NULL, regs, ®s->sp, regs->bp); } diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 2a8ccb9238b..cd8c0ed02b7 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c @@ -86,30 +86,12 @@ void exit_idle(void) __exit_idle(); } -#ifdef CONFIG_HOTPLUG_CPU -DECLARE_PER_CPU(int, cpu_state); - -#include <linux/nmi.h> -/* We halt the CPU with physical CPU hotplug */ -static inline void play_dead(void) -{ - idle_task_exit(); - c1e_remove_cpu(raw_smp_processor_id()); - - mb(); - /* Ack it */ - __get_cpu_var(cpu_state) = CPU_DEAD; - - local_irq_disable(); - /* mask all interrupts, flush any and all caches, and halt */ - wbinvd_halt(); -} -#else +#ifndef CONFIG_SMP static inline void play_dead(void) { BUG(); } -#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* * The idle thread. There's no useful work to be @@ -154,7 +136,7 @@ void cpu_idle(void) } /* Prints also some state that isn't saved in the pt_regs */ -void __show_regs(struct pt_regs *regs) +void __show_regs(struct pt_regs *regs, int all) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs; unsigned long d0, d1, d2, d3, d6, d7; @@ -193,6 +175,9 @@ void __show_regs(struct pt_regs *regs) rdmsrl(MSR_GS_BASE, gs); rdmsrl(MSR_KERNEL_GS_BASE, shadowgs); + if (!all) + return; + cr0 = read_cr0(); cr2 = read_cr2(); cr3 = read_cr3(); @@ -218,7 +203,7 @@ void __show_regs(struct pt_regs *regs) void show_regs(struct pt_regs *regs) { printk(KERN_INFO "CPU %d:", smp_processor_id()); - __show_regs(regs); + __show_regs(regs, 1); show_trace(NULL, regs, (void *)(regs + 1), regs->bp); } @@ -754,12 +739,12 @@ unsigned long get_wchan(struct task_struct *p) if (!p || p == current || p->state == TASK_RUNNING) return 0; stack = (unsigned long)task_stack_page(p); - if (p->thread.sp < stack || p->thread.sp > stack+THREAD_SIZE) + if (p->thread.sp < stack || p->thread.sp >= stack+THREAD_SIZE) return 0; fp = *(u64 *)(p->thread.sp); do { if (fp < (unsigned long)stack || - fp > (unsigned long)stack+THREAD_SIZE) + fp >= (unsigned long)stack+THREAD_SIZE) return 0; ip = *(u64 *)(fp+8); if (!in_sched_functions(ip)) diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index e375b658efc..0a6d8c12e10 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c @@ -40,7 +40,9 @@ enum x86_regset { REGSET_GENERAL, REGSET_FP, REGSET_XFP, + REGSET_IOPERM64 = REGSET_XFP, REGSET_TLS, + REGSET_IOPERM32, }; /* @@ -555,6 +557,29 @@ static int ptrace_set_debugreg(struct task_struct *child, return 0; } +/* + * These access the current or another (stopped) task's io permission + * bitmap for debugging or core dump. + */ +static int ioperm_active(struct task_struct *target, + const struct user_regset *regset) +{ + return target->thread.io_bitmap_max / regset->size; +} + +static int ioperm_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + if (!target->thread.io_bitmap_ptr) + return -ENXIO; + + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.io_bitmap_ptr, + 0, IO_BITMAP_BYTES); +} + #ifdef CONFIG_X86_PTRACE_BTS /* * The configuration for a particular BTS hardware implementation. @@ -1385,6 +1410,12 @@ static const struct user_regset x86_64_regsets[] = { .size = sizeof(long), .align = sizeof(long), .active = xfpregs_active, .get = xfpregs_get, .set = xfpregs_set }, + [REGSET_IOPERM64] = { + .core_note_type = NT_386_IOPERM, + .n = IO_BITMAP_LONGS, + .size = sizeof(long), .align = sizeof(long), + .active = ioperm_active, .get = ioperm_get + }, }; static const struct user_regset_view user_x86_64_view = { @@ -1431,6 +1462,12 @@ static const struct user_regset x86_32_regsets[] = { .active = regset_tls_active, .get = regset_tls_get, .set = regset_tls_set }, + [REGSET_IOPERM32] = { + .core_note_type = NT_386_IOPERM, + .n = IO_BITMAP_BYTES / sizeof(u32), + .size = sizeof(u32), .align = sizeof(u32), + .active = ioperm_active, .get = ioperm_get + }, }; static const struct user_regset_view user_x86_32_view = { @@ -1452,7 +1489,8 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task) #endif } -void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) +void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, + int error_code, int si_code) { struct siginfo info; @@ -1461,7 +1499,7 @@ void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code) memset(&info, 0, sizeof(info)); info.si_signo = SIGTRAP; - info.si_code = TRAP_BRKPT; + info.si_code = si_code; /* User-mode ip? */ info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL; @@ -1548,5 +1586,5 @@ asmregparm void syscall_trace_leave(struct pt_regs *regs) */ if (test_thread_flag(TIF_SINGLESTEP) && tracehook_consider_fatal_signal(current, SIGTRAP, SIG_DFL)) - send_sigtrap(current, regs, 0); + send_sigtrap(current, regs, 0, TRAP_BRKPT); } diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c index d1385881810..f6a11b9b1f9 100644 --- a/arch/x86/kernel/quirks.c +++ b/arch/x86/kernel/quirks.c @@ -354,9 +354,27 @@ static void ati_force_hpet_resume(void) printk(KERN_DEBUG "Force enabled HPET at resume\n"); } +static u32 ati_ixp4x0_rev(struct pci_dev *dev) +{ + u32 d; + u8 b; + + pci_read_config_byte(dev, 0xac, &b); + b &= ~(1<<5); + pci_write_config_byte(dev, 0xac, b); + pci_read_config_dword(dev, 0x70, &d); + d |= 1<<8; + pci_write_config_dword(dev, 0x70, d); + pci_read_config_dword(dev, 0x8, &d); + d &= 0xff; + dev_printk(KERN_DEBUG, &dev->dev, "SB4X0 revision 0x%x\n", d); + return d; +} + static void ati_force_enable_hpet(struct pci_dev *dev) { - u32 uninitialized_var(val); + u32 d, val; + u8 b; if (hpet_address || force_hpet_address) return; @@ -366,14 +384,33 @@ static void ati_force_enable_hpet(struct pci_dev *dev) return; } + d = ati_ixp4x0_rev(dev); + if (d < 0x82) + return; + + /* base address */ pci_write_config_dword(dev, 0x14, 0xfed00000); pci_read_config_dword(dev, 0x14, &val); + + /* enable interrupt */ + outb(0x72, 0xcd6); b = inb(0xcd7); + b |= 0x1; + outb(0x72, 0xcd6); outb(b, 0xcd7); + outb(0x72, 0xcd6); b = inb(0xcd7); + if (!(b & 0x1)) + return; + pci_read_config_dword(dev, 0x64, &d); + d |= (1<<10); + pci_write_config_dword(dev, 0x64, d); + pci_read_config_dword(dev, 0x64, &d); + if (!(d & (1<<10))) + return; + force_hpet_address = val; force_hpet_resume_type = ATI_FORCE_HPET_RESUME; dev_printk(KERN_DEBUG, &dev->dev, "Force enabled HPET at 0x%lx\n", force_hpet_address); cached_dev = dev; - return; } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS, ati_force_enable_hpet); diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 46c98efbbf8..2255782e8d4 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -302,7 +302,7 @@ static void __init relocate_initrd(void) if (clen > MAX_MAP_CHUNK-slop) clen = MAX_MAP_CHUNK-slop; mapaddr = ramdisk_image & PAGE_MASK; - p = early_ioremap(mapaddr, clen+slop); + p = early_memremap(mapaddr, clen+slop); memcpy(q, p+slop, clen); early_iounmap(p, clen+slop); q += clen; @@ -379,7 +379,7 @@ static void __init parse_setup_data(void) return; pa_data = boot_params.hdr.setup_data; while (pa_data) { - data = early_ioremap(pa_data, PAGE_SIZE); + data = early_memremap(pa_data, PAGE_SIZE); switch (data->type) { case SETUP_E820_EXT: parse_e820_ext(data, pa_data); @@ -402,7 +402,7 @@ static void __init e820_reserve_setup_data(void) return; pa_data = boot_params.hdr.setup_data; while (pa_data) { - data = early_ioremap(pa_data, sizeof(*data)); + data = early_memremap(pa_data, sizeof(*data)); e820_update_range(pa_data, sizeof(*data)+data->len, E820_RAM, E820_RESERVED_KERN); found = 1; @@ -428,7 +428,7 @@ static void __init reserve_early_setup_data(void) return; pa_data = boot_params.hdr.setup_data; while (pa_data) { - data = early_ioremap(pa_data, sizeof(*data)); + data = early_memremap(pa_data, sizeof(*data)); sprintf(buf, "setup data %x", data->type); reserve_early(pa_data, pa_data+sizeof(*data)+data->len, buf); pa_data = data->next; @@ -582,6 +582,190 @@ static struct x86_quirks default_x86_quirks __initdata; struct x86_quirks *x86_quirks __initdata = &default_x86_quirks; /* + * Some BIOSes seem to corrupt the low 64k of memory during events + * like suspend/resume and unplugging an HDMI cable. Reserve all + * remaining free memory in that area and fill it with a distinct + * pattern. + */ +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION +#define MAX_SCAN_AREAS 8 + +static int __read_mostly memory_corruption_check = -1; + +static unsigned __read_mostly corruption_check_size = 64*1024; +static unsigned __read_mostly corruption_check_period = 60; /* seconds */ + +static struct e820entry scan_areas[MAX_SCAN_AREAS]; +static int num_scan_areas; + + +static int set_corruption_check(char *arg) +{ + char *end; + + memory_corruption_check = simple_strtol(arg, &end, 10); + + return (*end == 0) ? 0 : -EINVAL; +} +early_param("memory_corruption_check", set_corruption_check); + +static int set_corruption_check_period(char *arg) +{ + char *end; + + corruption_check_period = simple_strtoul(arg, &end, 10); + + return (*end == 0) ? 0 : -EINVAL; +} +early_param("memory_corruption_check_period", set_corruption_check_period); + +static int set_corruption_check_size(char *arg) +{ + char *end; + unsigned size; + + size = memparse(arg, &end); + + if (*end == '\0') + corruption_check_size = size; + + return (size == corruption_check_size) ? 0 : -EINVAL; +} +early_param("memory_corruption_check_size", set_corruption_check_size); + + +static void __init setup_bios_corruption_check(void) +{ + u64 addr = PAGE_SIZE; /* assume first page is reserved anyway */ + + if (memory_corruption_check == -1) { + memory_corruption_check = +#ifdef CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK + 1 +#else + 0 +#endif + ; + } + + if (corruption_check_size == 0) + memory_corruption_check = 0; + + if (!memory_corruption_check) + return; + + corruption_check_size = round_up(corruption_check_size, PAGE_SIZE); + + while(addr < corruption_check_size && num_scan_areas < MAX_SCAN_AREAS) { + u64 size; + addr = find_e820_area_size(addr, &size, PAGE_SIZE); + + if (addr == 0) + break; + + if ((addr + size) > corruption_check_size) + size = corruption_check_size - addr; + + if (size == 0) + break; + + e820_update_range(addr, size, E820_RAM, E820_RESERVED); + scan_areas[num_scan_areas].addr = addr; + scan_areas[num_scan_areas].size = size; + num_scan_areas++; + + /* Assume we've already mapped this early memory */ + memset(__va(addr), 0, size); + + addr += size; + } + + printk(KERN_INFO "Scanning %d areas for low memory corruption\n", + num_scan_areas); + update_e820(); +} + +static struct timer_list periodic_check_timer; + +void check_for_bios_corruption(void) +{ + int i; + int corruption = 0; + + if (!memory_corruption_check) + return; + + for(i = 0; i < num_scan_areas; i++) { + unsigned long *addr = __va(scan_areas[i].addr); + unsigned long size = scan_areas[i].size; + + for(; size; addr++, size -= sizeof(unsigned long)) { + if (!*addr) + continue; + printk(KERN_ERR "Corrupted low memory at %p (%lx phys) = %08lx\n", + addr, __pa(addr), *addr); + corruption = 1; + *addr = 0; + } + } + + WARN(corruption, KERN_ERR "Memory corruption detected in low memory\n"); +} + +static void periodic_check_for_corruption(unsigned long data) +{ + check_for_bios_corruption(); + mod_timer(&periodic_check_timer, round_jiffies(jiffies + corruption_check_period*HZ)); +} + +void start_periodic_check_for_corruption(void) +{ + if (!memory_corruption_check || corruption_check_period == 0) + return; + + printk(KERN_INFO "Scanning for low memory corruption every %d seconds\n", + corruption_check_period); + + init_timer(&periodic_check_timer); + periodic_check_timer.function = &periodic_check_for_corruption; + periodic_check_for_corruption(0); +} +#endif + +static int __init dmi_low_memory_corruption(const struct dmi_system_id *d) +{ + printk(KERN_NOTICE + "%s detected: BIOS may corrupt low RAM, working it around.\n", + d->ident); + + e820_update_range(0, 0x10000, E820_RAM, E820_RESERVED); + sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map); + + return 0; +} + +/* List of systems that have known low memory corruption BIOS problems */ +static struct dmi_system_id __initdata bad_bios_dmi_table[] = { +#ifdef CONFIG_X86_RESERVE_LOW_64K + { + .callback = dmi_low_memory_corruption, + .ident = "AMI BIOS", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + }, + }, + { + .callback = dmi_low_memory_corruption, + .ident = "Phoenix BIOS", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"), + }, + }, +#endif + {} +}; + +/* * Determine if we were loaded by an EFI loader. If so, then we have also been * passed the efi memmap, systab, etc., so we should use these data structures * for initialization. Note, the efi init code path is determined by the @@ -715,6 +899,10 @@ void __init setup_arch(char **cmdline_p) finish_e820_parsing(); + dmi_scan_machine(); + + dmi_check_system(bad_bios_dmi_table); + #ifdef CONFIG_X86_32 probe_roms(); #endif @@ -771,6 +959,10 @@ void __init setup_arch(char **cmdline_p) high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; #endif +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION + setup_bios_corruption_check(); +#endif + /* max_pfn_mapped is updated here */ max_low_pfn_mapped = init_memory_mapping(0, max_low_pfn<<PAGE_SHIFT); max_pfn_mapped = max_low_pfn_mapped; @@ -799,8 +991,6 @@ void __init setup_arch(char **cmdline_p) vsmp_init(); #endif - dmi_scan_machine(); - io_delay_init(); /* @@ -808,6 +998,8 @@ void __init setup_arch(char **cmdline_p) */ acpi_boot_table_init(); + early_acpi_boot_init(); + #ifdef CONFIG_ACPI_NUMA /* * Parse SRAT to discover nodes. @@ -903,3 +1095,5 @@ void __init setup_arch(char **cmdline_p) #endif #endif } + + diff --git a/arch/x86/kernel/signal_32.c b/arch/x86/kernel/signal_32.c index b21070ea33a..d6dd057d0f2 100644 --- a/arch/x86/kernel/signal_32.c +++ b/arch/x86/kernel/signal_32.c @@ -27,6 +27,7 @@ #include <asm/uaccess.h> #include <asm/i387.h> #include <asm/vdso.h> +#include <asm/syscall.h> #include <asm/syscalls.h> #include "sigframe.h" @@ -112,6 +113,27 @@ asmlinkage int sys_sigaltstack(unsigned long bx) return do_sigaltstack(uss, uoss, regs->sp); } +#define COPY(x) { \ + err |= __get_user(regs->x, &sc->x); \ +} + +#define COPY_SEG(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->seg = tmp; \ +} + +#define COPY_SEG_STRICT(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->seg = tmp | 3; \ +} + +#define GET_SEG(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + loadsegment(seg, tmp); \ +} /* * Do a signal return; undo the signal stack. @@ -120,28 +142,13 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long *pax) { + void __user *buf; + unsigned int tmpflags; unsigned int err = 0; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; -#define COPY(x) err |= __get_user(regs->x, &sc->x) - -#define COPY_SEG(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ - regs->seg = tmp; } - -#define COPY_SEG_STRICT(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ - regs->seg = tmp|3; } - -#define GET_SEG(seg) \ - { unsigned short tmp; \ - err |= __get_user(tmp, &sc->seg); \ - loadsegment(seg, tmp); } - GET_SEG(gs); COPY_SEG(fs); COPY_SEG(es); @@ -151,21 +158,12 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, COPY_SEG_STRICT(cs); COPY_SEG_STRICT(ss); - { - unsigned int tmpflags; - - err |= __get_user(tmpflags, &sc->flags); - regs->flags = (regs->flags & ~FIX_EFLAGS) | - (tmpflags & FIX_EFLAGS); - regs->orig_ax = -1; /* disable syscall checks */ - } - - { - void __user *buf; + err |= __get_user(tmpflags, &sc->flags); + regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); + regs->orig_ax = -1; /* disable syscall checks */ - err |= __get_user(buf, &sc->fpstate); - err |= restore_i387_xstate(buf); - } + err |= __get_user(buf, &sc->fpstate); + err |= restore_i387_xstate(buf); err |= __get_user(*pax, &sc->ax); return err; @@ -214,9 +212,8 @@ badframe: return 0; } -asmlinkage int sys_rt_sigreturn(unsigned long __unused) +static long do_rt_sigreturn(struct pt_regs *regs) { - struct pt_regs *regs = (struct pt_regs *)&__unused; struct rt_sigframe __user *frame; unsigned long ax; sigset_t set; @@ -242,10 +239,17 @@ asmlinkage int sys_rt_sigreturn(unsigned long __unused) return ax; badframe: - force_sig(SIGSEGV, current); + signal_fault(regs, frame, "rt_sigreturn"); return 0; } +asmlinkage int sys_rt_sigreturn(unsigned long __unused) +{ + struct pt_regs *regs = (struct pt_regs *)&__unused; + + return do_rt_sigreturn(regs); +} + /* * Set up a signal frame. */ @@ -337,39 +341,29 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size, } static int -setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, - struct pt_regs *regs) +__setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, + struct pt_regs *regs) { struct sigframe __user *frame; void __user *restorer; int err = 0; - int usig; void __user *fpstate = NULL; frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; + if (__put_user(sig, &frame->sig)) + return -EFAULT; - err = __put_user(usig, &frame->sig); - if (err) - goto give_sigsegv; - - err = setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0]); - if (err) - goto give_sigsegv; + if (setup_sigcontext(&frame->sc, fpstate, regs, set->sig[0])) + return -EFAULT; if (_NSIG_WORDS > 1) { - err = __copy_to_user(&frame->extramask, &set->sig[1], - sizeof(frame->extramask)); - if (err) - goto give_sigsegv; + if (__copy_to_user(&frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + return -EFAULT; } if (current->mm->context.vdso) @@ -394,7 +388,7 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long)frame; @@ -409,38 +403,27 @@ setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, regs->cs = __USER_CS; return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } -static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; void __user *restorer; int err = 0; - int usig; void __user *fpstate = NULL; frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; - - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; + return -EFAULT; - err |= __put_user(usig, &frame->sig); + err |= __put_user(sig, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, info); if (err) - goto give_sigsegv; + return -EFAULT; /* Create the ucontext. */ if (cpu_has_xsave) @@ -456,7 +439,7 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs, set->sig[0]); err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up to return from userspace. */ restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn); @@ -476,12 +459,12 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->sp = (unsigned long)frame; regs->ip = (unsigned long)ka->sa.sa_handler; - regs->ax = (unsigned long)usig; + regs->ax = (unsigned long)sig; regs->dx = (unsigned long)&frame->info; regs->cx = (unsigned long)&frame->uc; @@ -491,15 +474,48 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->cs = __USER_CS; return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } /* * OK, we're invoking a handler: */ +static int signr_convert(int sig) +{ + struct thread_info *info = current_thread_info(); + + if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32) + return info->exec_domain->signal_invmap[sig]; + return sig; +} + +#define is_ia32 1 +#define ia32_setup_frame __setup_frame +#define ia32_setup_rt_frame __setup_rt_frame + +static int +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + int usig = signr_convert(sig); + int ret; + + /* Set up the stack frame */ + if (is_ia32) { + if (ka->sa.sa_flags & SA_SIGINFO) + ret = ia32_setup_rt_frame(usig, ka, info, set, regs); + else + ret = ia32_setup_frame(usig, ka, set, regs); + } else + ret = __setup_rt_frame(sig, ka, info, set, regs); + + if (ret) { + force_sigsegv(sig, current); + return -EFAULT; + } + + return ret; +} + static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) @@ -507,9 +523,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, int ret; /* Are we from a system call? */ - if ((long)regs->orig_ax >= 0) { + if (syscall_get_nr(current, regs) >= 0) { /* If so, check system call restarting.. */ - switch (regs->ax) { + switch (syscall_get_error(current, regs)) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: regs->ax = -EINTR; @@ -536,15 +552,20 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, likely(test_and_clear_thread_flag(TIF_FORCED_TF))) regs->flags &= ~X86_EFLAGS_TF; - /* Set up the stack frame */ - if (ka->sa.sa_flags & SA_SIGINFO) - ret = setup_rt_frame(sig, ka, info, oldset, regs); - else - ret = setup_frame(sig, ka, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); if (ret) return ret; +#ifdef CONFIG_X86_64 + /* + * This has nothing to do with segment registers, + * despite the name. This magic affects uaccess.h + * macros' behavior. Reset it to the normal setting. + */ + set_fs(USER_DS); +#endif + /* * Clear the direction flag as per the ABI for function entry. */ @@ -571,6 +592,7 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, return 0; } +#define NR_restart_syscall __NR_restart_syscall /* * 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 @@ -623,9 +645,9 @@ static void do_signal(struct pt_regs *regs) } /* Did we come from a system call? */ - if ((long)regs->orig_ax >= 0) { + if (syscall_get_nr(current, regs) >= 0) { /* Restart the system call - no handlers present */ - switch (regs->ax) { + switch (syscall_get_error(current, regs)) { case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: @@ -634,7 +656,7 @@ static void do_signal(struct pt_regs *regs) break; case -ERESTART_RESTARTBLOCK: - regs->ax = __NR_restart_syscall; + regs->ax = NR_restart_syscall; regs->ip -= 2; break; } @@ -657,6 +679,12 @@ static void do_signal(struct pt_regs *regs) void do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) { +#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE) + /* notify userspace of pending MCEs */ + if (thread_info_flags & _TIF_MCE_NOTIFY) + mce_notify_user(); +#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ + /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) do_signal(regs); @@ -666,5 +694,23 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) tracehook_notify_resume(regs); } +#ifdef CONFIG_X86_32 clear_thread_flag(TIF_IRET); +#endif /* CONFIG_X86_32 */ +} + +void signal_fault(struct pt_regs *regs, void __user *frame, char *where) +{ + struct task_struct *me = current; + + if (show_unhandled_signals && printk_ratelimit()) { + printk(KERN_INFO + "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", + me->comm, me->pid, where, frame, + regs->ip, regs->sp, regs->orig_ax); + print_vma_addr(" in ", regs->ip); + printk(KERN_CONT "\n"); + } + + force_sig(SIGSEGV, me); } diff --git a/arch/x86/kernel/signal_64.c b/arch/x86/kernel/signal_64.c index 823a55bf8c3..a5c9627f4db 100644 --- a/arch/x86/kernel/signal_64.c +++ b/arch/x86/kernel/signal_64.c @@ -52,6 +52,16 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, return do_sigaltstack(uss, uoss, regs->sp); } +#define COPY(x) { \ + err |= __get_user(regs->x, &sc->x); \ +} + +#define COPY_SEG_STRICT(seg) { \ + unsigned short tmp; \ + err |= __get_user(tmp, &sc->seg); \ + regs->seg = tmp | 3; \ +} + /* * Do a signal return; undo the signal stack. */ @@ -59,13 +69,13 @@ static int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, unsigned long *pax) { + void __user *buf; + unsigned int tmpflags; unsigned int err = 0; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; -#define COPY(x) (err |= __get_user(regs->x, &sc->x)) - COPY(di); COPY(si); COPY(bp); COPY(sp); COPY(bx); COPY(dx); COPY(cx); COPY(ip); COPY(r8); @@ -80,34 +90,24 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, /* Kernel saves and restores only the CS segment register on signals, * which is the bare minimum needed to allow mixed 32/64-bit code. * App's signal handler can save/restore other segments if needed. */ - { - unsigned cs; - err |= __get_user(cs, &sc->cs); - regs->cs = cs | 3; /* Force into user mode */ - } + COPY_SEG_STRICT(cs); - { - unsigned int tmpflags; - err |= __get_user(tmpflags, &sc->flags); - regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); - regs->orig_ax = -1; /* disable syscall checks */ - } + err |= __get_user(tmpflags, &sc->flags); + regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); + regs->orig_ax = -1; /* disable syscall checks */ - { - struct _fpstate __user *buf; - err |= __get_user(buf, &sc->fpstate); - err |= restore_i387_xstate(buf); - } + err |= __get_user(buf, &sc->fpstate); + err |= restore_i387_xstate(buf); err |= __get_user(*pax, &sc->ax); return err; } -asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +static long do_rt_sigreturn(struct pt_regs *regs) { struct rt_sigframe __user *frame; - sigset_t set; unsigned long ax; + sigset_t set; frame = (struct rt_sigframe __user *)(regs->sp - sizeof(long)); if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) @@ -130,10 +130,15 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) return ax; badframe: - signal_fault(regs, frame, "sigreturn"); + signal_fault(regs, frame, "rt_sigreturn"); return 0; } +asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +{ + return do_rt_sigreturn(regs); +} + /* * Set up a signal frame. */ @@ -195,8 +200,8 @@ get_stack(struct k_sigaction *ka, struct pt_regs *regs, unsigned long size) return (void __user *)round_down(sp - size, 64); } -static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, - sigset_t *set, struct pt_regs *regs) +static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; void __user *fp = NULL; @@ -209,17 +214,16 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, (unsigned long)fp - sizeof(struct rt_sigframe), 16) - 8; if (save_i387_xstate(fp) < 0) - err |= -1; + return -EFAULT; } else frame = get_stack(ka, regs, sizeof(struct rt_sigframe)) - 8; if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) - goto give_sigsegv; + return -EFAULT; if (ka->sa.sa_flags & SA_SIGINFO) { - err |= copy_siginfo_to_user(&frame->info, info); - if (err) - goto give_sigsegv; + if (copy_siginfo_to_user(&frame->info, info)) + return -EFAULT; } /* Create the ucontext. */ @@ -247,11 +251,11 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(ka->sa.sa_restorer, &frame->pretcode); } else { /* could use a vstub here */ - goto give_sigsegv; + return -EFAULT; } if (err) - goto give_sigsegv; + return -EFAULT; /* Set up registers for signal handler */ regs->di = sig; @@ -271,15 +275,45 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->cs = __USER_CS; return 0; - -give_sigsegv: - force_sigsegv(sig, current); - return -EFAULT; } /* * OK, we're invoking a handler */ +static int signr_convert(int sig) +{ + return sig; +} + +#ifdef CONFIG_IA32_EMULATION +#define is_ia32 test_thread_flag(TIF_IA32) +#else +#define is_ia32 0 +#endif + +static int +setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + int usig = signr_convert(sig); + int ret; + + /* Set up the stack frame */ + if (is_ia32) { + if (ka->sa.sa_flags & SA_SIGINFO) + ret = ia32_setup_rt_frame(usig, ka, info, set, regs); + else + ret = ia32_setup_frame(usig, ka, set, regs); + } else + ret = __setup_rt_frame(sig, ka, info, set, regs); + + if (ret) { + force_sigsegv(sig, current); + return -EFAULT; + } + + return ret; +} static int handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, @@ -317,51 +351,48 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, likely(test_and_clear_thread_flag(TIF_FORCED_TF))) regs->flags &= ~X86_EFLAGS_TF; -#ifdef CONFIG_IA32_EMULATION - if (test_thread_flag(TIF_IA32)) { - if (ka->sa.sa_flags & SA_SIGINFO) - ret = ia32_setup_rt_frame(sig, ka, info, oldset, regs); - else - ret = ia32_setup_frame(sig, ka, oldset, regs); - } else -#endif ret = setup_rt_frame(sig, ka, info, oldset, regs); - if (ret == 0) { - /* - * This has nothing to do with segment registers, - * despite the name. This magic affects uaccess.h - * macros' behavior. Reset it to the normal setting. - */ - set_fs(USER_DS); + if (ret) + return ret; - /* - * Clear the direction flag as per the ABI for function entry. - */ - regs->flags &= ~X86_EFLAGS_DF; +#ifdef CONFIG_X86_64 + /* + * This has nothing to do with segment registers, + * despite the name. This magic affects uaccess.h + * macros' behavior. Reset it to the normal setting. + */ + set_fs(USER_DS); +#endif - /* - * Clear TF when entering the signal handler, but - * notify any tracer that was single-stepping it. - * The tracer may want to single-step inside the - * handler too. - */ - regs->flags &= ~X86_EFLAGS_TF; + /* + * Clear the direction flag as per the ABI for function entry. + */ + regs->flags &= ~X86_EFLAGS_DF; - spin_lock_irq(¤t->sighand->siglock); - sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); - if (!(ka->sa.sa_flags & SA_NODEFER)) - sigaddset(¤t->blocked, sig); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); + /* + * Clear TF when entering the signal handler, but + * notify any tracer that was single-stepping it. + * The tracer may want to single-step inside the + * handler too. + */ + regs->flags &= ~X86_EFLAGS_TF; - tracehook_signal_handler(sig, info, ka, regs, - test_thread_flag(TIF_SINGLESTEP)); - } + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); - return ret; + tracehook_signal_handler(sig, info, ka, regs, + test_thread_flag(TIF_SINGLESTEP)); + + return 0; } +#define NR_restart_syscall \ + test_thread_flag(TIF_IA32) ? __NR_ia32_restart_syscall : __NR_restart_syscall /* * 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 @@ -391,7 +422,8 @@ static void do_signal(struct pt_regs *regs) signr = get_signal_to_deliver(&info, &ka, regs, NULL); if (signr > 0) { - /* Re-enable any watchpoints before delivering the + /* + * Re-enable any watchpoints before delivering the * signal to user space. The processor register will * have been cleared if the watchpoint triggered * inside the kernel. @@ -399,7 +431,7 @@ static void do_signal(struct pt_regs *regs) if (current->thread.debugreg7) set_debugreg(current->thread.debugreg7, 7); - /* Whee! Actually deliver the signal. */ + /* Whee! Actually deliver the signal. */ if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { /* * A signal was successfully delivered; the saved @@ -422,10 +454,9 @@ static void do_signal(struct pt_regs *regs) regs->ax = regs->orig_ax; regs->ip -= 2; break; + case -ERESTART_RESTARTBLOCK: - regs->ax = test_thread_flag(TIF_IA32) ? - __NR_ia32_restart_syscall : - __NR_restart_syscall; + regs->ax = NR_restart_syscall; regs->ip -= 2; break; } @@ -441,14 +472,18 @@ static void do_signal(struct pt_regs *regs) } } -void do_notify_resume(struct pt_regs *regs, void *unused, - __u32 thread_info_flags) +/* + * notification of userspace execution resumption + * - triggered by the TIF_WORK_MASK flags + */ +void +do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) { -#ifdef CONFIG_X86_MCE +#if defined(CONFIG_X86_64) && defined(CONFIG_X86_MCE) /* notify userspace of pending MCEs */ if (thread_info_flags & _TIF_MCE_NOTIFY) mce_notify_user(); -#endif /* CONFIG_X86_MCE */ +#endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) @@ -458,17 +493,23 @@ void do_notify_resume(struct pt_regs *regs, void *unused, clear_thread_flag(TIF_NOTIFY_RESUME); tracehook_notify_resume(regs); } + +#ifdef CONFIG_X86_32 + clear_thread_flag(TIF_IRET); +#endif /* CONFIG_X86_32 */ } void signal_fault(struct pt_regs *regs, void __user *frame, char *where) { struct task_struct *me = current; + if (show_unhandled_signals && printk_ratelimit()) { - printk("%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", - me->comm, me->pid, where, frame, regs->ip, - regs->sp, regs->orig_ax); + printk(KERN_INFO + "%s[%d] bad frame in %s frame:%p ip:%lx sp:%lx orax:%lx", + me->comm, me->pid, where, frame, + regs->ip, regs->sp, regs->orig_ax); print_vma_addr(" in ", regs->ip); - printk("\n"); + printk(KERN_CONT "\n"); } force_sig(SIGSEGV, me); diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 361b7a4c640..18f9b19f5f8 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -214,12 +214,16 @@ void smp_call_function_single_interrupt(struct pt_regs *regs) struct smp_ops smp_ops = { .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, .smp_prepare_cpus = native_smp_prepare_cpus, - .cpu_up = native_cpu_up, .smp_cpus_done = native_smp_cpus_done, .smp_send_stop = native_smp_send_stop, .smp_send_reschedule = native_smp_send_reschedule, + .cpu_up = native_cpu_up, + .cpu_die = native_cpu_die, + .cpu_disable = native_cpu_disable, + .play_dead = native_play_dead, + .send_call_func_ipi = native_send_call_func_ipi, .send_call_func_single_ipi = native_send_call_func_single_ipi, }; diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 9056f7e272c..8c3aca7cb34 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -52,6 +52,7 @@ #include <asm/desc.h> #include <asm/nmi.h> #include <asm/irq.h> +#include <asm/idle.h> #include <asm/smp.h> #include <asm/trampoline.h> #include <asm/cpu.h> @@ -333,14 +334,17 @@ static void __cpuinit start_secondary(void *unused) * does not change while we are assigning vectors to cpus. Holding * this lock ensures we don't half assign or remove an irq from a cpu. */ - ipi_call_lock_irq(); + ipi_call_lock(); lock_vector_lock(); __setup_vector_irq(smp_processor_id()); cpu_set(smp_processor_id(), cpu_online_map); unlock_vector_lock(); - ipi_call_unlock_irq(); + ipi_call_unlock(); per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; + /* enable local interrupts */ + local_irq_enable(); + setup_secondary_clock(); wmb(); @@ -595,10 +599,12 @@ wakeup_secondary_cpu(int logical_apicid, unsigned long start_eip) * Give the other CPU some time to accept the IPI. */ udelay(200); - maxlvt = lapic_get_maxlvt(); - if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ - apic_write(APIC_ESR, 0); - accept_status = (apic_read(APIC_ESR) & 0xEF); + if (APIC_INTEGRATED(apic_version[phys_apicid])) { + maxlvt = lapic_get_maxlvt(); + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ + apic_write(APIC_ESR, 0); + accept_status = (apic_read(APIC_ESR) & 0xEF); + } pr_debug("NMI sent.\n"); if (send_status) @@ -1255,39 +1261,6 @@ void __init native_smp_cpus_done(unsigned int max_cpus) check_nmi_watchdog(); } -#ifdef CONFIG_HOTPLUG_CPU - -static void remove_siblinginfo(int cpu) -{ - int sibling; - struct cpuinfo_x86 *c = &cpu_data(cpu); - - for_each_cpu_mask_nr(sibling, per_cpu(cpu_core_map, cpu)) { - cpu_clear(cpu, per_cpu(cpu_core_map, sibling)); - /*/ - * last thread sibling in this cpu core going down - */ - if (cpus_weight(per_cpu(cpu_sibling_map, cpu)) == 1) - cpu_data(sibling).booted_cores--; - } - - for_each_cpu_mask_nr(sibling, per_cpu(cpu_sibling_map, cpu)) - cpu_clear(cpu, per_cpu(cpu_sibling_map, sibling)); - cpus_clear(per_cpu(cpu_sibling_map, cpu)); - cpus_clear(per_cpu(cpu_core_map, cpu)); - c->phys_proc_id = 0; - c->cpu_core_id = 0; - cpu_clear(cpu, cpu_sibling_setup_map); -} - -static int additional_cpus __initdata = -1; - -static __init int setup_additional_cpus(char *s) -{ - return s && get_option(&s, &additional_cpus) ? 0 : -EINVAL; -} -early_param("additional_cpus", setup_additional_cpus); - /* * cpu_possible_map should be static, it cannot change as cpu's * are onlined, or offlined. The reason is per-cpu data-structures @@ -1307,21 +1280,13 @@ early_param("additional_cpus", setup_additional_cpus); */ __init void prefill_possible_map(void) { - int i; - int possible; + int i, possible; /* no processor from mptable or madt */ if (!num_processors) num_processors = 1; - if (additional_cpus == -1) { - if (disabled_cpus > 0) - additional_cpus = disabled_cpus; - else - additional_cpus = 0; - } - - possible = num_processors + additional_cpus; + possible = num_processors + disabled_cpus; if (possible > NR_CPUS) possible = NR_CPUS; @@ -1334,6 +1299,31 @@ __init void prefill_possible_map(void) nr_cpu_ids = possible; } +#ifdef CONFIG_HOTPLUG_CPU + +static void remove_siblinginfo(int cpu) +{ + int sibling; + struct cpuinfo_x86 *c = &cpu_data(cpu); + + for_each_cpu_mask_nr(sibling, per_cpu(cpu_core_map, cpu)) { + cpu_clear(cpu, per_cpu(cpu_core_map, sibling)); + /*/ + * last thread sibling in this cpu core going down + */ + if (cpus_weight(per_cpu(cpu_sibling_map, cpu)) == 1) + cpu_data(sibling).booted_cores--; + } + + for_each_cpu_mask_nr(sibling, per_cpu(cpu_sibling_map, cpu)) + cpu_clear(cpu, per_cpu(cpu_sibling_map, sibling)); + cpus_clear(per_cpu(cpu_sibling_map, cpu)); + cpus_clear(per_cpu(cpu_core_map, cpu)); + c->phys_proc_id = 0; + c->cpu_core_id = 0; + cpu_clear(cpu, cpu_sibling_setup_map); +} + static void __ref remove_cpu_from_maps(int cpu) { cpu_clear(cpu, cpu_online_map); @@ -1344,25 +1334,9 @@ static void __ref remove_cpu_from_maps(int cpu) numa_remove_cpu(cpu); } -int __cpu_disable(void) +void cpu_disable_common(void) { int cpu = smp_processor_id(); - - /* - * Perhaps use cpufreq to drop frequency, but that could go - * into generic code. - * - * We won't take down the boot processor on i386 due to some - * interrupts only being able to be serviced by the BSP. - * Especially so if we're not using an IOAPIC -zwane - */ - if (cpu == 0) - return -EBUSY; - - if (nmi_watchdog == NMI_LOCAL_APIC) - stop_apic_nmi_watchdog(NULL); - clear_local_APIC(); - /* * HACK: * Allow any queued timer interrupts to get serviced @@ -1380,10 +1354,32 @@ int __cpu_disable(void) remove_cpu_from_maps(cpu); unlock_vector_lock(); fixup_irqs(cpu_online_map); +} + +int native_cpu_disable(void) +{ + int cpu = smp_processor_id(); + + /* + * Perhaps use cpufreq to drop frequency, but that could go + * into generic code. + * + * We won't take down the boot processor on i386 due to some + * interrupts only being able to be serviced by the BSP. + * Especially so if we're not using an IOAPIC -zwane + */ + if (cpu == 0) + return -EBUSY; + + if (nmi_watchdog == NMI_LOCAL_APIC) + stop_apic_nmi_watchdog(NULL); + clear_local_APIC(); + + cpu_disable_common(); return 0; } -void __cpu_die(unsigned int cpu) +void native_cpu_die(unsigned int cpu) { /* We don't do anything here: idle task is faking death itself. */ unsigned int i; @@ -1400,15 +1396,45 @@ void __cpu_die(unsigned int cpu) } printk(KERN_ERR "CPU %u didn't die...\n", cpu); } + +void play_dead_common(void) +{ + idle_task_exit(); + reset_lazy_tlbstate(); + irq_ctx_exit(raw_smp_processor_id()); + c1e_remove_cpu(raw_smp_processor_id()); + + mb(); + /* Ack it */ + __get_cpu_var(cpu_state) = CPU_DEAD; + + /* + * With physical CPU hotplug, we should halt the cpu + */ + local_irq_disable(); +} + +void native_play_dead(void) +{ + play_dead_common(); + wbinvd_halt(); +} + #else /* ... !CONFIG_HOTPLUG_CPU */ -int __cpu_disable(void) +int native_cpu_disable(void) { return -ENOSYS; } -void __cpu_die(unsigned int cpu) +void native_cpu_die(unsigned int cpu) { /* We said "no" in __cpu_disable */ BUG(); } + +void native_play_dead(void) +{ + BUG(); +} + #endif diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c index bbecf8b6bf9..77b400f06ea 100644 --- a/arch/x86/kernel/time_32.c +++ b/arch/x86/kernel/time_32.c @@ -47,10 +47,9 @@ unsigned long profile_pc(struct pt_regs *regs) unsigned long pc = instruction_pointer(regs); #ifdef CONFIG_SMP - if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->cs) && - in_lock_functions(pc)) { + if (!user_mode_vm(regs) && in_lock_functions(pc)) { #ifdef CONFIG_FRAME_POINTER - return *(unsigned long *)(regs->bp + 4); + return *(unsigned long *)(regs->bp + sizeof(long)); #else unsigned long *sp = (unsigned long *)®s->sp; @@ -95,6 +94,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id) do_timer_interrupt_hook(); +#ifdef CONFIG_MCA if (MCA_bus) { /* The PS/2 uses level-triggered interrupts. You can't turn them off, nor would you want to (any attempt to @@ -108,6 +108,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id) u8 irq_v = inb_p( 0x61 ); /* read the current state */ outb_p( irq_v|0x80, 0x61 ); /* reset the IRQ */ } +#endif return IRQ_HANDLED; } diff --git a/arch/x86/kernel/time_64.c b/arch/x86/kernel/time_64.c index e3d49c553af..cb19d650c21 100644 --- a/arch/x86/kernel/time_64.c +++ b/arch/x86/kernel/time_64.c @@ -16,6 +16,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/time.h> +#include <linux/mca.h> #include <asm/i8253.h> #include <asm/hpet.h> @@ -33,23 +34,34 @@ unsigned long profile_pc(struct pt_regs *regs) /* Assume the lock function has either no stack frame or a copy of flags from PUSHF Eflags always has bits 22 and up cleared unlike kernel addresses. */ - if (!user_mode(regs) && in_lock_functions(pc)) { + if (!user_mode_vm(regs) && in_lock_functions(pc)) { +#ifdef CONFIG_FRAME_POINTER + return *(unsigned long *)(regs->bp + sizeof(long)); +#else unsigned long *sp = (unsigned long *)regs->sp; if (sp[0] >> 22) return sp[0]; if (sp[1] >> 22) return sp[1]; +#endif } return pc; } EXPORT_SYMBOL(profile_pc); -static irqreturn_t timer_event_interrupt(int irq, void *dev_id) +irqreturn_t timer_interrupt(int irq, void *dev_id) { add_pda(irq0_irqs, 1); global_clock_event->event_handler(global_clock_event); +#ifdef CONFIG_MCA + if (MCA_bus) { + u8 irq_v = inb_p(0x61); /* read the current state */ + outb_p(irq_v|0x80, 0x61); /* reset the IRQ */ + } +#endif + return IRQ_HANDLED; } @@ -100,7 +112,7 @@ unsigned long __init calibrate_cpu(void) } static struct irqaction irq0 = { - .handler = timer_event_interrupt, + .handler = timer_interrupt, .flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING, .mask = CPU_MASK_NONE, .name = "timer" @@ -111,16 +123,13 @@ void __init hpet_time_init(void) if (!hpet_enable()) setup_pit_timer(); + irq0.mask = cpumask_of_cpu(0); setup_irq(0, &irq0); } void __init time_init(void) { tsc_init(); - if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP)) - vgetcpu_mode = VGETCPU_RDTSCP; - else - vgetcpu_mode = VGETCPU_LSL; late_time_init = choose_time_init(); } diff --git a/arch/x86/kernel/tlb_32.c b/arch/x86/kernel/tlb_32.c index fec1ecedc9b..e00534b3353 100644 --- a/arch/x86/kernel/tlb_32.c +++ b/arch/x86/kernel/tlb_32.c @@ -241,3 +241,11 @@ void flush_tlb_all(void) on_each_cpu(do_flush_tlb_all, NULL, 1); } +void reset_lazy_tlbstate(void) +{ + int cpu = raw_smp_processor_id(); + + per_cpu(cpu_tlbstate, cpu).state = 0; + per_cpu(cpu_tlbstate, cpu).active_mm = &init_mm; +} + diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps.c index da5a5964fcc..e062974cce3 100644 --- a/arch/x86/kernel/traps_32.c +++ b/arch/x86/kernel/traps.c @@ -7,13 +7,11 @@ */ /* - * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'asm.s'. + * Handle hardware traps and faults. */ #include <linux/interrupt.h> #include <linux/kallsyms.h> #include <linux/spinlock.h> -#include <linux/highmem.h> #include <linux/kprobes.h> #include <linux/uaccess.h> #include <linux/utsname.h> @@ -32,6 +30,8 @@ #include <linux/bug.h> #include <linux/nmi.h> #include <linux/mm.h> +#include <linux/smp.h> +#include <linux/io.h> #ifdef CONFIG_EISA #include <linux/ioport.h> @@ -46,21 +46,31 @@ #include <linux/edac.h> #endif -#include <asm/arch_hooks.h> #include <asm/stacktrace.h> #include <asm/processor.h> #include <asm/debugreg.h> #include <asm/atomic.h> #include <asm/system.h> #include <asm/unwind.h> +#include <asm/traps.h> #include <asm/desc.h> #include <asm/i387.h> + +#include <mach_traps.h> + +#ifdef CONFIG_X86_64 +#include <asm/pgalloc.h> +#include <asm/proto.h> +#include <asm/pda.h> +#else +#include <asm/processor-flags.h> +#include <asm/arch_hooks.h> #include <asm/nmi.h> #include <asm/smp.h> #include <asm/io.h> #include <asm/traps.h> -#include "mach_traps.h" +#include "cpu/mcheck/mce.h" DECLARE_BITMAP(used_vectors, NR_VECTORS); EXPORT_SYMBOL_GPL(used_vectors); @@ -77,418 +87,104 @@ char ignore_fpu_irq; */ gate_desc idt_table[256] __attribute__((__section__(".data.idt"))) = { { { { 0, 0 } } }, }; - -int panic_on_unrecovered_nmi; -int kstack_depth_to_print = 24; -static unsigned int code_bytes = 64; -static int ignore_nmis; -static int die_counter; - -void printk_address(unsigned long address, int reliable) -{ -#ifdef CONFIG_KALLSYMS - unsigned long offset = 0; - unsigned long symsize; - const char *symname; - char *modname; - char *delim = ":"; - char namebuf[KSYM_NAME_LEN]; - char reliab[4] = ""; - - symname = kallsyms_lookup(address, &symsize, &offset, - &modname, namebuf); - if (!symname) { - printk(" [<%08lx>]\n", address); - return; - } - if (!reliable) - strcpy(reliab, "? "); - - if (!modname) - modname = delim = ""; - printk(" [<%08lx>] %s%s%s%s%s+0x%lx/0x%lx\n", - address, reliab, delim, modname, delim, symname, offset, symsize); -#else - printk(" [<%08lx>]\n", address); #endif -} - -static inline int valid_stack_ptr(struct thread_info *tinfo, - void *p, unsigned int size) -{ - void *t = tinfo; - return p > t && p <= t + THREAD_SIZE - size; -} - -/* The form of the top of the frame on the stack */ -struct stack_frame { - struct stack_frame *next_frame; - unsigned long return_address; -}; - -static inline unsigned long -print_context_stack(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) -{ - struct stack_frame *frame = (struct stack_frame *)bp; - - while (valid_stack_ptr(tinfo, stack, sizeof(*stack))) { - unsigned long addr; - - addr = *stack; - if (__kernel_text_address(addr)) { - if ((unsigned long) stack == bp + 4) { - ops->address(data, addr, 1); - frame = frame->next_frame; - bp = (unsigned long) frame; - } else { - ops->address(data, addr, bp == 0); - } - } - stack++; - } - return bp; -} - -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) -{ - if (!task) - task = current; - - if (!stack) { - unsigned long dummy; - stack = &dummy; - if (task != current) - stack = (unsigned long *)task->thread.sp; - } - -#ifdef CONFIG_FRAME_POINTER - if (!bp) { - if (task == current) { - /* Grab bp right from our regs */ - asm("movl %%ebp, %0" : "=r" (bp) :); - } else { - /* bp is the last reg pushed by switch_to */ - bp = *(unsigned long *) task->thread.sp; - } - } -#endif - - for (;;) { - struct thread_info *context; - - context = (struct thread_info *) - ((unsigned long)stack & (~(THREAD_SIZE - 1))); - bp = print_context_stack(context, stack, bp, ops, data); - /* - * Should be after the line below, but somewhere - * in early boot context comes out corrupted and we - * can't reference it: - */ - if (ops->stack(data, "IRQ") < 0) - break; - stack = (unsigned long *)context->previous_esp; - if (!stack) - break; - touch_nmi_watchdog(); - } -} -EXPORT_SYMBOL(dump_trace); - -static void -print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) -{ - printk(data); - print_symbol(msg, symbol); - printk("\n"); -} - -static void print_trace_warning(void *data, char *msg) -{ - printk("%s%s\n", (char *)data, msg); -} -static int print_trace_stack(void *data, char *name) -{ - return 0; -} - -/* - * Print one address/symbol entries per line. - */ -static void print_trace_address(void *data, unsigned long addr, int reliable) -{ - printk("%s [<%08lx>] ", (char *)data, addr); - if (!reliable) - printk("? "); - print_symbol("%s\n", addr); - touch_nmi_watchdog(); -} - -static const struct stacktrace_ops print_trace_ops = { - .warning = print_trace_warning, - .warning_symbol = print_trace_warning_symbol, - .stack = print_trace_stack, - .address = print_trace_address, -}; +static int ignore_nmis; -static void -show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, char *log_lvl) +static inline void conditional_sti(struct pt_regs *regs) { - dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); - printk("%s =======================\n", log_lvl); + if (regs->flags & X86_EFLAGS_IF) + local_irq_enable(); } -void show_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp) +static inline void preempt_conditional_sti(struct pt_regs *regs) { - show_trace_log_lvl(task, regs, stack, bp, ""); + inc_preempt_count(); + if (regs->flags & X86_EFLAGS_IF) + local_irq_enable(); } -static void -show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *sp, unsigned long bp, char *log_lvl) +static inline void preempt_conditional_cli(struct pt_regs *regs) { - unsigned long *stack; - int i; - - if (sp == NULL) { - if (task) - sp = (unsigned long *)task->thread.sp; - else - sp = (unsigned long *)&sp; - } - - stack = sp; - for (i = 0; i < kstack_depth_to_print; i++) { - if (kstack_end(stack)) - break; - if (i && ((i % 8) == 0)) - printk("\n%s ", log_lvl); - printk("%08lx ", *stack++); - } - printk("\n%sCall Trace:\n", log_lvl); - - show_trace_log_lvl(task, regs, sp, bp, log_lvl); + if (regs->flags & X86_EFLAGS_IF) + local_irq_disable(); + dec_preempt_count(); } -void show_stack(struct task_struct *task, unsigned long *sp) +#ifdef CONFIG_X86_32 +static inline void +die_if_kernel(const char *str, struct pt_regs *regs, long err) { - printk(" "); - show_stack_log_lvl(task, NULL, sp, 0, ""); + if (!user_mode_vm(regs)) + die(str, regs, err); } /* - * The architecture-independent dump_stack generator + * Perform the lazy TSS's I/O bitmap copy. If the TSS has an + * invalid offset set (the LAZY one) and the faulting thread has + * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS, + * we set the offset field correctly and return 1. */ -void dump_stack(void) +static int lazy_iobitmap_copy(void) { - unsigned long bp = 0; - unsigned long stack; - -#ifdef CONFIG_FRAME_POINTER - if (!bp) - asm("movl %%ebp, %0" : "=r" (bp):); -#endif - - printk("Pid: %d, comm: %.20s %s %s %.*s\n", - current->pid, current->comm, print_tainted(), - init_utsname()->release, - (int)strcspn(init_utsname()->version, " "), - init_utsname()->version); - - show_trace(current, NULL, &stack, bp); -} - -EXPORT_SYMBOL(dump_stack); - -void show_registers(struct pt_regs *regs) -{ - int i; + struct thread_struct *thread; + struct tss_struct *tss; + int cpu; - print_modules(); - __show_registers(regs, 0); + cpu = get_cpu(); + tss = &per_cpu(init_tss, cpu); + thread = ¤t->thread; - printk(KERN_EMERG "Process %.*s (pid: %d, ti=%p task=%p task.ti=%p)", - TASK_COMM_LEN, current->comm, task_pid_nr(current), - current_thread_info(), current, task_thread_info(current)); - /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (!user_mode_vm(regs)) { - unsigned int code_prologue = code_bytes * 43 / 64; - unsigned int code_len = code_bytes; - unsigned char c; - u8 *ip; - - printk("\n" KERN_EMERG "Stack: "); - show_stack_log_lvl(NULL, regs, ®s->sp, 0, KERN_EMERG); - - printk(KERN_EMERG "Code: "); - - ip = (u8 *)regs->ip - code_prologue; - if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { - /* try starting at EIP */ - ip = (u8 *)regs->ip; - code_len = code_len - code_prologue + 1; - } - for (i = 0; i < code_len; i++, ip++) { - if (ip < (u8 *)PAGE_OFFSET || - probe_kernel_address(ip, c)) { - printk(" Bad EIP value."); - break; - } - if (ip == (u8 *)regs->ip) - printk("<%02x> ", c); - else - printk("%02x ", c); + if (tss->x86_tss.io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY && + thread->io_bitmap_ptr) { + memcpy(tss->io_bitmap, thread->io_bitmap_ptr, + thread->io_bitmap_max); + /* + * If the previously set map was extending to higher ports + * than the current one, pad extra space with 0xff (no access). + */ + if (thread->io_bitmap_max < tss->io_bitmap_max) { + memset((char *) tss->io_bitmap + + thread->io_bitmap_max, 0xff, + tss->io_bitmap_max - thread->io_bitmap_max); } - } - printk("\n"); -} - -int is_valid_bugaddr(unsigned long ip) -{ - unsigned short ud2; - - if (ip < PAGE_OFFSET) - return 0; - if (probe_kernel_address((unsigned short *)ip, ud2)) - return 0; - - return ud2 == 0x0b0f; -} - -static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; -static int die_owner = -1; -static unsigned int die_nest_count; - -unsigned __kprobes long oops_begin(void) -{ - unsigned long flags; - - oops_enter(); - - if (die_owner != raw_smp_processor_id()) { - console_verbose(); - raw_local_irq_save(flags); - __raw_spin_lock(&die_lock); - die_owner = smp_processor_id(); - die_nest_count = 0; - bust_spinlocks(1); - } else { - raw_local_irq_save(flags); - } - die_nest_count++; - return flags; -} - -void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) -{ - bust_spinlocks(0); - die_owner = -1; - add_taint(TAINT_DIE); - __raw_spin_unlock(&die_lock); - raw_local_irq_restore(flags); - - if (!regs) - return; - - if (kexec_should_crash(current)) - crash_kexec(regs); - - if (in_interrupt()) - panic("Fatal exception in interrupt"); - - if (panic_on_oops) - panic("Fatal exception"); - - oops_exit(); - do_exit(signr); -} - -int __kprobes __die(const char *str, struct pt_regs *regs, long err) -{ - unsigned short ss; - unsigned long sp; + tss->io_bitmap_max = thread->io_bitmap_max; + tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET; + tss->io_bitmap_owner = thread; + put_cpu(); - printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter); -#ifdef CONFIG_PREEMPT - printk("PREEMPT "); -#endif -#ifdef CONFIG_SMP - printk("SMP "); -#endif -#ifdef CONFIG_DEBUG_PAGEALLOC - printk("DEBUG_PAGEALLOC"); -#endif - printk("\n"); - if (notify_die(DIE_OOPS, str, regs, err, - current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) return 1; - - show_registers(regs); - /* Executive summary in case the oops scrolled away */ - sp = (unsigned long) (®s->sp); - savesegment(ss, ss); - if (user_mode(regs)) { - sp = regs->sp; - ss = regs->ss & 0xffff; } - printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip); - print_symbol("%s", regs->ip); - printk(" SS:ESP %04x:%08lx\n", ss, sp); - return 0; -} - -/* - * This is gone through when something in the kernel has done something bad - * and is about to be terminated: - */ -void die(const char *str, struct pt_regs *regs, long err) -{ - unsigned long flags = oops_begin(); - - if (die_nest_count < 3) { - report_bug(regs->ip, regs); - - if (__die(str, regs, err)) - regs = NULL; - } else { - printk(KERN_EMERG "Recursive die() failure, output suppressed\n"); - } - - oops_end(flags, regs, SIGSEGV); -} + put_cpu(); -static inline void -die_if_kernel(const char *str, struct pt_regs *regs, long err) -{ - if (!user_mode_vm(regs)) - die(str, regs, err); + return 0; } +#endif static void __kprobes -do_trap(int trapnr, int signr, char *str, int vm86, struct pt_regs *regs, +do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, long error_code, siginfo_t *info) { struct task_struct *tsk = current; +#ifdef CONFIG_X86_32 if (regs->flags & X86_VM_MASK) { - if (vm86) + /* + * traps 0, 1, 3, 4, and 5 should be forwarded to vm86. + * On nmi (interrupt 2), do_trap should not be called. + */ + if (trapnr < 6) goto vm86_trap; goto trap_signal; } +#endif if (!user_mode(regs)) goto kernel_trap; +#ifdef CONFIG_X86_32 trap_signal: +#endif /* * We want error_code and trap_no set for userspace faults and * kernelspace faults which result in die(), but not @@ -501,6 +197,18 @@ trap_signal: tsk->thread.error_code = error_code; tsk->thread.trap_no = trapnr; +#ifdef CONFIG_X86_64 + if (show_unhandled_signals && unhandled_signal(tsk, signr) && + printk_ratelimit()) { + printk(KERN_INFO + "%s[%d] trap %s ip:%lx sp:%lx error:%lx", + tsk->comm, tsk->pid, str, + regs->ip, regs->sp, error_code); + print_vma_addr(" in ", regs->ip); + printk("\n"); + } +#endif + if (info) force_sig_info(signr, info, tsk); else @@ -515,29 +223,29 @@ kernel_trap: } return; +#ifdef CONFIG_X86_32 vm86_trap: if (handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, trapnr)) goto trap_signal; return; +#endif } #define DO_ERROR(trapnr, signr, str, name) \ -void do_##name(struct pt_regs *regs, long error_code) \ +dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ { \ - trace_hardirqs_fixup(); \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ == NOTIFY_STOP) \ return; \ - do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ + conditional_sti(regs); \ + do_trap(trapnr, signr, str, regs, error_code, NULL); \ } -#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr, irq) \ -void do_##name(struct pt_regs *regs, long error_code) \ +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \ { \ siginfo_t info; \ - if (irq) \ - local_irq_enable(); \ info.si_signo = signr; \ info.si_errno = 0; \ info.si_code = sicode; \ @@ -545,90 +253,68 @@ void do_##name(struct pt_regs *regs, long error_code) \ if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ == NOTIFY_STOP) \ return; \ - do_trap(trapnr, signr, str, 0, regs, error_code, &info); \ + conditional_sti(regs); \ + do_trap(trapnr, signr, str, regs, error_code, &info); \ } -#define DO_VM86_ERROR(trapnr, signr, str, name) \ -void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - do_trap(trapnr, signr, str, 1, regs, error_code, NULL); \ -} - -#define DO_VM86_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ -void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - siginfo_t info; \ - info.si_signo = signr; \ - info.si_errno = 0; \ - info.si_code = sicode; \ - info.si_addr = (void __user *)siaddr; \ - trace_hardirqs_fixup(); \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - do_trap(trapnr, signr, str, 1, regs, error_code, &info); \ -} - -DO_VM86_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip) -#ifndef CONFIG_KPROBES -DO_VM86_ERROR(3, SIGTRAP, "int3", int3) -#endif -DO_VM86_ERROR(4, SIGSEGV, "overflow", overflow) -DO_VM86_ERROR(5, SIGSEGV, "bounds", bounds) -DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip, 0) +DO_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip) +DO_ERROR(4, SIGSEGV, "overflow", overflow) +DO_ERROR(5, SIGSEGV, "bounds", bounds) +DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip) DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) +#ifdef CONFIG_X86_32 DO_ERROR(12, SIGBUS, "stack segment", stack_segment) -DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0, 0) -DO_ERROR_INFO(32, SIGILL, "iret exception", iret_error, ILL_BADSTK, 0, 1) +#endif +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) + +#ifdef CONFIG_X86_64 +/* Runs on IST stack */ +dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code) +{ + if (notify_die(DIE_TRAP, "stack segment", regs, error_code, + 12, SIGBUS) == NOTIFY_STOP) + return; + preempt_conditional_sti(regs); + do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL); + preempt_conditional_cli(regs); +} + +dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) +{ + static const char str[] = "double fault"; + struct task_struct *tsk = current; + + /* Return not checked because double check cannot be ignored */ + notify_die(DIE_TRAP, str, regs, error_code, 8, SIGSEGV); -void __kprobes + tsk->thread.error_code = error_code; + tsk->thread.trap_no = 8; + + /* This is always a kernel trap and never fixable (and thus must + never return). */ + for (;;) + die(str, regs, error_code); +} +#endif + +dotraplinkage void __kprobes do_general_protection(struct pt_regs *regs, long error_code) { struct task_struct *tsk; - struct thread_struct *thread; - struct tss_struct *tss; - int cpu; - cpu = get_cpu(); - tss = &per_cpu(init_tss, cpu); - thread = ¤t->thread; - - /* - * Perform the lazy TSS's I/O bitmap copy. If the TSS has an - * invalid offset set (the LAZY one) and the faulting thread has - * a valid I/O bitmap pointer, we copy the I/O bitmap in the TSS - * and we set the offset field correctly. Then we let the CPU to - * restart the faulting instruction. - */ - if (tss->x86_tss.io_bitmap_base == INVALID_IO_BITMAP_OFFSET_LAZY && - thread->io_bitmap_ptr) { - memcpy(tss->io_bitmap, thread->io_bitmap_ptr, - thread->io_bitmap_max); - /* - * If the previously set map was extending to higher ports - * than the current one, pad extra space with 0xff (no access). - */ - if (thread->io_bitmap_max < tss->io_bitmap_max) { - memset((char *) tss->io_bitmap + - thread->io_bitmap_max, 0xff, - tss->io_bitmap_max - thread->io_bitmap_max); - } - tss->io_bitmap_max = thread->io_bitmap_max; - tss->x86_tss.io_bitmap_base = IO_BITMAP_OFFSET; - tss->io_bitmap_owner = thread; - put_cpu(); + conditional_sti(regs); +#ifdef CONFIG_X86_32 + if (lazy_iobitmap_copy()) { + /* restart the faulting instruction */ return; } - put_cpu(); if (regs->flags & X86_VM_MASK) goto gp_in_vm86; +#endif tsk = current; if (!user_mode(regs)) @@ -650,10 +336,12 @@ do_general_protection(struct pt_regs *regs, long error_code) force_sig(SIGSEGV, tsk); return; +#ifdef CONFIG_X86_32 gp_in_vm86: local_irq_enable(); handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code); return; +#endif gp_in_kernel: if (fixup_exception(regs)) @@ -690,7 +378,8 @@ mem_parity_error(unsigned char reason, struct pt_regs *regs) printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); /* Clear and disable the memory parity error line. */ - clear_mem_error(reason); + reason = (reason & 0xf) | 4; + outb(reason, 0x61); } static notrace __kprobes void @@ -716,7 +405,8 @@ io_check_error(unsigned char reason, struct pt_regs *regs) static notrace __kprobes void unknown_nmi_error(unsigned char reason, struct pt_regs *regs) { - if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) + if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == + NOTIFY_STOP) return; #ifdef CONFIG_MCA /* @@ -739,41 +429,6 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs) printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); } -static DEFINE_SPINLOCK(nmi_print_lock); - -void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic) -{ - if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) - return; - - spin_lock(&nmi_print_lock); - /* - * We are in trouble anyway, lets at least try - * to get a message out: - */ - bust_spinlocks(1); - printk(KERN_EMERG "%s", str); - printk(" on CPU%d, ip %08lx, registers:\n", - smp_processor_id(), regs->ip); - show_registers(regs); - if (do_panic) - panic("Non maskable interrupt"); - console_silent(); - spin_unlock(&nmi_print_lock); - bust_spinlocks(0); - - /* - * If we are in kernel we are probably nested up pretty bad - * and might aswell get out now while we still can: - */ - if (!user_mode_vm(regs)) { - current->thread.trap_no = 2; - crash_kexec(regs); - } - - do_exit(SIGSEGV); -} - static notrace __kprobes void default_do_nmi(struct pt_regs *regs) { unsigned char reason = 0; @@ -812,22 +467,25 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs) mem_parity_error(reason, regs); if (reason & 0x40) io_check_error(reason, regs); +#ifdef CONFIG_X86_32 /* * Reassert NMI in case it became active meanwhile * as it's edge-triggered: */ reassert_nmi(); +#endif } -notrace __kprobes void do_nmi(struct pt_regs *regs, long error_code) +dotraplinkage notrace __kprobes void +do_nmi(struct pt_regs *regs, long error_code) { - int cpu; - nmi_enter(); - cpu = smp_processor_id(); - - ++nmi_count(cpu); +#ifdef CONFIG_X86_32 + { int cpu; cpu = smp_processor_id(); ++nmi_count(cpu); } +#else + add_pda(__nmi_count, 1); +#endif if (!ignore_nmis) default_do_nmi(regs); @@ -847,21 +505,44 @@ void restart_nmi(void) acpi_nmi_enable(); } -#ifdef CONFIG_KPROBES -void __kprobes do_int3(struct pt_regs *regs, long error_code) +/* May run on IST stack. */ +dotraplinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) { - trace_hardirqs_fixup(); - +#ifdef CONFIG_KPROBES if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) == NOTIFY_STOP) return; - /* - * This is an interrupt gate, because kprobes wants interrupts - * disabled. Normal trap handlers don't. - */ - restore_interrupts(regs); +#else + if (notify_die(DIE_TRAP, "int3", regs, error_code, 3, SIGTRAP) + == NOTIFY_STOP) + return; +#endif + + preempt_conditional_sti(regs); + do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); + preempt_conditional_cli(regs); +} - do_trap(3, SIGTRAP, "int3", 1, regs, error_code, NULL); +#ifdef CONFIG_X86_64 +/* Help handler running on IST stack to switch back to user stack + for scheduling or signal handling. The actual stack switch is done in + entry.S */ +asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) +{ + struct pt_regs *regs = eregs; + /* Did already sync */ + if (eregs == (struct pt_regs *)eregs->sp) + ; + /* Exception from user space */ + else if (user_mode(eregs)) + regs = task_pt_regs(current); + /* Exception from kernel and interrupts are enabled. Move to + kernel process stack. */ + else if (eregs->flags & X86_EFLAGS_IF) + regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs)); + if (eregs != regs) + *regs = *eregs; + return regs; } #endif @@ -886,13 +567,14 @@ void __kprobes do_int3(struct pt_regs *regs, long error_code) * about restoring all the debug state, and ptrace doesn't have to * find every occurrence of the TF bit that could be saved away even * by user code) + * + * May run on IST stack. */ -void __kprobes do_debug(struct pt_regs *regs, long error_code) +dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code) { struct task_struct *tsk = current; - unsigned int condition; - - trace_hardirqs_fixup(); + unsigned long condition; + int si_code; get_debugreg(condition, 6); @@ -905,9 +587,9 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, SIGTRAP) == NOTIFY_STOP) return; + /* It's safe to allow irq's after DR6 has been saved */ - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); + preempt_conditional_sti(regs); /* Mask out spurious debug traps due to lazy DR7 setting */ if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { @@ -915,8 +597,10 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) goto clear_dr7; } +#ifdef CONFIG_X86_32 if (regs->flags & X86_VM_MASK) goto debug_vm86; +#endif /* Save debug status register where ptrace can see it */ tsk->thread.debugreg6 = condition; @@ -926,17 +610,13 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) * kernel space (but re-enable TF when returning to user mode). */ if (condition & DR_STEP) { - /* - * We already checked v86 mode above, so we can - * check for kernel mode by just checking the CPL - * of CS. - */ if (!user_mode(regs)) goto clear_TF_reenable; } + si_code = get_si_code(condition); /* Ok, finally something we can handle */ - send_sigtrap(tsk, regs, error_code); + send_sigtrap(tsk, regs, error_code, si_code); /* * Disable additional traps. They'll be re-enabled when @@ -944,18 +624,37 @@ void __kprobes do_debug(struct pt_regs *regs, long error_code) */ clear_dr7: set_debugreg(0, 7); + preempt_conditional_cli(regs); return; +#ifdef CONFIG_X86_32 debug_vm86: handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1); + preempt_conditional_cli(regs); return; +#endif clear_TF_reenable: set_tsk_thread_flag(tsk, TIF_SINGLESTEP); regs->flags &= ~X86_EFLAGS_TF; + preempt_conditional_cli(regs); return; } +#ifdef CONFIG_X86_64 +static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr) +{ + if (fixup_exception(regs)) + return 1; + + notify_die(DIE_GPF, str, regs, 0, trapnr, SIGFPE); + /* Illegal floating point operation in the kernel */ + current->thread.trap_no = trapnr; + die(str, regs, 0); + return 0; +} +#endif + /* * Note that we play around with the 'TS' bit in an attempt to get * the correct behaviour even in the presence of the asynchronous @@ -992,7 +691,9 @@ void math_error(void __user *ip) swd = get_fpu_swd(task); switch (swd & ~cwd & 0x3f) { case 0x000: /* No unmasked exception */ +#ifdef CONFIG_X86_32 return; +#endif default: /* Multiple exceptions */ break; case 0x001: /* Invalid Op */ @@ -1020,9 +721,18 @@ void math_error(void __user *ip) force_sig_info(SIGFPE, &info, task); } -void do_coprocessor_error(struct pt_regs *regs, long error_code) +dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code) { + conditional_sti(regs); + +#ifdef CONFIG_X86_32 ignore_fpu_irq = 1; +#else + if (!user_mode(regs) && + kernel_math_error(regs, "kernel x87 math error", 16)) + return; +#endif + math_error((void __user *)regs->ip); } @@ -1074,8 +784,12 @@ static void simd_math_error(void __user *ip) force_sig_info(SIGFPE, &info, task); } -void do_simd_coprocessor_error(struct pt_regs *regs, long error_code) +dotraplinkage void +do_simd_coprocessor_error(struct pt_regs *regs, long error_code) { + conditional_sti(regs); + +#ifdef CONFIG_X86_32 if (cpu_has_xmm) { /* Handle SIMD FPU exceptions on PIII+ processors. */ ignore_fpu_irq = 1; @@ -1094,16 +808,25 @@ void do_simd_coprocessor_error(struct pt_regs *regs, long error_code) current->thread.error_code = error_code; die_if_kernel("cache flush denied", regs, error_code); force_sig(SIGSEGV, current); +#else + if (!user_mode(regs) && + kernel_math_error(regs, "kernel simd math error", 19)) + return; + simd_math_error((void __user *)regs->ip); +#endif } -void do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) +dotraplinkage void +do_spurious_interrupt_bug(struct pt_regs *regs, long error_code) { + conditional_sti(regs); #if 0 /* No need to warn about this any longer. */ printk(KERN_INFO "Ignoring P6 Local APIC Spurious Interrupt Bug...\n"); #endif } +#ifdef CONFIG_X86_32 unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp) { struct desc_struct *gdt = get_cpu_gdt_table(smp_processor_id()); @@ -1122,6 +845,15 @@ unsigned long patch_espfix_desc(unsigned long uesp, unsigned long kesp) return new_kesp; } +#else +asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void) +{ +} + +asmlinkage void __attribute__((weak)) mce_threshold_interrupt(void) +{ +} +#endif /* * 'math_state_restore()' saves the current math information in the @@ -1154,14 +886,24 @@ asmlinkage void math_state_restore(void) } clts(); /* Allow maths ops (or we recurse) */ +#ifdef CONFIG_X86_32 restore_fpu(tsk); +#else + /* + * Paranoid restore. send a SIGSEGV if we fail to restore the state. + */ + if (unlikely(restore_fpu_checking(tsk))) { + stts(); + force_sig(SIGSEGV, tsk); + return; + } +#endif thread->status |= TS_USEDFPU; /* So we fnsave on switch_to() */ tsk->fpu_counter++; } EXPORT_SYMBOL_GPL(math_state_restore); #ifndef CONFIG_MATH_EMULATION - asmlinkage void math_emulate(long arg) { printk(KERN_EMERG @@ -1170,12 +912,54 @@ asmlinkage void math_emulate(long arg) force_sig(SIGFPE, current); schedule(); } - #endif /* CONFIG_MATH_EMULATION */ +dotraplinkage void __kprobes +do_device_not_available(struct pt_regs *regs, long error) +{ +#ifdef CONFIG_X86_32 + if (read_cr0() & X86_CR0_EM) { + conditional_sti(regs); + math_emulate(0); + } else { + math_state_restore(); /* interrupts still off */ + conditional_sti(regs); + } +#else + math_state_restore(); +#endif +} + +#ifdef CONFIG_X86_32 +#ifdef CONFIG_X86_MCE +dotraplinkage void __kprobes do_machine_check(struct pt_regs *regs, long error) +{ + conditional_sti(regs); + machine_check_vector(regs, error); +} +#endif + +dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code) +{ + siginfo_t info; + local_irq_enable(); + + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = ILL_BADSTK; + info.si_addr = 0; + if (notify_die(DIE_TRAP, "iret exception", + regs, error_code, 32, SIGILL) == NOTIFY_STOP) + return; + do_trap(32, SIGILL, "iret exception", regs, error_code, &info); +} +#endif + void __init trap_init(void) { +#ifdef CONFIG_X86_32 int i; +#endif #ifdef CONFIG_EISA void __iomem *p = early_ioremap(0x0FFFD9, 4); @@ -1185,29 +969,40 @@ void __init trap_init(void) early_iounmap(p, 4); #endif - set_trap_gate(0, ÷_error); - set_intr_gate(1, &debug); - set_intr_gate(2, &nmi); - set_system_intr_gate(3, &int3); /* int3 can be called from all */ - set_system_gate(4, &overflow); /* int4 can be called from all */ - set_trap_gate(5, &bounds); - set_trap_gate(6, &invalid_op); - set_trap_gate(7, &device_not_available); + set_intr_gate(0, ÷_error); + set_intr_gate_ist(1, &debug, DEBUG_STACK); + set_intr_gate_ist(2, &nmi, NMI_STACK); + /* int3 can be called from all */ + set_system_intr_gate_ist(3, &int3, DEBUG_STACK); + /* int4 can be called from all */ + set_system_intr_gate(4, &overflow); + set_intr_gate(5, &bounds); + set_intr_gate(6, &invalid_op); + set_intr_gate(7, &device_not_available); +#ifdef CONFIG_X86_32 set_task_gate(8, GDT_ENTRY_DOUBLEFAULT_TSS); - set_trap_gate(9, &coprocessor_segment_overrun); - set_trap_gate(10, &invalid_TSS); - set_trap_gate(11, &segment_not_present); - set_trap_gate(12, &stack_segment); - set_trap_gate(13, &general_protection); +#else + set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK); +#endif + set_intr_gate(9, &coprocessor_segment_overrun); + set_intr_gate(10, &invalid_TSS); + set_intr_gate(11, &segment_not_present); + set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK); + set_intr_gate(13, &general_protection); set_intr_gate(14, &page_fault); - set_trap_gate(15, &spurious_interrupt_bug); - set_trap_gate(16, &coprocessor_error); - set_trap_gate(17, &alignment_check); + set_intr_gate(15, &spurious_interrupt_bug); + set_intr_gate(16, &coprocessor_error); + set_intr_gate(17, &alignment_check); #ifdef CONFIG_X86_MCE - set_trap_gate(18, &machine_check); + set_intr_gate_ist(18, &machine_check, MCE_STACK); #endif - set_trap_gate(19, &simd_coprocessor_error); + set_intr_gate(19, &simd_coprocessor_error); +#ifdef CONFIG_IA32_EMULATION + set_system_intr_gate(IA32_SYSCALL_VECTOR, ia32_syscall); +#endif + +#ifdef CONFIG_X86_32 if (cpu_has_fxsr) { printk(KERN_INFO "Enabling fast FPU save and restore... "); set_in_cr4(X86_CR4_OSFXSR); @@ -1220,36 +1015,20 @@ void __init trap_init(void) printk("done.\n"); } - set_system_gate(SYSCALL_VECTOR, &system_call); + set_system_trap_gate(SYSCALL_VECTOR, &system_call); /* Reserve all the builtin and the syscall vector: */ for (i = 0; i < FIRST_EXTERNAL_VECTOR; i++) set_bit(i, used_vectors); set_bit(SYSCALL_VECTOR, used_vectors); - +#endif /* * Should be a barrier for any external CPU state: */ cpu_init(); +#ifdef CONFIG_X86_32 trap_init_hook(); +#endif } - -static int __init kstack_setup(char *s) -{ - kstack_depth_to_print = simple_strtoul(s, NULL, 0); - - return 1; -} -__setup("kstack=", kstack_setup); - -static int __init code_bytes_setup(char *s) -{ - code_bytes = simple_strtoul(s, NULL, 0); - if (code_bytes > 8192) - code_bytes = 8192; - - return 1; -} -__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c deleted file mode 100644 index 2887a789e38..00000000000 --- a/arch/x86/kernel/traps_64.c +++ /dev/null @@ -1,1214 +0,0 @@ -/* - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs - * - * Pentium III FXSR, SSE support - * Gareth Hughes <gareth@valinux.com>, May 2000 - */ - -/* - * 'Traps.c' handles hardware traps and faults after we have saved some - * state in 'entry.S'. - */ -#include <linux/moduleparam.h> -#include <linux/interrupt.h> -#include <linux/kallsyms.h> -#include <linux/spinlock.h> -#include <linux/kprobes.h> -#include <linux/uaccess.h> -#include <linux/utsname.h> -#include <linux/kdebug.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/ptrace.h> -#include <linux/string.h> -#include <linux/unwind.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/kexec.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/init.h> -#include <linux/bug.h> -#include <linux/nmi.h> -#include <linux/mm.h> -#include <linux/smp.h> -#include <linux/io.h> - -#if defined(CONFIG_EDAC) -#include <linux/edac.h> -#endif - -#include <asm/stacktrace.h> -#include <asm/processor.h> -#include <asm/debugreg.h> -#include <asm/atomic.h> -#include <asm/system.h> -#include <asm/unwind.h> -#include <asm/desc.h> -#include <asm/i387.h> -#include <asm/pgalloc.h> -#include <asm/proto.h> -#include <asm/pda.h> -#include <asm/traps.h> - -#include <mach_traps.h> - -int panic_on_unrecovered_nmi; -int kstack_depth_to_print = 12; -static unsigned int code_bytes = 64; -static int ignore_nmis; -static int die_counter; - -static inline void conditional_sti(struct pt_regs *regs) -{ - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); -} - -static inline void preempt_conditional_sti(struct pt_regs *regs) -{ - inc_preempt_count(); - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); -} - -static inline void preempt_conditional_cli(struct pt_regs *regs) -{ - if (regs->flags & X86_EFLAGS_IF) - local_irq_disable(); - /* Make sure to not schedule here because we could be running - on an exception stack. */ - dec_preempt_count(); -} - -void printk_address(unsigned long address, int reliable) -{ - printk(" [<%016lx>] %s%pS\n", - address, reliable ? "" : "? ", (void *) address); -} - -static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, - unsigned *usedp, char **idp) -{ - static char ids[][8] = { - [DEBUG_STACK - 1] = "#DB", - [NMI_STACK - 1] = "NMI", - [DOUBLEFAULT_STACK - 1] = "#DF", - [STACKFAULT_STACK - 1] = "#SS", - [MCE_STACK - 1] = "#MC", -#if DEBUG_STKSZ > EXCEPTION_STKSZ - [N_EXCEPTION_STACKS ... - N_EXCEPTION_STACKS + DEBUG_STKSZ / EXCEPTION_STKSZ - 2] = "#DB[?]" -#endif - }; - unsigned k; - - /* - * Iterate over all exception stacks, and figure out whether - * 'stack' is in one of them: - */ - for (k = 0; k < N_EXCEPTION_STACKS; k++) { - unsigned long end = per_cpu(orig_ist, cpu).ist[k]; - /* - * Is 'stack' above this exception frame's end? - * If yes then skip to the next frame. - */ - if (stack >= end) - continue; - /* - * Is 'stack' above this exception frame's start address? - * If yes then we found the right frame. - */ - if (stack >= end - EXCEPTION_STKSZ) { - /* - * Make sure we only iterate through an exception - * stack once. If it comes up for the second time - * then there's something wrong going on - just - * break out and return NULL: - */ - if (*usedp & (1U << k)) - break; - *usedp |= 1U << k; - *idp = ids[k]; - return (unsigned long *)end; - } - /* - * If this is a debug stack, and if it has a larger size than - * the usual exception stacks, then 'stack' might still - * be within the lower portion of the debug stack: - */ -#if DEBUG_STKSZ > EXCEPTION_STKSZ - if (k == DEBUG_STACK - 1 && stack >= end - DEBUG_STKSZ) { - unsigned j = N_EXCEPTION_STACKS - 1; - - /* - * Black magic. A large debug stack is composed of - * multiple exception stack entries, which we - * iterate through now. Dont look: - */ - do { - ++j; - end -= EXCEPTION_STKSZ; - ids[j][4] = '1' + (j - N_EXCEPTION_STACKS); - } while (stack < end - EXCEPTION_STKSZ); - if (*usedp & (1U << j)) - break; - *usedp |= 1U << j; - *idp = ids[j]; - return (unsigned long *)end; - } -#endif - } - return NULL; -} - -/* - * x86-64 can have up to three kernel stacks: - * process stack - * interrupt stack - * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack - */ - -static inline int valid_stack_ptr(struct thread_info *tinfo, - void *p, unsigned int size, void *end) -{ - void *t = tinfo; - if (end) { - if (p < end && p >= (end-THREAD_SIZE)) - return 1; - else - return 0; - } - return p > t && p < t + THREAD_SIZE - size; -} - -/* The form of the top of the frame on the stack */ -struct stack_frame { - struct stack_frame *next_frame; - unsigned long return_address; -}; - -static inline unsigned long -print_context_stack(struct thread_info *tinfo, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data, - unsigned long *end) -{ - struct stack_frame *frame = (struct stack_frame *)bp; - - while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) { - unsigned long addr; - - addr = *stack; - if (__kernel_text_address(addr)) { - if ((unsigned long) stack == bp + 8) { - ops->address(data, addr, 1); - frame = frame->next_frame; - bp = (unsigned long) frame; - } else { - ops->address(data, addr, bp == 0); - } - } - stack++; - } - return bp; -} - -void dump_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, - const struct stacktrace_ops *ops, void *data) -{ - const unsigned cpu = get_cpu(); - unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; - unsigned used = 0; - struct thread_info *tinfo; - - if (!task) - task = current; - - if (!stack) { - unsigned long dummy; - stack = &dummy; - if (task && task != current) - stack = (unsigned long *)task->thread.sp; - } - -#ifdef CONFIG_FRAME_POINTER - if (!bp) { - if (task == current) { - /* Grab bp right from our regs */ - asm("movq %%rbp, %0" : "=r" (bp) : ); - } else { - /* bp is the last reg pushed by switch_to */ - bp = *(unsigned long *) task->thread.sp; - } - } -#endif - - /* - * Print function call entries in all stacks, starting at the - * current stack address. If the stacks consist of nested - * exceptions - */ - tinfo = task_thread_info(task); - for (;;) { - char *id; - unsigned long *estack_end; - estack_end = in_exception_stack(cpu, (unsigned long)stack, - &used, &id); - - if (estack_end) { - if (ops->stack(data, id) < 0) - break; - - bp = print_context_stack(tinfo, stack, bp, ops, - data, estack_end); - ops->stack(data, "<EOE>"); - /* - * We link to the next stack via the - * second-to-last pointer (index -2 to end) in the - * exception stack: - */ - stack = (unsigned long *) estack_end[-2]; - continue; - } - if (irqstack_end) { - unsigned long *irqstack; - irqstack = irqstack_end - - (IRQSTACKSIZE - 64) / sizeof(*irqstack); - - if (stack >= irqstack && stack < irqstack_end) { - if (ops->stack(data, "IRQ") < 0) - break; - bp = print_context_stack(tinfo, stack, bp, - ops, data, irqstack_end); - /* - * We link to the next stack (which would be - * the process stack normally) the last - * pointer (index -1 to end) in the IRQ stack: - */ - stack = (unsigned long *) (irqstack_end[-1]); - irqstack_end = NULL; - ops->stack(data, "EOI"); - continue; - } - } - break; - } - - /* - * This handles the process stack: - */ - bp = print_context_stack(tinfo, stack, bp, ops, data, NULL); - put_cpu(); -} -EXPORT_SYMBOL(dump_trace); - -static void -print_trace_warning_symbol(void *data, char *msg, unsigned long symbol) -{ - print_symbol(msg, symbol); - printk("\n"); -} - -static void print_trace_warning(void *data, char *msg) -{ - printk("%s\n", msg); -} - -static int print_trace_stack(void *data, char *name) -{ - printk(" <%s> ", name); - return 0; -} - -static void print_trace_address(void *data, unsigned long addr, int reliable) -{ - touch_nmi_watchdog(); - printk_address(addr, reliable); -} - -static const struct stacktrace_ops print_trace_ops = { - .warning = print_trace_warning, - .warning_symbol = print_trace_warning_symbol, - .stack = print_trace_stack, - .address = print_trace_address, -}; - -static void -show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp, char *log_lvl) -{ - printk("Call Trace:\n"); - dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl); -} - -void show_trace(struct task_struct *task, struct pt_regs *regs, - unsigned long *stack, unsigned long bp) -{ - show_trace_log_lvl(task, regs, stack, bp, ""); -} - -static void -show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, - unsigned long *sp, unsigned long bp, char *log_lvl) -{ - unsigned long *stack; - int i; - const int cpu = smp_processor_id(); - unsigned long *irqstack_end = - (unsigned long *) (cpu_pda(cpu)->irqstackptr); - unsigned long *irqstack = - (unsigned long *) (cpu_pda(cpu)->irqstackptr - IRQSTACKSIZE); - - /* - * debugging aid: "show_stack(NULL, NULL);" prints the - * back trace for this cpu. - */ - - if (sp == NULL) { - if (task) - sp = (unsigned long *)task->thread.sp; - else - sp = (unsigned long *)&sp; - } - - stack = sp; - for (i = 0; i < kstack_depth_to_print; i++) { - if (stack >= irqstack && stack <= irqstack_end) { - if (stack == irqstack_end) { - stack = (unsigned long *) (irqstack_end[-1]); - printk(" <EOI> "); - } - } else { - if (((long) stack & (THREAD_SIZE-1)) == 0) - break; - } - if (i && ((i % 4) == 0)) - printk("\n"); - printk(" %016lx", *stack++); - touch_nmi_watchdog(); - } - printk("\n"); - show_trace_log_lvl(task, regs, sp, bp, log_lvl); -} - -void show_stack(struct task_struct *task, unsigned long *sp) -{ - show_stack_log_lvl(task, NULL, sp, 0, ""); -} - -/* - * The architecture-independent dump_stack generator - */ -void dump_stack(void) -{ - unsigned long bp = 0; - unsigned long stack; - -#ifdef CONFIG_FRAME_POINTER - if (!bp) - asm("movq %%rbp, %0" : "=r" (bp) : ); -#endif - - printk("Pid: %d, comm: %.20s %s %s %.*s\n", - current->pid, current->comm, print_tainted(), - init_utsname()->release, - (int)strcspn(init_utsname()->version, " "), - init_utsname()->version); - show_trace(NULL, NULL, &stack, bp); -} -EXPORT_SYMBOL(dump_stack); - -void show_registers(struct pt_regs *regs) -{ - int i; - unsigned long sp; - const int cpu = smp_processor_id(); - struct task_struct *cur = cpu_pda(cpu)->pcurrent; - - sp = regs->sp; - printk("CPU %d ", cpu); - __show_regs(regs); - printk("Process %s (pid: %d, threadinfo %p, task %p)\n", - cur->comm, cur->pid, task_thread_info(cur), cur); - - /* - * When in-kernel, we also print out the stack and code at the - * time of the fault.. - */ - if (!user_mode(regs)) { - unsigned int code_prologue = code_bytes * 43 / 64; - unsigned int code_len = code_bytes; - unsigned char c; - u8 *ip; - - printk("Stack: "); - show_stack_log_lvl(NULL, regs, (unsigned long *)sp, - regs->bp, ""); - - printk(KERN_EMERG "Code: "); - - ip = (u8 *)regs->ip - code_prologue; - if (ip < (u8 *)PAGE_OFFSET || probe_kernel_address(ip, c)) { - /* try starting at RIP */ - ip = (u8 *)regs->ip; - code_len = code_len - code_prologue + 1; - } - for (i = 0; i < code_len; i++, ip++) { - if (ip < (u8 *)PAGE_OFFSET || - probe_kernel_address(ip, c)) { - printk(" Bad RIP value."); - break; - } - if (ip == (u8 *)regs->ip) - printk("<%02x> ", c); - else - printk("%02x ", c); - } - } - printk("\n"); -} - -int is_valid_bugaddr(unsigned long ip) -{ - unsigned short ud2; - - if (__copy_from_user(&ud2, (const void __user *) ip, sizeof(ud2))) - return 0; - - return ud2 == 0x0b0f; -} - -static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED; -static int die_owner = -1; -static unsigned int die_nest_count; - -unsigned __kprobes long oops_begin(void) -{ - int cpu; - unsigned long flags; - - oops_enter(); - - /* racy, but better than risking deadlock. */ - raw_local_irq_save(flags); - cpu = smp_processor_id(); - if (!__raw_spin_trylock(&die_lock)) { - if (cpu == die_owner) - /* nested oops. should stop eventually */; - else - __raw_spin_lock(&die_lock); - } - die_nest_count++; - die_owner = cpu; - console_verbose(); - bust_spinlocks(1); - return flags; -} - -void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr) -{ - die_owner = -1; - bust_spinlocks(0); - die_nest_count--; - if (!die_nest_count) - /* Nest count reaches zero, release the lock. */ - __raw_spin_unlock(&die_lock); - raw_local_irq_restore(flags); - if (!regs) { - oops_exit(); - return; - } - if (panic_on_oops) - panic("Fatal exception"); - oops_exit(); - do_exit(signr); -} - -int __kprobes __die(const char *str, struct pt_regs *regs, long err) -{ - printk(KERN_EMERG "%s: %04lx [%u] ", str, err & 0xffff, ++die_counter); -#ifdef CONFIG_PREEMPT - printk("PREEMPT "); -#endif -#ifdef CONFIG_SMP - printk("SMP "); -#endif -#ifdef CONFIG_DEBUG_PAGEALLOC - printk("DEBUG_PAGEALLOC"); -#endif - printk("\n"); - if (notify_die(DIE_OOPS, str, regs, err, - current->thread.trap_no, SIGSEGV) == NOTIFY_STOP) - return 1; - - show_registers(regs); - add_taint(TAINT_DIE); - /* Executive summary in case the oops scrolled away */ - printk(KERN_ALERT "RIP "); - printk_address(regs->ip, 1); - printk(" RSP <%016lx>\n", regs->sp); - if (kexec_should_crash(current)) - crash_kexec(regs); - return 0; -} - -void die(const char *str, struct pt_regs *regs, long err) -{ - unsigned long flags = oops_begin(); - - if (!user_mode(regs)) - report_bug(regs->ip, regs); - - if (__die(str, regs, err)) - regs = NULL; - oops_end(flags, regs, SIGSEGV); -} - -notrace __kprobes void -die_nmi(char *str, struct pt_regs *regs, int do_panic) -{ - unsigned long flags; - - if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP) - return; - - flags = oops_begin(); - /* - * We are in trouble anyway, lets at least try - * to get a message out. - */ - printk(KERN_EMERG "%s", str); - printk(" on CPU%d, ip %08lx, registers:\n", - smp_processor_id(), regs->ip); - show_registers(regs); - if (kexec_should_crash(current)) - crash_kexec(regs); - if (do_panic || panic_on_oops) - panic("Non maskable interrupt"); - oops_end(flags, NULL, SIGBUS); - nmi_exit(); - local_irq_enable(); - do_exit(SIGBUS); -} - -static void __kprobes -do_trap(int trapnr, int signr, char *str, struct pt_regs *regs, - long error_code, siginfo_t *info) -{ - struct task_struct *tsk = current; - - if (!user_mode(regs)) - goto kernel_trap; - - /* - * We want error_code and trap_no set for userspace faults and - * kernelspace faults which result in die(), but not - * kernelspace faults which are fixed up. die() gives the - * process no chance to handle the signal and notice the - * kernel fault information, so that won't result in polluting - * the information about previously queued, but not yet - * delivered, faults. See also do_general_protection below. - */ - tsk->thread.error_code = error_code; - tsk->thread.trap_no = trapnr; - - if (show_unhandled_signals && unhandled_signal(tsk, signr) && - printk_ratelimit()) { - printk(KERN_INFO - "%s[%d] trap %s ip:%lx sp:%lx error:%lx", - tsk->comm, tsk->pid, str, - regs->ip, regs->sp, error_code); - print_vma_addr(" in ", regs->ip); - printk("\n"); - } - - if (info) - force_sig_info(signr, info, tsk); - else - force_sig(signr, tsk); - return; - -kernel_trap: - if (!fixup_exception(regs)) { - tsk->thread.error_code = error_code; - tsk->thread.trap_no = trapnr; - die(str, regs, error_code); - } - return; -} - -#define DO_ERROR(trapnr, signr, str, name) \ -asmlinkage void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - conditional_sti(regs); \ - do_trap(trapnr, signr, str, regs, error_code, NULL); \ -} - -#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ -asmlinkage void do_##name(struct pt_regs *regs, long error_code) \ -{ \ - siginfo_t info; \ - info.si_signo = signr; \ - info.si_errno = 0; \ - info.si_code = sicode; \ - info.si_addr = (void __user *)siaddr; \ - trace_hardirqs_fixup(); \ - if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \ - == NOTIFY_STOP) \ - return; \ - conditional_sti(regs); \ - do_trap(trapnr, signr, str, regs, error_code, &info); \ -} - -DO_ERROR_INFO(0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip) -DO_ERROR(4, SIGSEGV, "overflow", overflow) -DO_ERROR(5, SIGSEGV, "bounds", bounds) -DO_ERROR_INFO(6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip) -DO_ERROR(9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) -DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) -DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) -DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) - -/* Runs on IST stack */ -asmlinkage void do_stack_segment(struct pt_regs *regs, long error_code) -{ - if (notify_die(DIE_TRAP, "stack segment", regs, error_code, - 12, SIGBUS) == NOTIFY_STOP) - return; - preempt_conditional_sti(regs); - do_trap(12, SIGBUS, "stack segment", regs, error_code, NULL); - preempt_conditional_cli(regs); -} - -asmlinkage void do_double_fault(struct pt_regs *regs, long error_code) -{ - static const char str[] = "double fault"; - struct task_struct *tsk = current; - - /* Return not checked because double check cannot be ignored */ - notify_die(DIE_TRAP, str, regs, error_code, 8, SIGSEGV); - - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 8; - - /* This is always a kernel trap and never fixable (and thus must - never return). */ - for (;;) - die(str, regs, error_code); -} - -asmlinkage void __kprobes -do_general_protection(struct pt_regs *regs, long error_code) -{ - struct task_struct *tsk; - - conditional_sti(regs); - - tsk = current; - if (!user_mode(regs)) - goto gp_in_kernel; - - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 13; - - if (show_unhandled_signals && unhandled_signal(tsk, SIGSEGV) && - printk_ratelimit()) { - printk(KERN_INFO - "%s[%d] general protection ip:%lx sp:%lx error:%lx", - tsk->comm, tsk->pid, - regs->ip, regs->sp, error_code); - print_vma_addr(" in ", regs->ip); - printk("\n"); - } - - force_sig(SIGSEGV, tsk); - return; - -gp_in_kernel: - if (fixup_exception(regs)) - return; - - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 13; - if (notify_die(DIE_GPF, "general protection fault", regs, - error_code, 13, SIGSEGV) == NOTIFY_STOP) - return; - die("general protection fault", regs, error_code); -} - -static notrace __kprobes void -mem_parity_error(unsigned char reason, struct pt_regs *regs) -{ - printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", - reason); - printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); - -#if defined(CONFIG_EDAC) - if (edac_handler_set()) { - edac_atomic_assert_error(); - return; - } -#endif - - if (panic_on_unrecovered_nmi) - panic("NMI: Not continuing"); - - printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); - - /* Clear and disable the memory parity error line. */ - reason = (reason & 0xf) | 4; - outb(reason, 0x61); -} - -static notrace __kprobes void -io_check_error(unsigned char reason, struct pt_regs *regs) -{ - printk("NMI: IOCK error (debug interrupt?)\n"); - show_registers(regs); - - /* Re-enable the IOCK line, wait for a few seconds */ - reason = (reason & 0xf) | 8; - outb(reason, 0x61); - mdelay(2000); - reason &= ~8; - outb(reason, 0x61); -} - -static notrace __kprobes void -unknown_nmi_error(unsigned char reason, struct pt_regs *regs) -{ - if (notify_die(DIE_NMIUNKNOWN, "nmi", regs, reason, 2, SIGINT) == - NOTIFY_STOP) - return; - printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", - reason); - printk(KERN_EMERG "Do you have a strange power saving mode enabled?\n"); - - if (panic_on_unrecovered_nmi) - panic("NMI: Not continuing"); - - printk(KERN_EMERG "Dazed and confused, but trying to continue\n"); -} - -/* Runs on IST stack. This code must keep interrupts off all the time. - Nested NMIs are prevented by the CPU. */ -asmlinkage notrace __kprobes void default_do_nmi(struct pt_regs *regs) -{ - unsigned char reason = 0; - int cpu; - - cpu = smp_processor_id(); - - /* Only the BSP gets external NMIs from the system. */ - if (!cpu) - reason = get_nmi_reason(); - - if (!(reason & 0xc0)) { - if (notify_die(DIE_NMI_IPI, "nmi_ipi", regs, reason, 2, SIGINT) - == NOTIFY_STOP) - return; - /* - * Ok, so this is none of the documented NMI sources, - * so it must be the NMI watchdog. - */ - if (nmi_watchdog_tick(regs, reason)) - return; - if (!do_nmi_callback(regs, cpu)) - unknown_nmi_error(reason, regs); - - return; - } - if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT) == NOTIFY_STOP) - return; - - /* AK: following checks seem to be broken on modern chipsets. FIXME */ - if (reason & 0x80) - mem_parity_error(reason, regs); - if (reason & 0x40) - io_check_error(reason, regs); -} - -asmlinkage notrace __kprobes void -do_nmi(struct pt_regs *regs, long error_code) -{ - nmi_enter(); - - add_pda(__nmi_count, 1); - - if (!ignore_nmis) - default_do_nmi(regs); - - nmi_exit(); -} - -void stop_nmi(void) -{ - acpi_nmi_disable(); - ignore_nmis++; -} - -void restart_nmi(void) -{ - ignore_nmis--; - acpi_nmi_enable(); -} - -/* runs on IST stack. */ -asmlinkage void __kprobes do_int3(struct pt_regs *regs, long error_code) -{ - trace_hardirqs_fixup(); - - if (notify_die(DIE_INT3, "int3", regs, error_code, 3, SIGTRAP) - == NOTIFY_STOP) - return; - - preempt_conditional_sti(regs); - do_trap(3, SIGTRAP, "int3", regs, error_code, NULL); - preempt_conditional_cli(regs); -} - -/* Help handler running on IST stack to switch back to user stack - for scheduling or signal handling. The actual stack switch is done in - entry.S */ -asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs) -{ - struct pt_regs *regs = eregs; - /* Did already sync */ - if (eregs == (struct pt_regs *)eregs->sp) - ; - /* Exception from user space */ - else if (user_mode(eregs)) - regs = task_pt_regs(current); - /* Exception from kernel and interrupts are enabled. Move to - kernel process stack. */ - else if (eregs->flags & X86_EFLAGS_IF) - regs = (struct pt_regs *)(eregs->sp -= sizeof(struct pt_regs)); - if (eregs != regs) - *regs = *eregs; - return regs; -} - -/* runs on IST stack. */ -asmlinkage void __kprobes do_debug(struct pt_regs *regs, - unsigned long error_code) -{ - struct task_struct *tsk = current; - unsigned long condition; - siginfo_t info; - - trace_hardirqs_fixup(); - - get_debugreg(condition, 6); - - /* - * The processor cleared BTF, so don't mark that we need it set. - */ - clear_tsk_thread_flag(tsk, TIF_DEBUGCTLMSR); - tsk->thread.debugctlmsr = 0; - - if (notify_die(DIE_DEBUG, "debug", regs, condition, error_code, - SIGTRAP) == NOTIFY_STOP) - return; - - preempt_conditional_sti(regs); - - /* Mask out spurious debug traps due to lazy DR7 setting */ - if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) { - if (!tsk->thread.debugreg7) - goto clear_dr7; - } - - tsk->thread.debugreg6 = condition; - - /* - * Single-stepping through TF: make sure we ignore any events in - * kernel space (but re-enable TF when returning to user mode). - */ - if (condition & DR_STEP) { - if (!user_mode(regs)) - goto clear_TF_reenable; - } - - /* Ok, finally something we can handle */ - tsk->thread.trap_no = 1; - tsk->thread.error_code = error_code; - info.si_signo = SIGTRAP; - info.si_errno = 0; - info.si_code = TRAP_BRKPT; - info.si_addr = user_mode(regs) ? (void __user *)regs->ip : NULL; - force_sig_info(SIGTRAP, &info, tsk); - -clear_dr7: - set_debugreg(0, 7); - preempt_conditional_cli(regs); - return; - -clear_TF_reenable: - set_tsk_thread_flag(tsk, TIF_SINGLESTEP); - regs->flags &= ~X86_EFLAGS_TF; - preempt_conditional_cli(regs); - return; -} - -static int kernel_math_error(struct pt_regs *regs, const char *str, int trapnr) -{ - if (fixup_exception(regs)) - return 1; - - notify_die(DIE_GPF, str, regs, 0, trapnr, SIGFPE); - /* Illegal floating point operation in the kernel */ - current->thread.trap_no = trapnr; - die(str, regs, 0); - return 0; -} - -/* - * Note that we play around with the 'TS' bit in an attempt to get - * the correct behaviour even in the presence of the asynchronous - * IRQ13 behaviour - */ -asmlinkage void do_coprocessor_error(struct pt_regs *regs) -{ - void __user *ip = (void __user *)(regs->ip); - struct task_struct *task; - siginfo_t info; - unsigned short cwd, swd; - - conditional_sti(regs); - if (!user_mode(regs) && - kernel_math_error(regs, "kernel x87 math error", 16)) - return; - - /* - * Save the info for the exception handler and clear the error. - */ - task = current; - save_init_fpu(task); - task->thread.trap_no = 16; - task->thread.error_code = 0; - info.si_signo = SIGFPE; - info.si_errno = 0; - info.si_code = __SI_FAULT; - info.si_addr = ip; - /* - * (~cwd & swd) will mask out exceptions that are not set to unmasked - * status. 0x3f is the exception bits in these regs, 0x200 is the - * C1 reg you need in case of a stack fault, 0x040 is the stack - * fault bit. We should only be taking one exception at a time, - * so if this combination doesn't produce any single exception, - * then we have a bad program that isn't synchronizing its FPU usage - * and it will suffer the consequences since we won't be able to - * fully reproduce the context of the exception - */ - cwd = get_fpu_cwd(task); - swd = get_fpu_swd(task); - switch (swd & ~cwd & 0x3f) { - case 0x000: /* No unmasked exception */ - default: /* Multiple exceptions */ - break; - case 0x001: /* Invalid Op */ - /* - * swd & 0x240 == 0x040: Stack Underflow - * swd & 0x240 == 0x240: Stack Overflow - * User must clear the SF bit (0x40) if set - */ - info.si_code = FPE_FLTINV; - break; - case 0x002: /* Denormalize */ - case 0x010: /* Underflow */ - info.si_code = FPE_FLTUND; - break; - case 0x004: /* Zero Divide */ - info.si_code = FPE_FLTDIV; - break; - case 0x008: /* Overflow */ - info.si_code = FPE_FLTOVF; - break; - case 0x020: /* Precision */ - info.si_code = FPE_FLTRES; - break; - } - force_sig_info(SIGFPE, &info, task); -} - -asmlinkage void bad_intr(void) -{ - printk("bad interrupt"); -} - -asmlinkage void do_simd_coprocessor_error(struct pt_regs *regs) -{ - void __user *ip = (void __user *)(regs->ip); - struct task_struct *task; - siginfo_t info; - unsigned short mxcsr; - - conditional_sti(regs); - if (!user_mode(regs) && - kernel_math_error(regs, "kernel simd math error", 19)) - return; - - /* - * Save the info for the exception handler and clear the error. - */ - task = current; - save_init_fpu(task); - task->thread.trap_no = 19; - task->thread.error_code = 0; - info.si_signo = SIGFPE; - info.si_errno = 0; - info.si_code = __SI_FAULT; - info.si_addr = ip; - /* - * The SIMD FPU exceptions are handled a little differently, as there - * is only a single status/control register. Thus, to determine which - * unmasked exception was caught we must mask the exception mask bits - * at 0x1f80, and then use these to mask the exception bits at 0x3f. - */ - mxcsr = get_fpu_mxcsr(task); - switch (~((mxcsr & 0x1f80) >> 7) & (mxcsr & 0x3f)) { - case 0x000: - default: - break; - case 0x001: /* Invalid Op */ - info.si_code = FPE_FLTINV; - break; - case 0x002: /* Denormalize */ - case 0x010: /* Underflow */ - info.si_code = FPE_FLTUND; - break; - case 0x004: /* Zero Divide */ - info.si_code = FPE_FLTDIV; - break; - case 0x008: /* Overflow */ - info.si_code = FPE_FLTOVF; - break; - case 0x020: /* Precision */ - info.si_code = FPE_FLTRES; - break; - } - force_sig_info(SIGFPE, &info, task); -} - -asmlinkage void do_spurious_interrupt_bug(struct pt_regs *regs) -{ -} - -asmlinkage void __attribute__((weak)) smp_thermal_interrupt(void) -{ -} - -asmlinkage void __attribute__((weak)) mce_threshold_interrupt(void) -{ -} - -/* - * 'math_state_restore()' saves the current math information in the - * old math state array, and gets the new ones from the current task - * - * Careful.. There are problems with IBM-designed IRQ13 behaviour. - * Don't touch unless you *really* know how it works. - */ -asmlinkage void math_state_restore(void) -{ - struct task_struct *me = current; - - if (!used_math()) { - local_irq_enable(); - /* - * does a slab alloc which can sleep - */ - if (init_fpu(me)) { - /* - * ran out of memory! - */ - do_group_exit(SIGKILL); - return; - } - local_irq_disable(); - } - - clts(); /* Allow maths ops (or we recurse) */ - /* - * Paranoid restore. send a SIGSEGV if we fail to restore the state. - */ - if (unlikely(restore_fpu_checking(me))) { - stts(); - force_sig(SIGSEGV, me); - return; - } - task_thread_info(me)->status |= TS_USEDFPU; - me->fpu_counter++; -} -EXPORT_SYMBOL_GPL(math_state_restore); - -void __init trap_init(void) -{ - set_intr_gate(0, ÷_error); - set_intr_gate_ist(1, &debug, DEBUG_STACK); - set_intr_gate_ist(2, &nmi, NMI_STACK); - /* int3 can be called from all */ - set_system_gate_ist(3, &int3, DEBUG_STACK); - /* int4 can be called from all */ - set_system_gate(4, &overflow); - set_intr_gate(5, &bounds); - set_intr_gate(6, &invalid_op); - set_intr_gate(7, &device_not_available); - set_intr_gate_ist(8, &double_fault, DOUBLEFAULT_STACK); - set_intr_gate(9, &coprocessor_segment_overrun); - set_intr_gate(10, &invalid_TSS); - set_intr_gate(11, &segment_not_present); - set_intr_gate_ist(12, &stack_segment, STACKFAULT_STACK); - set_intr_gate(13, &general_protection); - set_intr_gate(14, &page_fault); - set_intr_gate(15, &spurious_interrupt_bug); - set_intr_gate(16, &coprocessor_error); - set_intr_gate(17, &alignment_check); -#ifdef CONFIG_X86_MCE - set_intr_gate_ist(18, &machine_check, MCE_STACK); -#endif - set_intr_gate(19, &simd_coprocessor_error); - -#ifdef CONFIG_IA32_EMULATION - set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall); -#endif - /* - * Should be a barrier for any external CPU state: - */ - cpu_init(); -} - -static int __init oops_setup(char *s) -{ - if (!s) - return -EINVAL; - if (!strcmp(s, "panic")) - panic_on_oops = 1; - return 0; -} -early_param("oops", oops_setup); - -static int __init kstack_setup(char *s) -{ - if (!s) - return -EINVAL; - kstack_depth_to_print = simple_strtoul(s, NULL, 0); - return 0; -} -early_param("kstack", kstack_setup); - -static int __init code_bytes_setup(char *s) -{ - code_bytes = simple_strtoul(s, NULL, 0); - if (code_bytes > 8192) - code_bytes = 8192; - - return 1; -} -__setup("code_bytes=", code_bytes_setup); diff --git a/arch/x86/kernel/vmlinux_64.lds.S b/arch/x86/kernel/vmlinux_64.lds.S index 201e81a91a9..46e05447405 100644 --- a/arch/x86/kernel/vmlinux_64.lds.S +++ b/arch/x86/kernel/vmlinux_64.lds.S @@ -172,8 +172,8 @@ SECTIONS .x86_cpu_dev.init : AT(ADDR(.x86_cpu_dev.init) - LOAD_OFFSET) { *(.x86_cpu_dev.init) } - SECURITY_INIT __x86_cpu_dev_end = .; + SECURITY_INIT . = ALIGN(8); .parainstructions : AT(ADDR(.parainstructions) - LOAD_OFFSET) { diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index 07713d64deb..9abac8a9d82 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c @@ -95,7 +95,9 @@ int save_i387_xstate(void __user *buf) * Start with clearing the user buffer. This will present a * clean context for the bytes not touched by the fxsave/xsave. */ - __clear_user(buf, sig_xstate_size); + err = __clear_user(buf, sig_xstate_size); + if (err) + return err; if (task_thread_info(tsk)->status & TS_XSAVE) err = xsave_user(buf); @@ -114,6 +116,8 @@ int save_i387_xstate(void __user *buf) if (task_thread_info(tsk)->status & TS_XSAVE) { struct _fpstate __user *fx = buf; + struct _xstate __user *x = buf; + u64 xstate_bv; err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved, sizeof(struct _fpx_sw_bytes)); @@ -121,6 +125,31 @@ int save_i387_xstate(void __user *buf) err |= __put_user(FP_XSTATE_MAGIC2, (__u32 __user *) (buf + sig_xstate_size - FP_XSTATE_MAGIC2_SIZE)); + + /* + * Read the xstate_bv which we copied (directly from the cpu or + * from the state in task struct) to the user buffers and + * set the FP/SSE bits. + */ + err |= __get_user(xstate_bv, &x->xstate_hdr.xstate_bv); + + /* + * For legacy compatible, we always set FP/SSE bits in the bit + * vector while saving the state to the user context. This will + * enable us capturing any changes(during sigreturn) to + * the FP/SSE bits by the legacy applications which don't touch + * xstate_bv in the xsave header. + * + * xsave aware apps can change the xstate_bv in the xsave + * header as well as change any contents in the memory layout. + * xrestore as part of sigreturn will capture all the changes. + */ + xstate_bv |= XSTATE_FPSSE; + + err |= __put_user(xstate_bv, &x->xstate_hdr.xstate_bv); + + if (err) + return err; } return 1; @@ -272,7 +301,7 @@ void __cpuinit xsave_init(void) /* * setup the xstate image representing the init state */ -void setup_xstate_init(void) +static void __init setup_xstate_init(void) { init_xstate_buf = alloc_bootmem(xstate_size); init_xstate_buf->i387.mxcsr = MXCSR_DEFAULT; diff --git a/arch/x86/mach-generic/es7000.c b/arch/x86/mach-generic/es7000.c index 520cca0ee04..6513d41ea21 100644 --- a/arch/x86/mach-generic/es7000.c +++ b/arch/x86/mach-generic/es7000.c @@ -47,16 +47,26 @@ static __init int mps_oem_check(struct mp_config_table *mpc, char *oem, /* Hook from generic ACPI tables.c */ static int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) { - unsigned long oem_addr; + unsigned long oem_addr = 0; + int check_dsdt; + int ret = 0; + + /* check dsdt at first to avoid clear fix_map for oem_addr */ + check_dsdt = es7000_check_dsdt(); + if (!find_unisys_acpi_oem_table(&oem_addr)) { - if (es7000_check_dsdt()) - return parse_unisys_oem((char *)oem_addr); + if (check_dsdt) + ret = parse_unisys_oem((char *)oem_addr); else { setup_unisys(); - return 1; + ret = 1; } + /* + * we need to unmap it + */ + unmap_unisys_acpi_oem_table(oem_addr); } - return 0; + return ret; } #else static int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id) diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index dfb932dcf13..59f89b434b4 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -13,12 +13,8 @@ obj-$(CONFIG_MMIOTRACE) += mmiotrace.o mmiotrace-y := pf_in.o mmio-mod.o obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o -ifeq ($(CONFIG_X86_32),y) -obj-$(CONFIG_NUMA) += discontig_32.o -else -obj-$(CONFIG_NUMA) += numa_64.o +obj-$(CONFIG_NUMA) += numa_$(BITS).o obj-$(CONFIG_K8_NUMA) += k8topology_64.o -endif obj-$(CONFIG_ACPI_NUMA) += srat_$(BITS).o obj-$(CONFIG_MEMTEST) += memtest.o diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 8f92cac4e6d..3f2b8962cbd 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -592,11 +592,6 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) unsigned long flags; #endif - /* - * We can fault from pretty much anywhere, with unknown IRQ state. - */ - trace_hardirqs_fixup(); - tsk = current; mm = tsk->mm; prefetchw(&mm->mmap_sem); @@ -914,15 +909,15 @@ LIST_HEAD(pgd_list); void vmalloc_sync_all(void) { -#ifdef CONFIG_X86_32 - unsigned long start = VMALLOC_START & PGDIR_MASK; unsigned long address; +#ifdef CONFIG_X86_32 if (SHARED_KERNEL_PMD) return; - BUILD_BUG_ON(TASK_SIZE & ~PGDIR_MASK); - for (address = start; address >= TASK_SIZE; address += PGDIR_SIZE) { + for (address = VMALLOC_START & PMD_MASK; + address >= TASK_SIZE && address < FIXADDR_TOP; + address += PMD_SIZE) { unsigned long flags; struct page *page; @@ -935,10 +930,8 @@ void vmalloc_sync_all(void) spin_unlock_irqrestore(&pgd_lock, flags); } #else /* CONFIG_X86_64 */ - unsigned long start = VMALLOC_START & PGDIR_MASK; - unsigned long address; - - for (address = start; address <= VMALLOC_END; address += PGDIR_SIZE) { + for (address = VMALLOC_START & PGDIR_MASK; address <= VMALLOC_END; + address += PGDIR_SIZE) { const pgd_t *pgd_ref = pgd_offset_k(address); unsigned long flags; struct page *page; diff --git a/arch/x86/mm/gup.c b/arch/x86/mm/gup.c index 007bb06c750..4ba373c5b8c 100644 --- a/arch/x86/mm/gup.c +++ b/arch/x86/mm/gup.c @@ -82,7 +82,7 @@ static noinline int gup_pte_range(pmd_t pmd, unsigned long addr, pte_t pte = gup_get_pte(ptep); struct page *page; - if ((pte_val(pte) & (mask | _PAGE_SPECIAL)) != mask) { + if ((pte_flags(pte) & (mask | _PAGE_SPECIAL)) != mask) { pte_unmap(ptep); return 0; } @@ -116,10 +116,10 @@ static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr, mask = _PAGE_PRESENT|_PAGE_USER; if (write) mask |= _PAGE_RW; - if ((pte_val(pte) & mask) != mask) + if ((pte_flags(pte) & mask) != mask) return 0; /* hugepages are never "special" */ - VM_BUG_ON(pte_val(pte) & _PAGE_SPECIAL); + VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL); VM_BUG_ON(!pfn_valid(pte_pfn(pte))); refs = 0; @@ -173,10 +173,10 @@ static noinline int gup_huge_pud(pud_t pud, unsigned long addr, mask = _PAGE_PRESENT|_PAGE_USER; if (write) mask |= _PAGE_RW; - if ((pte_val(pte) & mask) != mask) + if ((pte_flags(pte) & mask) != mask) return 0; /* hugepages are never "special" */ - VM_BUG_ON(pte_val(pte) & _PAGE_SPECIAL); + VM_BUG_ON(pte_flags(pte) & _PAGE_SPECIAL); VM_BUG_ON(!pfn_valid(pte_pfn(pte))); refs = 0; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index c3789bb1930..8396868e82c 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -31,6 +31,7 @@ #include <linux/cpumask.h> #include <asm/asm.h> +#include <asm/bios_ebda.h> #include <asm/processor.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -557,7 +558,7 @@ void zap_low_mappings(void) int nx_enabled; -pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL); +pteval_t __supported_pte_mask __read_mostly = ~(_PAGE_NX | _PAGE_GLOBAL | _PAGE_IOMAP); EXPORT_SYMBOL_GPL(__supported_pte_mask); #ifdef CONFIG_X86_PAE @@ -969,6 +970,8 @@ void __init mem_init(void) int codesize, reservedpages, datasize, initsize; int tmp; + start_periodic_check_for_corruption(); + #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); #endif diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 83e13f2d53d..b8e461d4941 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -31,6 +31,7 @@ #include <linux/nmi.h> #include <asm/processor.h> +#include <asm/bios_ebda.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/pgtable.h> @@ -88,7 +89,7 @@ early_param("gbpages", parse_direct_gbpages_on); int after_bootmem; -unsigned long __supported_pte_mask __read_mostly = ~0UL; +pteval_t __supported_pte_mask __read_mostly = ~_PAGE_IOMAP; EXPORT_SYMBOL_GPL(__supported_pte_mask); static int do_not_nx __cpuinitdata; @@ -195,9 +196,6 @@ set_pte_vaddr_pud(pud_t *pud_page, unsigned long vaddr, pte_t new_pte) } pte = pte_offset_kernel(pmd, vaddr); - if (!pte_none(*pte) && pte_val(new_pte) && - pte_val(*pte) != (pte_val(new_pte) & __supported_pte_mask)) - pte_ERROR(*pte); set_pte(pte, new_pte); /* @@ -312,7 +310,7 @@ static __ref void *alloc_low_page(unsigned long *phys) if (pfn >= table_top) panic("alloc_low_page: ran out of memory"); - adr = early_ioremap(pfn * PAGE_SIZE, PAGE_SIZE); + adr = early_memremap(pfn * PAGE_SIZE, PAGE_SIZE); memset(adr, 0, PAGE_SIZE); *phys = pfn * PAGE_SIZE; return adr; @@ -748,7 +746,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, old_start = mr[i].start; memmove(&mr[i], &mr[i+1], (nr_range - 1 - i) * sizeof (struct map_range)); - mr[i].start = old_start; + mr[i--].start = old_start; nr_range--; } @@ -881,6 +879,8 @@ void __init mem_init(void) { long codesize, reservedpages, datasize, initsize; + start_periodic_check_for_corruption(); + pci_iommu_alloc(); /* clear_bss() already clear the empty_zero_page */ diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c index 6ab3196d12b..e4c43ec71b2 100644 --- a/arch/x86/mm/ioremap.c +++ b/arch/x86/mm/ioremap.c @@ -24,18 +24,47 @@ #ifdef CONFIG_X86_64 +static inline int phys_addr_valid(unsigned long addr) +{ + return addr < (1UL << boot_cpu_data.x86_phys_bits); +} + unsigned long __phys_addr(unsigned long x) { - if (x >= __START_KERNEL_map) - return x - __START_KERNEL_map + phys_base; - return x - PAGE_OFFSET; + if (x >= __START_KERNEL_map) { + x -= __START_KERNEL_map; + VIRTUAL_BUG_ON(x >= KERNEL_IMAGE_SIZE); + x += phys_base; + } else { + VIRTUAL_BUG_ON(x < PAGE_OFFSET); + x -= PAGE_OFFSET; + VIRTUAL_BUG_ON(system_state == SYSTEM_BOOTING ? x > MAXMEM : + !phys_addr_valid(x)); + } + return x; } EXPORT_SYMBOL(__phys_addr); -static inline int phys_addr_valid(unsigned long addr) +bool __virt_addr_valid(unsigned long x) { - return addr < (1UL << boot_cpu_data.x86_phys_bits); + if (x >= __START_KERNEL_map) { + x -= __START_KERNEL_map; + if (x >= KERNEL_IMAGE_SIZE) + return false; + x += phys_base; + } else { + if (x < PAGE_OFFSET) + return false; + x -= PAGE_OFFSET; + if (system_state == SYSTEM_BOOTING ? + x > MAXMEM : !phys_addr_valid(x)) { + return false; + } + } + + return pfn_valid(x >> PAGE_SHIFT); } +EXPORT_SYMBOL(__virt_addr_valid); #else @@ -44,6 +73,28 @@ static inline int phys_addr_valid(unsigned long addr) return 1; } +#ifdef CONFIG_DEBUG_VIRTUAL +unsigned long __phys_addr(unsigned long x) +{ + /* VMALLOC_* aren't constants; not available at the boot time */ + VIRTUAL_BUG_ON(x < PAGE_OFFSET); + VIRTUAL_BUG_ON(system_state != SYSTEM_BOOTING && + is_vmalloc_addr((void *) x)); + return x - PAGE_OFFSET; +} +EXPORT_SYMBOL(__phys_addr); +#endif + +bool __virt_addr_valid(unsigned long x) +{ + if (x < PAGE_OFFSET) + return false; + if (system_state != SYSTEM_BOOTING && is_vmalloc_addr((void *) x)) + return false; + return pfn_valid((x - PAGE_OFFSET) >> PAGE_SHIFT); +} +EXPORT_SYMBOL(__virt_addr_valid); + #endif int page_is_ram(unsigned long pagenr) @@ -223,16 +274,16 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr, switch (prot_val) { case _PAGE_CACHE_UC: default: - prot = PAGE_KERNEL_NOCACHE; + prot = PAGE_KERNEL_IO_NOCACHE; break; case _PAGE_CACHE_UC_MINUS: - prot = PAGE_KERNEL_UC_MINUS; + prot = PAGE_KERNEL_IO_UC_MINUS; break; case _PAGE_CACHE_WC: - prot = PAGE_KERNEL_WC; + prot = PAGE_KERNEL_IO_WC; break; case _PAGE_CACHE_WB: - prot = PAGE_KERNEL; + prot = PAGE_KERNEL_IO; break; } @@ -549,12 +600,12 @@ static void __init __early_set_fixmap(enum fixed_addresses idx, } static inline void __init early_set_fixmap(enum fixed_addresses idx, - unsigned long phys) + unsigned long phys, pgprot_t prot) { if (after_paging_init) - set_fixmap(idx, phys); + __set_fixmap(idx, phys, prot); else - __early_set_fixmap(idx, phys, PAGE_KERNEL); + __early_set_fixmap(idx, phys, prot); } static inline void __init early_clear_fixmap(enum fixed_addresses idx) @@ -565,16 +616,22 @@ static inline void __init early_clear_fixmap(enum fixed_addresses idx) __early_set_fixmap(idx, 0, __pgprot(0)); } - -static int __initdata early_ioremap_nested; - +static void *prev_map[FIX_BTMAPS_SLOTS] __initdata; +static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata; static int __init check_early_ioremap_leak(void) { - if (!early_ioremap_nested) + int count = 0; + int i; + + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) + if (prev_map[i]) + count++; + + if (!count) return 0; WARN(1, KERN_WARNING "Debug warning: early ioremap leak of %d areas detected.\n", - early_ioremap_nested); + count); printk(KERN_WARNING "please boot with early_ioremap_debug and report the dmesg.\n"); @@ -582,18 +639,33 @@ static int __init check_early_ioremap_leak(void) } late_initcall(check_early_ioremap_leak); -void __init *early_ioremap(unsigned long phys_addr, unsigned long size) +static void __init *__early_ioremap(unsigned long phys_addr, unsigned long size, pgprot_t prot) { unsigned long offset, last_addr; - unsigned int nrpages, nesting; + unsigned int nrpages; enum fixed_addresses idx0, idx; + int i, slot; WARN_ON(system_state != SYSTEM_BOOTING); - nesting = early_ioremap_nested; + slot = -1; + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (!prev_map[i]) { + slot = i; + break; + } + } + + if (slot < 0) { + printk(KERN_INFO "early_iomap(%08lx, %08lx) not found slot\n", + phys_addr, size); + WARN_ON(1); + return NULL; + } + if (early_ioremap_debug) { printk(KERN_INFO "early_ioremap(%08lx, %08lx) [%d] => ", - phys_addr, size, nesting); + phys_addr, size, slot); dump_stack(); } @@ -604,17 +676,13 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size) return NULL; } - if (nesting >= FIX_BTMAPS_NESTING) { - WARN_ON(1); - return NULL; - } - early_ioremap_nested++; + prev_size[slot] = size; /* * Mappings have to be page-aligned */ offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; - size = PAGE_ALIGN(last_addr) - phys_addr; + size = PAGE_ALIGN(last_addr + 1) - phys_addr; /* * Mappings have to fit in the FIX_BTMAP area. @@ -628,10 +696,10 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size) /* * Ok, go for it.. */ - idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting; + idx0 = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; idx = idx0; while (nrpages > 0) { - early_set_fixmap(idx, phys_addr); + early_set_fixmap(idx, phys_addr, prot); phys_addr += PAGE_SIZE; --idx; --nrpages; @@ -639,7 +707,20 @@ void __init *early_ioremap(unsigned long phys_addr, unsigned long size) if (early_ioremap_debug) printk(KERN_CONT "%08lx + %08lx\n", offset, fix_to_virt(idx0)); - return (void *) (offset + fix_to_virt(idx0)); + prev_map[slot] = (void *) (offset + fix_to_virt(idx0)); + return prev_map[slot]; +} + +/* Remap an IO device */ +void __init *early_ioremap(unsigned long phys_addr, unsigned long size) +{ + return __early_ioremap(phys_addr, size, PAGE_KERNEL_IO); +} + +/* Remap memory */ +void __init *early_memremap(unsigned long phys_addr, unsigned long size) +{ + return __early_ioremap(phys_addr, size, PAGE_KERNEL); } void __init early_iounmap(void *addr, unsigned long size) @@ -648,15 +729,33 @@ void __init early_iounmap(void *addr, unsigned long size) unsigned long offset; unsigned int nrpages; enum fixed_addresses idx; - int nesting; + int i, slot; + + slot = -1; + for (i = 0; i < FIX_BTMAPS_SLOTS; i++) { + if (prev_map[i] == addr) { + slot = i; + break; + } + } - nesting = --early_ioremap_nested; - if (WARN_ON(nesting < 0)) + if (slot < 0) { + printk(KERN_INFO "early_iounmap(%p, %08lx) not found slot\n", + addr, size); + WARN_ON(1); return; + } + + if (prev_size[slot] != size) { + printk(KERN_INFO "early_iounmap(%p, %08lx) [%d] size not consistent %08lx\n", + addr, size, slot, prev_size[slot]); + WARN_ON(1); + return; + } if (early_ioremap_debug) { printk(KERN_INFO "early_iounmap(%p, %08lx) [%d]\n", addr, - size, nesting); + size, slot); dump_stack(); } @@ -668,12 +767,13 @@ void __init early_iounmap(void *addr, unsigned long size) offset = virt_addr & ~PAGE_MASK; nrpages = PAGE_ALIGN(offset + size - 1) >> PAGE_SHIFT; - idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*nesting; + idx = FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*slot; while (nrpages > 0) { early_clear_fixmap(idx); --idx; --nrpages; } + prev_map[slot] = 0; } void __this_fixmap_does_not_exist(void) diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/numa_32.c index 847c164725f..847c164725f 100644 --- a/arch/x86/mm/discontig_32.c +++ b/arch/x86/mm/numa_32.c diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c index 1b4763e26ea..51c0a2fc14f 100644 --- a/arch/x86/mm/srat_64.c +++ b/arch/x86/mm/srat_64.c @@ -138,7 +138,7 @@ acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa) return; } - if (is_uv_system()) + if (get_uv_system_type() >= UV_X2APIC) apic_id = (pa->apic_id << 8) | pa->local_sapic_eid; else apic_id = pa->apic_id; diff --git a/arch/x86/oprofile/Makefile b/arch/x86/oprofile/Makefile index 30f3eb36666..446902b2a6b 100644 --- a/arch/x86/oprofile/Makefile +++ b/arch/x86/oprofile/Makefile @@ -7,6 +7,6 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ timer_int.o ) oprofile-y := $(DRIVER_OBJS) init.o backtrace.o -oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_athlon.o \ +oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \ op_model_ppro.o op_model_p4.o oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index 8a5f1614a3d..57f6c908808 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -1,10 +1,11 @@ /** * @file nmi_int.c * - * @remark Copyright 2002 OProfile authors + * @remark Copyright 2002-2008 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> + * @author Robert Richter <robert.richter@amd.com> */ #include <linux/init.h> @@ -439,6 +440,7 @@ int __init op_nmi_init(struct oprofile_operations *ops) __u8 vendor = boot_cpu_data.x86_vendor; __u8 family = boot_cpu_data.x86; char *cpu_type; + int ret = 0; if (!cpu_has_apic) return -ENODEV; @@ -451,19 +453,23 @@ int __init op_nmi_init(struct oprofile_operations *ops) default: return -ENODEV; case 6: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "i386/athlon"; break; case 0xf: - model = &op_athlon_spec; + model = &op_amd_spec; /* Actually it could be i386/hammer too, but give user space an consistent name. */ cpu_type = "x86-64/hammer"; break; case 0x10: - model = &op_athlon_spec; + model = &op_amd_spec; cpu_type = "x86-64/family10"; break; + case 0x11: + model = &op_amd_spec; + cpu_type = "x86-64/family11h"; + break; } break; @@ -490,17 +496,24 @@ int __init op_nmi_init(struct oprofile_operations *ops) return -ENODEV; } - init_sysfs(); #ifdef CONFIG_SMP register_cpu_notifier(&oprofile_cpu_nb); #endif - using_nmi = 1; + /* default values, can be overwritten by model */ ops->create_files = nmi_create_files; ops->setup = nmi_setup; ops->shutdown = nmi_shutdown; ops->start = nmi_start; ops->stop = nmi_stop; ops->cpu_type = cpu_type; + + if (model->init) + ret = model->init(ops); + if (ret) + return ret; + + init_sysfs(); + using_nmi = 1; printk(KERN_INFO "oprofile: using NMI interrupt.\n"); return 0; } @@ -513,4 +526,6 @@ void op_nmi_exit(void) unregister_cpu_notifier(&oprofile_cpu_nb); #endif } + if (model->exit) + model->exit(); } diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c new file mode 100644 index 00000000000..d9faf607b3a --- /dev/null +++ b/arch/x86/oprofile/op_model_amd.c @@ -0,0 +1,543 @@ +/* + * @file op_model_amd.c + * athlon / K7 / K8 / Family 10h model-specific MSR operations + * + * @remark Copyright 2002-2008 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * @author Philippe Elie + * @author Graydon Hoare + * @author Robert Richter <robert.richter@amd.com> + * @author Barry Kasindorf +*/ + +#include <linux/oprofile.h> +#include <linux/device.h> +#include <linux/pci.h> + +#include <asm/ptrace.h> +#include <asm/msr.h> +#include <asm/nmi.h> + +#include "op_x86_model.h" +#include "op_counter.h" + +#define NUM_COUNTERS 4 +#define NUM_CONTROLS 4 + +#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) +#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) +#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) +#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) + +#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) +#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) +#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) +#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) +#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) +#define CTRL_CLEAR_LO(x) (x &= (1<<21)) +#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) +#define CTRL_SET_ENABLE(val) (val |= 1<<20) +#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) +#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) +#define CTRL_SET_UM(val, m) (val |= (m << 8)) +#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) +#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) +#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) +#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) + +static unsigned long reset_value[NUM_COUNTERS]; + +#ifdef CONFIG_OPROFILE_IBS + +/* IbsFetchCtl bits/masks */ +#define IBS_FETCH_HIGH_VALID_BIT (1UL << 17) /* bit 49 */ +#define IBS_FETCH_HIGH_ENABLE (1UL << 16) /* bit 48 */ +#define IBS_FETCH_LOW_MAX_CNT_MASK 0x0000FFFFUL /* MaxCnt mask */ + +/*IbsOpCtl bits */ +#define IBS_OP_LOW_VALID_BIT (1ULL<<18) /* bit 18 */ +#define IBS_OP_LOW_ENABLE (1ULL<<17) /* bit 17 */ + +/* Codes used in cpu_buffer.c */ +/* This produces duplicate code, need to be fixed */ +#define IBS_FETCH_BEGIN 3 +#define IBS_OP_BEGIN 4 + +/* The function interface needs to be fixed, something like add + data. Should then be added to linux/oprofile.h. */ +extern void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, u8 code); + +struct ibs_fetch_sample { + /* MSRC001_1031 IBS Fetch Linear Address Register */ + unsigned int ibs_fetch_lin_addr_low; + unsigned int ibs_fetch_lin_addr_high; + /* MSRC001_1030 IBS Fetch Control Register */ + unsigned int ibs_fetch_ctl_low; + unsigned int ibs_fetch_ctl_high; + /* MSRC001_1032 IBS Fetch Physical Address Register */ + unsigned int ibs_fetch_phys_addr_low; + unsigned int ibs_fetch_phys_addr_high; +}; + +struct ibs_op_sample { + /* MSRC001_1034 IBS Op Logical Address Register (IbsRIP) */ + unsigned int ibs_op_rip_low; + unsigned int ibs_op_rip_high; + /* MSRC001_1035 IBS Op Data Register */ + unsigned int ibs_op_data1_low; + unsigned int ibs_op_data1_high; + /* MSRC001_1036 IBS Op Data 2 Register */ + unsigned int ibs_op_data2_low; + unsigned int ibs_op_data2_high; + /* MSRC001_1037 IBS Op Data 3 Register */ + unsigned int ibs_op_data3_low; + unsigned int ibs_op_data3_high; + /* MSRC001_1038 IBS DC Linear Address Register (IbsDcLinAd) */ + unsigned int ibs_dc_linear_low; + unsigned int ibs_dc_linear_high; + /* MSRC001_1039 IBS DC Physical Address Register (IbsDcPhysAd) */ + unsigned int ibs_dc_phys_low; + unsigned int ibs_dc_phys_high; +}; + +/* + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h+ +*/ +static void clear_ibs_nmi(void); + +static int ibs_allowed; /* AMD Family10h and later */ + +struct op_ibs_config { + unsigned long op_enabled; + unsigned long fetch_enabled; + unsigned long max_cnt_fetch; + unsigned long max_cnt_op; + unsigned long rand_en; + unsigned long dispatched_ops; +}; + +static struct op_ibs_config ibs_config; + +#endif + +/* functions for op_amd_spec */ + +static void op_amd_fill_in_addresses(struct op_msrs * const msrs) +{ + int i; + + for (i = 0; i < NUM_COUNTERS; i++) { + if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) + msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; + else + msrs->counters[i].addr = 0; + } + + for (i = 0; i < NUM_CONTROLS; i++) { + if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) + msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; + else + msrs->controls[i].addr = 0; + } +} + + +static void op_amd_setup_ctrs(struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + /* clear all counters */ + for (i = 0 ; i < NUM_CONTROLS; ++i) { + if (unlikely(!CTRL_IS_RESERVED(msrs, i))) + continue; + CTRL_READ(low, high, msrs, i); + CTRL_CLEAR_LO(low); + CTRL_CLEAR_HI(high); + CTRL_WRITE(low, high, msrs, i); + } + + /* avoid a false detection of ctr overflows in NMI handler */ + for (i = 0; i < NUM_COUNTERS; ++i) { + if (unlikely(!CTR_IS_RESERVED(msrs, i))) + continue; + CTR_WRITE(1, msrs, i); + } + + /* enable active counters */ + for (i = 0; i < NUM_COUNTERS; ++i) { + if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { + reset_value[i] = counter_config[i].count; + + CTR_WRITE(counter_config[i].count, msrs, i); + + CTRL_READ(low, high, msrs, i); + CTRL_CLEAR_LO(low); + CTRL_CLEAR_HI(high); + CTRL_SET_ENABLE(low); + CTRL_SET_USR(low, counter_config[i].user); + CTRL_SET_KERN(low, counter_config[i].kernel); + CTRL_SET_UM(low, counter_config[i].unit_mask); + CTRL_SET_EVENT_LOW(low, counter_config[i].event); + CTRL_SET_EVENT_HIGH(high, counter_config[i].event); + CTRL_SET_HOST_ONLY(high, 0); + CTRL_SET_GUEST_ONLY(high, 0); + + CTRL_WRITE(low, high, msrs, i); + } else { + reset_value[i] = 0; + } + } +} + +#ifdef CONFIG_OPROFILE_IBS + +static inline int +op_amd_handle_ibs(struct pt_regs * const regs, + struct op_msrs const * const msrs) +{ + unsigned int low, high; + struct ibs_fetch_sample ibs_fetch; + struct ibs_op_sample ibs_op; + + if (!ibs_allowed) + return 1; + + if (ibs_config.fetch_enabled) { + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); + if (high & IBS_FETCH_HIGH_VALID_BIT) { + ibs_fetch.ibs_fetch_ctl_high = high; + ibs_fetch.ibs_fetch_ctl_low = low; + rdmsr(MSR_AMD64_IBSFETCHLINAD, low, high); + ibs_fetch.ibs_fetch_lin_addr_high = high; + ibs_fetch.ibs_fetch_lin_addr_low = low; + rdmsr(MSR_AMD64_IBSFETCHPHYSAD, low, high); + ibs_fetch.ibs_fetch_phys_addr_high = high; + ibs_fetch.ibs_fetch_phys_addr_low = low; + + oprofile_add_ibs_sample(regs, + (unsigned int *)&ibs_fetch, + IBS_FETCH_BEGIN); + + /*reenable the IRQ */ + rdmsr(MSR_AMD64_IBSFETCHCTL, low, high); + high &= ~IBS_FETCH_HIGH_VALID_BIT; + high |= IBS_FETCH_HIGH_ENABLE; + low &= IBS_FETCH_LOW_MAX_CNT_MASK; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + } + + if (ibs_config.op_enabled) { + rdmsr(MSR_AMD64_IBSOPCTL, low, high); + if (low & IBS_OP_LOW_VALID_BIT) { + rdmsr(MSR_AMD64_IBSOPRIP, low, high); + ibs_op.ibs_op_rip_low = low; + ibs_op.ibs_op_rip_high = high; + rdmsr(MSR_AMD64_IBSOPDATA, low, high); + ibs_op.ibs_op_data1_low = low; + ibs_op.ibs_op_data1_high = high; + rdmsr(MSR_AMD64_IBSOPDATA2, low, high); + ibs_op.ibs_op_data2_low = low; + ibs_op.ibs_op_data2_high = high; + rdmsr(MSR_AMD64_IBSOPDATA3, low, high); + ibs_op.ibs_op_data3_low = low; + ibs_op.ibs_op_data3_high = high; + rdmsr(MSR_AMD64_IBSDCLINAD, low, high); + ibs_op.ibs_dc_linear_low = low; + ibs_op.ibs_dc_linear_high = high; + rdmsr(MSR_AMD64_IBSDCPHYSAD, low, high); + ibs_op.ibs_dc_phys_low = low; + ibs_op.ibs_dc_phys_high = high; + + /* reenable the IRQ */ + oprofile_add_ibs_sample(regs, + (unsigned int *)&ibs_op, + IBS_OP_BEGIN); + rdmsr(MSR_AMD64_IBSOPCTL, low, high); + high = 0; + low &= ~IBS_OP_LOW_VALID_BIT; + low |= IBS_OP_LOW_ENABLE; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } + } + + return 1; +} + +#endif + +static int op_amd_check_ctrs(struct pt_regs * const regs, + struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + for (i = 0 ; i < NUM_COUNTERS; ++i) { + if (!reset_value[i]) + continue; + CTR_READ(low, high, msrs, i); + if (CTR_OVERFLOWED(low)) { + oprofile_add_sample(regs, i); + CTR_WRITE(reset_value[i], msrs, i); + } + } + +#ifdef CONFIG_OPROFILE_IBS + op_amd_handle_ibs(regs, msrs); +#endif + + /* See op_model_ppro.c */ + return 1; +} + +static void op_amd_start(struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + for (i = 0 ; i < NUM_COUNTERS ; ++i) { + if (reset_value[i]) { + CTRL_READ(low, high, msrs, i); + CTRL_SET_ACTIVE(low); + CTRL_WRITE(low, high, msrs, i); + } + } + +#ifdef CONFIG_OPROFILE_IBS + if (ibs_allowed && ibs_config.fetch_enabled) { + low = (ibs_config.max_cnt_fetch >> 4) & 0xFFFF; + high = IBS_FETCH_HIGH_ENABLE; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + + if (ibs_allowed && ibs_config.op_enabled) { + low = ((ibs_config.max_cnt_op >> 4) & 0xFFFF) + IBS_OP_LOW_ENABLE; + high = 0; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } +#endif +} + + +static void op_amd_stop(struct op_msrs const * const msrs) +{ + unsigned int low, high; + int i; + + /* Subtle: stop on all counters to avoid race with + * setting our pm callback */ + for (i = 0 ; i < NUM_COUNTERS ; ++i) { + if (!reset_value[i]) + continue; + CTRL_READ(low, high, msrs, i); + CTRL_SET_INACTIVE(low); + CTRL_WRITE(low, high, msrs, i); + } + +#ifdef CONFIG_OPROFILE_IBS + if (ibs_allowed && ibs_config.fetch_enabled) { + low = 0; /* clear max count and enable */ + high = 0; + wrmsr(MSR_AMD64_IBSFETCHCTL, low, high); + } + + if (ibs_allowed && ibs_config.op_enabled) { + low = 0; /* clear max count and enable */ + high = 0; + wrmsr(MSR_AMD64_IBSOPCTL, low, high); + } +#endif +} + +static void op_amd_shutdown(struct op_msrs const * const msrs) +{ + int i; + + for (i = 0 ; i < NUM_COUNTERS ; ++i) { + if (CTR_IS_RESERVED(msrs, i)) + release_perfctr_nmi(MSR_K7_PERFCTR0 + i); + } + for (i = 0 ; i < NUM_CONTROLS ; ++i) { + if (CTRL_IS_RESERVED(msrs, i)) + release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); + } +} + +#ifndef CONFIG_OPROFILE_IBS + +/* no IBS support */ + +static int op_amd_init(struct oprofile_operations *ops) +{ + return 0; +} + +static void op_amd_exit(void) {} + +#else + +static u8 ibs_eilvt_off; + +static inline void apic_init_ibs_nmi_per_cpu(void *arg) +{ + ibs_eilvt_off = setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_NMI, 0); +} + +static inline void apic_clear_ibs_nmi_per_cpu(void *arg) +{ + setup_APIC_eilvt_ibs(0, APIC_EILVT_MSG_FIX, 1); +} + +static int pfm_amd64_setup_eilvt(void) +{ +#define IBSCTL_LVTOFFSETVAL (1 << 8) +#define IBSCTL 0x1cc + struct pci_dev *cpu_cfg; + int nodes; + u32 value = 0; + + /* per CPU setup */ + on_each_cpu(apic_init_ibs_nmi_per_cpu, NULL, 1); + + nodes = 0; + cpu_cfg = NULL; + do { + cpu_cfg = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_10H_NB_MISC, + cpu_cfg); + if (!cpu_cfg) + break; + ++nodes; + pci_write_config_dword(cpu_cfg, IBSCTL, ibs_eilvt_off + | IBSCTL_LVTOFFSETVAL); + pci_read_config_dword(cpu_cfg, IBSCTL, &value); + if (value != (ibs_eilvt_off | IBSCTL_LVTOFFSETVAL)) { + printk(KERN_DEBUG "Failed to setup IBS LVT offset, " + "IBSCTL = 0x%08x", value); + return 1; + } + } while (1); + + if (!nodes) { + printk(KERN_DEBUG "No CPU node configured for IBS"); + return 1; + } + +#ifdef CONFIG_NUMA + /* Sanity check */ + /* Works only for 64bit with proper numa implementation. */ + if (nodes != num_possible_nodes()) { + printk(KERN_DEBUG "Failed to setup CPU node(s) for IBS, " + "found: %d, expected %d", + nodes, num_possible_nodes()); + return 1; + } +#endif + return 0; +} + +/* + * initialize the APIC for the IBS interrupts + * if available (AMD Family10h rev B0 and later) + */ +static void setup_ibs(void) +{ + ibs_allowed = boot_cpu_has(X86_FEATURE_IBS); + + if (!ibs_allowed) + return; + + if (pfm_amd64_setup_eilvt()) { + ibs_allowed = 0; + return; + } + + printk(KERN_INFO "oprofile: AMD IBS detected\n"); +} + + +/* + * unitialize the APIC for the IBS interrupts if needed on AMD Family10h + * rev B0 and later */ +static void clear_ibs_nmi(void) +{ + if (ibs_allowed) + on_each_cpu(apic_clear_ibs_nmi_per_cpu, NULL, 1); +} + +static int (*create_arch_files)(struct super_block * sb, struct dentry * root); + +static int setup_ibs_files(struct super_block * sb, struct dentry * root) +{ + char buf[12]; + struct dentry *dir; + int ret = 0; + + /* architecture specific files */ + if (create_arch_files) + ret = create_arch_files(sb, root); + + if (ret) + return ret; + + if (!ibs_allowed) + return ret; + + /* model specific files */ + + /* setup some reasonable defaults */ + ibs_config.max_cnt_fetch = 250000; + ibs_config.fetch_enabled = 0; + ibs_config.max_cnt_op = 250000; + ibs_config.op_enabled = 0; + ibs_config.dispatched_ops = 1; + snprintf(buf, sizeof(buf), "ibs_fetch"); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "rand_enable", + &ibs_config.rand_en); + oprofilefs_create_ulong(sb, dir, "enable", + &ibs_config.fetch_enabled); + oprofilefs_create_ulong(sb, dir, "max_count", + &ibs_config.max_cnt_fetch); + snprintf(buf, sizeof(buf), "ibs_uops"); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "enable", + &ibs_config.op_enabled); + oprofilefs_create_ulong(sb, dir, "max_count", + &ibs_config.max_cnt_op); + oprofilefs_create_ulong(sb, dir, "dispatched_ops", + &ibs_config.dispatched_ops); + + return 0; +} + +static int op_amd_init(struct oprofile_operations *ops) +{ + setup_ibs(); + create_arch_files = ops->create_files; + ops->create_files = setup_ibs_files; + return 0; +} + +static void op_amd_exit(void) +{ + clear_ibs_nmi(); +} + +#endif + +struct op_x86_model_spec const op_amd_spec = { + .init = op_amd_init, + .exit = op_amd_exit, + .num_counters = NUM_COUNTERS, + .num_controls = NUM_CONTROLS, + .fill_in_addresses = &op_amd_fill_in_addresses, + .setup_ctrs = &op_amd_setup_ctrs, + .check_ctrs = &op_amd_check_ctrs, + .start = &op_amd_start, + .stop = &op_amd_stop, + .shutdown = &op_amd_shutdown +}; diff --git a/arch/x86/oprofile/op_model_athlon.c b/arch/x86/oprofile/op_model_athlon.c deleted file mode 100644 index 3d534879a9d..00000000000 --- a/arch/x86/oprofile/op_model_athlon.c +++ /dev/null @@ -1,190 +0,0 @@ -/* - * @file op_model_athlon.h - * athlon / K7 / K8 / Family 10h model-specific MSR operations - * - * @remark Copyright 2002 OProfile authors - * @remark Read the file COPYING - * - * @author John Levon - * @author Philippe Elie - * @author Graydon Hoare - */ - -#include <linux/oprofile.h> -#include <asm/ptrace.h> -#include <asm/msr.h> -#include <asm/nmi.h> - -#include "op_x86_model.h" -#include "op_counter.h" - -#define NUM_COUNTERS 4 -#define NUM_CONTROLS 4 - -#define CTR_IS_RESERVED(msrs, c) (msrs->counters[(c)].addr ? 1 : 0) -#define CTR_READ(l, h, msrs, c) do {rdmsr(msrs->counters[(c)].addr, (l), (h)); } while (0) -#define CTR_WRITE(l, msrs, c) do {wrmsr(msrs->counters[(c)].addr, -(unsigned int)(l), -1); } while (0) -#define CTR_OVERFLOWED(n) (!((n) & (1U<<31))) - -#define CTRL_IS_RESERVED(msrs, c) (msrs->controls[(c)].addr ? 1 : 0) -#define CTRL_READ(l, h, msrs, c) do {rdmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) -#define CTRL_WRITE(l, h, msrs, c) do {wrmsr(msrs->controls[(c)].addr, (l), (h)); } while (0) -#define CTRL_SET_ACTIVE(n) (n |= (1<<22)) -#define CTRL_SET_INACTIVE(n) (n &= ~(1<<22)) -#define CTRL_CLEAR_LO(x) (x &= (1<<21)) -#define CTRL_CLEAR_HI(x) (x &= 0xfffffcf0) -#define CTRL_SET_ENABLE(val) (val |= 1<<20) -#define CTRL_SET_USR(val, u) (val |= ((u & 1) << 16)) -#define CTRL_SET_KERN(val, k) (val |= ((k & 1) << 17)) -#define CTRL_SET_UM(val, m) (val |= (m << 8)) -#define CTRL_SET_EVENT_LOW(val, e) (val |= (e & 0xff)) -#define CTRL_SET_EVENT_HIGH(val, e) (val |= ((e >> 8) & 0xf)) -#define CTRL_SET_HOST_ONLY(val, h) (val |= ((h & 1) << 9)) -#define CTRL_SET_GUEST_ONLY(val, h) (val |= ((h & 1) << 8)) - -static unsigned long reset_value[NUM_COUNTERS]; - -static void athlon_fill_in_addresses(struct op_msrs * const msrs) -{ - int i; - - for (i = 0; i < NUM_COUNTERS; i++) { - if (reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i)) - msrs->counters[i].addr = MSR_K7_PERFCTR0 + i; - else - msrs->counters[i].addr = 0; - } - - for (i = 0; i < NUM_CONTROLS; i++) { - if (reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i)) - msrs->controls[i].addr = MSR_K7_EVNTSEL0 + i; - else - msrs->controls[i].addr = 0; - } -} - - -static void athlon_setup_ctrs(struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - - /* clear all counters */ - for (i = 0 ; i < NUM_CONTROLS; ++i) { - if (unlikely(!CTRL_IS_RESERVED(msrs, i))) - continue; - CTRL_READ(low, high, msrs, i); - CTRL_CLEAR_LO(low); - CTRL_CLEAR_HI(high); - CTRL_WRITE(low, high, msrs, i); - } - - /* avoid a false detection of ctr overflows in NMI handler */ - for (i = 0; i < NUM_COUNTERS; ++i) { - if (unlikely(!CTR_IS_RESERVED(msrs, i))) - continue; - CTR_WRITE(1, msrs, i); - } - - /* enable active counters */ - for (i = 0; i < NUM_COUNTERS; ++i) { - if ((counter_config[i].enabled) && (CTR_IS_RESERVED(msrs, i))) { - reset_value[i] = counter_config[i].count; - - CTR_WRITE(counter_config[i].count, msrs, i); - - CTRL_READ(low, high, msrs, i); - CTRL_CLEAR_LO(low); - CTRL_CLEAR_HI(high); - CTRL_SET_ENABLE(low); - CTRL_SET_USR(low, counter_config[i].user); - CTRL_SET_KERN(low, counter_config[i].kernel); - CTRL_SET_UM(low, counter_config[i].unit_mask); - CTRL_SET_EVENT_LOW(low, counter_config[i].event); - CTRL_SET_EVENT_HIGH(high, counter_config[i].event); - CTRL_SET_HOST_ONLY(high, 0); - CTRL_SET_GUEST_ONLY(high, 0); - - CTRL_WRITE(low, high, msrs, i); - } else { - reset_value[i] = 0; - } - } -} - - -static int athlon_check_ctrs(struct pt_regs * const regs, - struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - - for (i = 0 ; i < NUM_COUNTERS; ++i) { - if (!reset_value[i]) - continue; - CTR_READ(low, high, msrs, i); - if (CTR_OVERFLOWED(low)) { - oprofile_add_sample(regs, i); - CTR_WRITE(reset_value[i], msrs, i); - } - } - - /* See op_model_ppro.c */ - return 1; -} - - -static void athlon_start(struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - for (i = 0 ; i < NUM_COUNTERS ; ++i) { - if (reset_value[i]) { - CTRL_READ(low, high, msrs, i); - CTRL_SET_ACTIVE(low); - CTRL_WRITE(low, high, msrs, i); - } - } -} - - -static void athlon_stop(struct op_msrs const * const msrs) -{ - unsigned int low, high; - int i; - - /* Subtle: stop on all counters to avoid race with - * setting our pm callback */ - for (i = 0 ; i < NUM_COUNTERS ; ++i) { - if (!reset_value[i]) - continue; - CTRL_READ(low, high, msrs, i); - CTRL_SET_INACTIVE(low); - CTRL_WRITE(low, high, msrs, i); - } -} - -static void athlon_shutdown(struct op_msrs const * const msrs) -{ - int i; - - for (i = 0 ; i < NUM_COUNTERS ; ++i) { - if (CTR_IS_RESERVED(msrs, i)) - release_perfctr_nmi(MSR_K7_PERFCTR0 + i); - } - for (i = 0 ; i < NUM_CONTROLS ; ++i) { - if (CTRL_IS_RESERVED(msrs, i)) - release_evntsel_nmi(MSR_K7_EVNTSEL0 + i); - } -} - -struct op_x86_model_spec const op_athlon_spec = { - .num_counters = NUM_COUNTERS, - .num_controls = NUM_CONTROLS, - .fill_in_addresses = &athlon_fill_in_addresses, - .setup_ctrs = &athlon_setup_ctrs, - .check_ctrs = &athlon_check_ctrs, - .start = &athlon_start, - .stop = &athlon_stop, - .shutdown = &athlon_shutdown -}; diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index 45b605fa71d..05a0261ba0c 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h @@ -32,6 +32,8 @@ struct pt_regs; * various x86 CPU models' perfctr support. */ struct op_x86_model_spec { + int (*init)(struct oprofile_operations *ops); + void (*exit)(void); unsigned int const num_counters; unsigned int const num_controls; void (*fill_in_addresses)(struct op_msrs * const msrs); @@ -46,6 +48,6 @@ struct op_x86_model_spec { extern struct op_x86_model_spec const op_ppro_spec; extern struct op_x86_model_spec const op_p4_spec; extern struct op_x86_model_spec const op_p4_ht2_spec; -extern struct op_x86_model_spec const op_athlon_spec; +extern struct op_x86_model_spec const op_amd_spec; #endif /* OP_X86_MODEL_H */ diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index 4bdaa590375..3c27a809393 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c @@ -511,3 +511,31 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1201, fam10h_pci_cfg_space_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1202, fam10h_pci_cfg_space_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1203, fam10h_pci_cfg_space_size); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, 0x1204, fam10h_pci_cfg_space_size); + +/* + * SB600: Disable BAR1 on device 14.0 to avoid HPET resources from + * confusing the PCI engine: + */ +static void sb600_disable_hpet_bar(struct pci_dev *dev) +{ + u8 val; + + /* + * The SB600 and SB700 both share the same device + * ID, but the PM register 0x55 does something different + * for the SB700, so make sure we are dealing with the + * SB600 before touching the bit: + */ + + pci_read_config_byte(dev, 0x08, &val); + + if (val < 0x2F) { + outb(0x55, 0xCD6); + val = inb(0xCD7); + + /* Set bit 7 in PM register 0x55 */ + outb(0x55, 0xCD6); + outb(val | 0x80, 0xCD7); + } +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, 0x4385, sb600_disable_hpet_bar); diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig index 3815e425f47..87b9ab16642 100644 --- a/arch/x86/xen/Kconfig +++ b/arch/x86/xen/Kconfig @@ -26,5 +26,13 @@ config XEN_MAX_DOMAIN_MEMORY config XEN_SAVE_RESTORE bool - depends on PM - default y
\ No newline at end of file + depends on XEN && PM + default y + +config XEN_DEBUG_FS + bool "Enable Xen debug and tuning parameters in debugfs" + depends on XEN && DEBUG_FS + default n + help + Enable statistics output and various tuning options in debugfs. + Enabling this option may incur a significant performance overhead. diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 59c1e539aed..313947940a1 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -1,4 +1,12 @@ -obj-y := enlighten.o setup.o multicalls.o mmu.o \ +ifdef CONFIG_FTRACE +# Do not profile debug and lowlevel utilities +CFLAGS_REMOVE_spinlock.o = -pg +CFLAGS_REMOVE_time.o = -pg +CFLAGS_REMOVE_irq.o = -pg +endif + +obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \ time.o xen-asm_$(BITS).o grant-table.o suspend.o -obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_SMP) += smp.o spinlock.o +obj-$(CONFIG_XEN_DEBUG_FS) += debugfs.o
\ No newline at end of file diff --git a/arch/x86/xen/debugfs.c b/arch/x86/xen/debugfs.c new file mode 100644 index 00000000000..b53225d2cac --- /dev/null +++ b/arch/x86/xen/debugfs.c @@ -0,0 +1,123 @@ +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/module.h> + +#include "debugfs.h" + +static struct dentry *d_xen_debug; + +struct dentry * __init xen_init_debugfs(void) +{ + if (!d_xen_debug) { + d_xen_debug = debugfs_create_dir("xen", NULL); + + if (!d_xen_debug) + pr_warning("Could not create 'xen' debugfs directory\n"); + } + + return d_xen_debug; +} + +struct array_data +{ + void *array; + unsigned elements; +}; + +static int u32_array_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return nonseekable_open(inode, file); +} + +static size_t format_array(char *buf, size_t bufsize, const char *fmt, + u32 *array, unsigned array_size) +{ + size_t ret = 0; + unsigned i; + + for(i = 0; i < array_size; i++) { + size_t len; + + len = snprintf(buf, bufsize, fmt, array[i]); + len++; /* ' ' or '\n' */ + ret += len; + + if (buf) { + buf += len; + bufsize -= len; + buf[-1] = (i == array_size-1) ? '\n' : ' '; + } + } + + ret++; /* \0 */ + if (buf) + *buf = '\0'; + + return ret; +} + +static char *format_array_alloc(const char *fmt, u32 *array, unsigned array_size) +{ + size_t len = format_array(NULL, 0, fmt, array, array_size); + char *ret; + + ret = kmalloc(len, GFP_KERNEL); + if (ret == NULL) + return NULL; + + format_array(ret, len, fmt, array, array_size); + return ret; +} + +static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, + loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct array_data *data = inode->i_private; + size_t size; + + if (*ppos == 0) { + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + + file->private_data = format_array_alloc("%u", data->array, data->elements); + } + + size = 0; + if (file->private_data) + size = strlen(file->private_data); + + return simple_read_from_buffer(buf, len, ppos, file->private_data, size); +} + +static int xen_array_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static struct file_operations u32_array_fops = { + .owner = THIS_MODULE, + .open = u32_array_open, + .release= xen_array_release, + .read = u32_array_read, +}; + +struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode, + struct dentry *parent, + u32 *array, unsigned elements) +{ + struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL); + + if (data == NULL) + return NULL; + + data->array = array; + data->elements = elements; + + return debugfs_create_file(name, mode, parent, data, &u32_array_fops); +} diff --git a/arch/x86/xen/debugfs.h b/arch/x86/xen/debugfs.h new file mode 100644 index 00000000000..e2813208483 --- /dev/null +++ b/arch/x86/xen/debugfs.h @@ -0,0 +1,10 @@ +#ifndef _XEN_DEBUGFS_H +#define _XEN_DEBUGFS_H + +struct dentry * __init xen_init_debugfs(void); + +struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode, + struct dentry *parent, + u32 *array, unsigned elements); + +#endif /* _XEN_DEBUGFS_H */ diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c index a27d562a974..0013a729b41 100644 --- a/arch/x86/xen/enlighten.c +++ b/arch/x86/xen/enlighten.c @@ -30,7 +30,6 @@ #include <xen/interface/xen.h> #include <xen/interface/physdev.h> #include <xen/interface/vcpu.h> -#include <xen/interface/sched.h> #include <xen/features.h> #include <xen/page.h> #include <xen/hvc-console.h> @@ -58,6 +57,9 @@ EXPORT_SYMBOL_GPL(hypercall_page); DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu); DEFINE_PER_CPU(struct vcpu_info, xen_vcpu_info); +enum xen_domain_type xen_domain_type = XEN_NATIVE; +EXPORT_SYMBOL_GPL(xen_domain_type); + /* * Identity map, in addition to plain kernel map. This needs to be * large enough to allocate page table pages to allocate the rest. @@ -111,7 +113,14 @@ struct shared_info *HYPERVISOR_shared_info = (void *)&xen_dummy_shared_info; * * 0: not available, 1: available */ -static int have_vcpu_info_placement = 1; +static int have_vcpu_info_placement = +#ifdef CONFIG_X86_32 + 1 +#else + 0 +#endif + ; + static void xen_vcpu_setup(int cpu) { @@ -227,103 +236,68 @@ static unsigned long xen_get_debugreg(int reg) return HYPERVISOR_get_debugreg(reg); } -static unsigned long xen_save_fl(void) +static void xen_leave_lazy(void) { - struct vcpu_info *vcpu; - unsigned long flags; - - vcpu = x86_read_percpu(xen_vcpu); - - /* flag has opposite sense of mask */ - flags = !vcpu->evtchn_upcall_mask; - - /* convert to IF type flag - -0 -> 0x00000000 - -1 -> 0xffffffff - */ - return (-flags) & X86_EFLAGS_IF; + paravirt_leave_lazy(paravirt_get_lazy_mode()); + xen_mc_flush(); } -static void xen_restore_fl(unsigned long flags) +static unsigned long xen_store_tr(void) { - struct vcpu_info *vcpu; - - /* convert from IF type flag */ - flags = !(flags & X86_EFLAGS_IF); - - /* There's a one instruction preempt window here. We need to - make sure we're don't switch CPUs between getting the vcpu - pointer and updating the mask. */ - preempt_disable(); - vcpu = x86_read_percpu(xen_vcpu); - vcpu->evtchn_upcall_mask = flags; - preempt_enable_no_resched(); - - /* Doesn't matter if we get preempted here, because any - pending event will get dealt with anyway. */ - - if (flags == 0) { - preempt_check_resched(); - barrier(); /* unmask then check (avoid races) */ - if (unlikely(vcpu->evtchn_upcall_pending)) - force_evtchn_callback(); - } + return 0; } -static void xen_irq_disable(void) +/* + * Set the page permissions for a particular virtual address. If the + * address is a vmalloc mapping (or other non-linear mapping), then + * find the linear mapping of the page and also set its protections to + * match. + */ +static void set_aliased_prot(void *v, pgprot_t prot) { - /* There's a one instruction preempt window here. We need to - make sure we're don't switch CPUs between getting the vcpu - pointer and updating the mask. */ - preempt_disable(); - x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; - preempt_enable_no_resched(); -} + int level; + pte_t *ptep; + pte_t pte; + unsigned long pfn; + struct page *page; -static void xen_irq_enable(void) -{ - struct vcpu_info *vcpu; + ptep = lookup_address((unsigned long)v, &level); + BUG_ON(ptep == NULL); - /* We don't need to worry about being preempted here, since - either a) interrupts are disabled, so no preemption, or b) - the caller is confused and is trying to re-enable interrupts - on an indeterminate processor. */ + pfn = pte_pfn(*ptep); + page = pfn_to_page(pfn); - vcpu = x86_read_percpu(xen_vcpu); - vcpu->evtchn_upcall_mask = 0; + pte = pfn_pte(pfn, prot); - /* Doesn't matter if we get preempted here, because any - pending event will get dealt with anyway. */ + if (HYPERVISOR_update_va_mapping((unsigned long)v, pte, 0)) + BUG(); - barrier(); /* unmask then check (avoid races) */ - if (unlikely(vcpu->evtchn_upcall_pending)) - force_evtchn_callback(); -} + if (!PageHighMem(page)) { + void *av = __va(PFN_PHYS(pfn)); -static void xen_safe_halt(void) -{ - /* Blocking includes an implicit local_irq_enable(). */ - if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0) - BUG(); + if (av != v) + if (HYPERVISOR_update_va_mapping((unsigned long)av, pte, 0)) + BUG(); + } else + kmap_flush_unused(); } -static void xen_halt(void) +static void xen_alloc_ldt(struct desc_struct *ldt, unsigned entries) { - if (irqs_disabled()) - HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); - else - xen_safe_halt(); -} + const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; + int i; -static void xen_leave_lazy(void) -{ - paravirt_leave_lazy(paravirt_get_lazy_mode()); - xen_mc_flush(); + for(i = 0; i < entries; i += entries_per_page) + set_aliased_prot(ldt + i, PAGE_KERNEL_RO); } -static unsigned long xen_store_tr(void) +static void xen_free_ldt(struct desc_struct *ldt, unsigned entries) { - return 0; + const unsigned entries_per_page = PAGE_SIZE / LDT_ENTRY_SIZE; + int i; + + for(i = 0; i < entries; i += entries_per_page) + set_aliased_prot(ldt + i, PAGE_KERNEL); } static void xen_set_ldt(const void *addr, unsigned entries) @@ -426,8 +400,7 @@ static void xen_load_gs_index(unsigned int idx) static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum, const void *ptr) { - unsigned long lp = (unsigned long)&dt[entrynum]; - xmaddr_t mach_lp = virt_to_machine(lp); + xmaddr_t mach_lp = arbitrary_virt_to_machine(&dt[entrynum]); u64 entry = *(u64 *)ptr; preempt_disable(); @@ -560,7 +533,7 @@ static void xen_write_gdt_entry(struct desc_struct *dt, int entry, } static void xen_load_sp0(struct tss_struct *tss, - struct thread_struct *thread) + struct thread_struct *thread) { struct multicall_space mcs = xen_mc_entry(0); MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->sp0); @@ -835,6 +808,19 @@ static int xen_write_msr_safe(unsigned int msr, unsigned low, unsigned high) ret = -EFAULT; break; #endif + + case MSR_STAR: + case MSR_CSTAR: + case MSR_LSTAR: + case MSR_SYSCALL_MASK: + case MSR_IA32_SYSENTER_CS: + case MSR_IA32_SYSENTER_ESP: + case MSR_IA32_SYSENTER_EIP: + /* Fast syscall setup is all done in hypercalls, so + these are all ignored. Stub them out here to stop + Xen console noise. */ + break; + default: ret = native_write_msr_safe(msr, low, high); } @@ -878,8 +864,8 @@ static void xen_alloc_ptpage(struct mm_struct *mm, unsigned long pfn, unsigned l SetPagePinned(page); if (!PageHighMem(page)) { - make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); - if (level == PT_PTE) + make_lowmem_page_readonly(__va(PFN_PHYS((unsigned long)pfn))); + if (level == PT_PTE && USE_SPLIT_PTLOCKS) pin_pagetable_pfn(MMUEXT_PIN_L1_TABLE, pfn); } else /* make sure there are no stray mappings of @@ -947,7 +933,7 @@ static void xen_release_ptpage(unsigned long pfn, unsigned level) if (PagePinned(page)) { if (!PageHighMem(page)) { - if (level == PT_PTE) + if (level == PT_PTE && USE_SPLIT_PTLOCKS) pin_pagetable_pfn(MMUEXT_UNPIN_TABLE, pfn); make_lowmem_page_readwrite(__va(PFN_PHYS(pfn))); } @@ -994,6 +980,7 @@ static void *xen_kmap_atomic_pte(struct page *page, enum km_type type) } #endif +#ifdef CONFIG_X86_32 static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte) { /* If there's an existing pte, then don't allow _PAGE_RW to be set */ @@ -1012,6 +999,7 @@ static __init void xen_set_pte_init(pte_t *ptep, pte_t pte) xen_set_pte(ptep, pte); } +#endif static __init void xen_pagetable_setup_start(pgd_t *base) { @@ -1078,7 +1066,6 @@ void xen_setup_vcpu_info_placement(void) /* xen_vcpu_setup managed to place the vcpu_info within the percpu area for all cpus, so make use of it */ -#ifdef CONFIG_X86_32 if (have_vcpu_info_placement) { printk(KERN_INFO "Xen: using vcpu_info placement\n"); @@ -1088,7 +1075,6 @@ void xen_setup_vcpu_info_placement(void) pv_irq_ops.irq_enable = xen_irq_enable_direct; pv_mmu_ops.read_cr2 = xen_read_cr2_direct; } -#endif } static unsigned xen_patch(u8 type, u16 clobbers, void *insnbuf, @@ -1109,12 +1095,10 @@ static unsigned xen_patch(u8 type, u16 clobbers, void *insnbuf, goto patch_site switch (type) { -#ifdef CONFIG_X86_32 SITE(pv_irq_ops, irq_enable); SITE(pv_irq_ops, irq_disable); SITE(pv_irq_ops, save_fl); SITE(pv_irq_ops, restore_fl); -#endif /* CONFIG_X86_32 */ #undef SITE patch_site: @@ -1252,6 +1236,9 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { .load_gs_index = xen_load_gs_index, #endif + .alloc_ldt = xen_alloc_ldt, + .free_ldt = xen_free_ldt, + .store_gdt = native_store_gdt, .store_idt = native_store_idt, .store_tr = xen_store_tr, @@ -1273,36 +1260,6 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = { }, }; -static void __init __xen_init_IRQ(void) -{ -#ifdef CONFIG_X86_64 - int i; - - /* Create identity vector->irq map */ - for(i = 0; i < NR_VECTORS; i++) { - int cpu; - - for_each_possible_cpu(cpu) - per_cpu(vector_irq, cpu)[i] = i; - } -#endif /* CONFIG_X86_64 */ - - xen_init_IRQ(); -} - -static const struct pv_irq_ops xen_irq_ops __initdata = { - .init_IRQ = __xen_init_IRQ, - .save_fl = xen_save_fl, - .restore_fl = xen_restore_fl, - .irq_disable = xen_irq_disable, - .irq_enable = xen_irq_enable, - .safe_halt = xen_safe_halt, - .halt = xen_halt, -#ifdef CONFIG_X86_64 - .adjust_exception_frame = xen_adjust_exception_frame, -#endif -}; - static const struct pv_apic_ops xen_apic_ops __initdata = { #ifdef CONFIG_X86_LOCAL_APIC .setup_boot_clock = paravirt_nop, @@ -1443,7 +1400,7 @@ static void __init xen_reserve_top(void) if (HYPERVISOR_xen_version(XENVER_platform_parameters, &pp) == 0) top = pp.virt_start; - reserve_top_address(-top + 2 * PAGE_SIZE); + reserve_top_address(-top); #endif /* CONFIG_X86_32 */ } @@ -1477,48 +1434,11 @@ static void *m2v(phys_addr_t maddr) return __ka(m2p(maddr)); } -#ifdef CONFIG_X86_64 -static void walk(pgd_t *pgd, unsigned long addr) -{ - unsigned l4idx = pgd_index(addr); - unsigned l3idx = pud_index(addr); - unsigned l2idx = pmd_index(addr); - unsigned l1idx = pte_index(addr); - pgd_t l4; - pud_t l3; - pmd_t l2; - pte_t l1; - - xen_raw_printk("walk %p, %lx -> %d %d %d %d\n", - pgd, addr, l4idx, l3idx, l2idx, l1idx); - - l4 = pgd[l4idx]; - xen_raw_printk(" l4: %016lx\n", l4.pgd); - xen_raw_printk(" %016lx\n", pgd_val(l4)); - - l3 = ((pud_t *)(m2v(l4.pgd)))[l3idx]; - xen_raw_printk(" l3: %016lx\n", l3.pud); - xen_raw_printk(" %016lx\n", pud_val(l3)); - - l2 = ((pmd_t *)(m2v(l3.pud)))[l2idx]; - xen_raw_printk(" l2: %016lx\n", l2.pmd); - xen_raw_printk(" %016lx\n", pmd_val(l2)); - - l1 = ((pte_t *)(m2v(l2.pmd)))[l1idx]; - xen_raw_printk(" l1: %016lx\n", l1.pte); - xen_raw_printk(" %016lx\n", pte_val(l1)); -} -#endif - static void set_page_prot(void *addr, pgprot_t prot) { unsigned long pfn = __pa(addr) >> PAGE_SHIFT; pte_t pte = pfn_pte(pfn, prot); - xen_raw_printk("addr=%p pfn=%lx mfn=%lx prot=%016llx pte=%016llx\n", - addr, pfn, get_phys_to_machine(pfn), - pgprot_val(prot), pte.pte); - if (HYPERVISOR_update_va_mapping((unsigned long)addr, pte, 0)) BUG(); } @@ -1694,6 +1614,8 @@ asmlinkage void __init xen_start_kernel(void) if (!xen_start_info) return; + xen_domain_type = XEN_PV_DOMAIN; + BUG_ON(memcmp(xen_start_info->magic, "xen-3", 5) != 0); xen_setup_features(); @@ -1703,10 +1625,11 @@ asmlinkage void __init xen_start_kernel(void) pv_init_ops = xen_init_ops; pv_time_ops = xen_time_ops; pv_cpu_ops = xen_cpu_ops; - pv_irq_ops = xen_irq_ops; pv_apic_ops = xen_apic_ops; pv_mmu_ops = xen_mmu_ops; + xen_init_irq_ops(); + #ifdef CONFIG_X86_LOCAL_APIC /* * set up the basic apic ops. @@ -1737,7 +1660,7 @@ asmlinkage void __init xen_start_kernel(void) /* Prevent unwanted bits from being set in PTEs. */ __supported_pte_mask &= ~_PAGE_GLOBAL; - if (!is_initial_xendomain()) + if (!xen_initial_domain()) __supported_pte_mask &= ~(_PAGE_PWT | _PAGE_PCD); /* Don't do the full vcpu_info placement stuff until we have a @@ -1772,7 +1695,7 @@ asmlinkage void __init xen_start_kernel(void) boot_params.hdr.ramdisk_size = xen_start_info->mod_len; boot_params.hdr.cmd_line_ptr = __pa(xen_start_info->cmd_line); - if (!is_initial_xendomain()) { + if (!xen_initial_domain()) { add_preferred_console("xenboot", 0, NULL); add_preferred_console("tty", 0, NULL); add_preferred_console("hvc", 0, NULL); @@ -1780,15 +1703,6 @@ asmlinkage void __init xen_start_kernel(void) xen_raw_console_write("about to get started...\n"); -#if 0 - xen_raw_printk("&boot_params=%p __pa(&boot_params)=%lx __va(__pa(&boot_params))=%lx\n", - &boot_params, __pa_symbol(&boot_params), - __va(__pa_symbol(&boot_params))); - - walk(pgd, &boot_params); - walk(pgd, __va(__pa(&boot_params))); -#endif - /* Start the world */ #ifdef CONFIG_X86_32 i386_start_kernel(); diff --git a/arch/x86/xen/irq.c b/arch/x86/xen/irq.c new file mode 100644 index 00000000000..28b85ab8422 --- /dev/null +++ b/arch/x86/xen/irq.c @@ -0,0 +1,143 @@ +#include <linux/hardirq.h> + +#include <xen/interface/xen.h> +#include <xen/interface/sched.h> +#include <xen/interface/vcpu.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> + +#include "xen-ops.h" + +/* + * Force a proper event-channel callback from Xen after clearing the + * callback mask. We do this in a very simple manner, by making a call + * down into Xen. The pending flag will be checked by Xen on return. + */ +void xen_force_evtchn_callback(void) +{ + (void)HYPERVISOR_xen_version(0, NULL); +} + +static void __init __xen_init_IRQ(void) +{ +#ifdef CONFIG_X86_64 + int i; + + /* Create identity vector->irq map */ + for(i = 0; i < NR_VECTORS; i++) { + int cpu; + + for_each_possible_cpu(cpu) + per_cpu(vector_irq, cpu)[i] = i; + } +#endif /* CONFIG_X86_64 */ + + xen_init_IRQ(); +} + +static unsigned long xen_save_fl(void) +{ + struct vcpu_info *vcpu; + unsigned long flags; + + vcpu = x86_read_percpu(xen_vcpu); + + /* flag has opposite sense of mask */ + flags = !vcpu->evtchn_upcall_mask; + + /* convert to IF type flag + -0 -> 0x00000000 + -1 -> 0xffffffff + */ + return (-flags) & X86_EFLAGS_IF; +} + +static void xen_restore_fl(unsigned long flags) +{ + struct vcpu_info *vcpu; + + /* convert from IF type flag */ + flags = !(flags & X86_EFLAGS_IF); + + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = flags; + preempt_enable_no_resched(); + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + if (flags == 0) { + preempt_check_resched(); + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + xen_force_evtchn_callback(); + } +} + +static void xen_irq_disable(void) +{ + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; + preempt_enable_no_resched(); +} + +static void xen_irq_enable(void) +{ + struct vcpu_info *vcpu; + + /* We don't need to worry about being preempted here, since + either a) interrupts are disabled, so no preemption, or b) + the caller is confused and is trying to re-enable interrupts + on an indeterminate processor. */ + + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = 0; + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + xen_force_evtchn_callback(); +} + +static void xen_safe_halt(void) +{ + /* Blocking includes an implicit local_irq_enable(). */ + if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0) + BUG(); +} + +static void xen_halt(void) +{ + if (irqs_disabled()) + HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); + else + xen_safe_halt(); +} + +static const struct pv_irq_ops xen_irq_ops __initdata = { + .init_IRQ = __xen_init_IRQ, + .save_fl = xen_save_fl, + .restore_fl = xen_restore_fl, + .irq_disable = xen_irq_disable, + .irq_enable = xen_irq_enable, + .safe_halt = xen_safe_halt, + .halt = xen_halt, +#ifdef CONFIG_X86_64 + .adjust_exception_frame = xen_adjust_exception_frame, +#endif +}; + +void __init xen_init_irq_ops() +{ + pv_irq_ops = xen_irq_ops; +} diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index aa37469da69..ae173f6edd8 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -40,6 +40,7 @@ */ #include <linux/sched.h> #include <linux/highmem.h> +#include <linux/debugfs.h> #include <linux/bug.h> #include <asm/pgtable.h> @@ -57,6 +58,61 @@ #include "multicalls.h" #include "mmu.h" +#include "debugfs.h" + +#define MMU_UPDATE_HISTO 30 + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct { + u32 pgd_update; + u32 pgd_update_pinned; + u32 pgd_update_batched; + + u32 pud_update; + u32 pud_update_pinned; + u32 pud_update_batched; + + u32 pmd_update; + u32 pmd_update_pinned; + u32 pmd_update_batched; + + u32 pte_update; + u32 pte_update_pinned; + u32 pte_update_batched; + + u32 mmu_update; + u32 mmu_update_extended; + u32 mmu_update_histo[MMU_UPDATE_HISTO]; + + u32 prot_commit; + u32 prot_commit_batched; + + u32 set_pte_at; + u32 set_pte_at_batched; + u32 set_pte_at_pinned; + u32 set_pte_at_current; + u32 set_pte_at_kernel; +} mmu_stats; + +static u8 zero_stats; + +static inline void check_zero(void) +{ + if (unlikely(zero_stats)) { + memset(&mmu_stats, 0, sizeof(mmu_stats)); + zero_stats = 0; + } +} + +#define ADD_STATS(elem, val) \ + do { check_zero(); mmu_stats.elem += (val); } while(0) + +#else /* !CONFIG_XEN_DEBUG_FS */ + +#define ADD_STATS(elem, val) do { (void)(val); } while(0) + +#endif /* CONFIG_XEN_DEBUG_FS */ /* * Just beyond the highest usermode address. STACK_TOP_MAX has a @@ -229,25 +285,35 @@ void make_lowmem_page_readwrite(void *vaddr) } -static bool page_pinned(void *ptr) +static bool xen_page_pinned(void *ptr) { struct page *page = virt_to_page(ptr); return PagePinned(page); } -static void extend_mmu_update(const struct mmu_update *update) +static void xen_extend_mmu_update(const struct mmu_update *update) { struct multicall_space mcs; struct mmu_update *u; mcs = xen_mc_extend_args(__HYPERVISOR_mmu_update, sizeof(*u)); - if (mcs.mc != NULL) + if (mcs.mc != NULL) { + ADD_STATS(mmu_update_extended, 1); + ADD_STATS(mmu_update_histo[mcs.mc->args[1]], -1); + mcs.mc->args[1]++; - else { + + if (mcs.mc->args[1] < MMU_UPDATE_HISTO) + ADD_STATS(mmu_update_histo[mcs.mc->args[1]], 1); + else + ADD_STATS(mmu_update_histo[0], 1); + } else { + ADD_STATS(mmu_update, 1); mcs = __xen_mc_entry(sizeof(*u)); MULTI_mmu_update(mcs.mc, mcs.args, 1, NULL, DOMID_SELF); + ADD_STATS(mmu_update_histo[1], 1); } u = mcs.args; @@ -265,7 +331,9 @@ void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val) /* ptr may be ioremapped for 64-bit pagetable setup */ u.ptr = arbitrary_virt_to_machine(ptr).maddr; u.val = pmd_val_ma(val); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); + + ADD_STATS(pmd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); xen_mc_issue(PARAVIRT_LAZY_MMU); @@ -274,13 +342,17 @@ void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val) void xen_set_pmd(pmd_t *ptr, pmd_t val) { + ADD_STATS(pmd_update, 1); + /* If page is not pinned, we can just update the entry directly */ - if (!page_pinned(ptr)) { + if (!xen_page_pinned(ptr)) { *ptr = val; return; } + ADD_STATS(pmd_update_pinned, 1); + xen_set_pmd_hyper(ptr, val); } @@ -300,12 +372,18 @@ void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, if (mm == &init_mm) preempt_disable(); + ADD_STATS(set_pte_at, 1); +// ADD_STATS(set_pte_at_pinned, xen_page_pinned(ptep)); + ADD_STATS(set_pte_at_current, mm == current->mm); + ADD_STATS(set_pte_at_kernel, mm == &init_mm); + if (mm == current->mm || mm == &init_mm) { if (paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU) { struct multicall_space mcs; mcs = xen_mc_entry(0); MULTI_update_va_mapping(mcs.mc, addr, pteval, 0); + ADD_STATS(set_pte_at_batched, 1); xen_mc_issue(PARAVIRT_LAZY_MMU); goto out; } else @@ -334,7 +412,10 @@ void xen_ptep_modify_prot_commit(struct mm_struct *mm, unsigned long addr, u.ptr = virt_to_machine(ptep).maddr | MMU_PT_UPDATE_PRESERVE_AD; u.val = pte_val_ma(pte); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); + + ADD_STATS(prot_commit, 1); + ADD_STATS(prot_commit_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); xen_mc_issue(PARAVIRT_LAZY_MMU); } @@ -400,7 +481,9 @@ void xen_set_pud_hyper(pud_t *ptr, pud_t val) /* ptr may be ioremapped for 64-bit pagetable setup */ u.ptr = arbitrary_virt_to_machine(ptr).maddr; u.val = pud_val_ma(val); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); + + ADD_STATS(pud_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); xen_mc_issue(PARAVIRT_LAZY_MMU); @@ -409,18 +492,26 @@ void xen_set_pud_hyper(pud_t *ptr, pud_t val) void xen_set_pud(pud_t *ptr, pud_t val) { + ADD_STATS(pud_update, 1); + /* If page is not pinned, we can just update the entry directly */ - if (!page_pinned(ptr)) { + if (!xen_page_pinned(ptr)) { *ptr = val; return; } + ADD_STATS(pud_update_pinned, 1); + xen_set_pud_hyper(ptr, val); } void xen_set_pte(pte_t *ptep, pte_t pte) { + ADD_STATS(pte_update, 1); +// ADD_STATS(pte_update_pinned, xen_page_pinned(ptep)); + ADD_STATS(pte_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); + #ifdef CONFIG_X86_PAE ptep->pte_high = pte.pte_high; smp_wmb(); @@ -490,7 +581,7 @@ static void __xen_set_pgd_hyper(pgd_t *ptr, pgd_t val) u.ptr = virt_to_machine(ptr).maddr; u.val = pgd_val_ma(val); - extend_mmu_update(&u); + xen_extend_mmu_update(&u); } /* @@ -517,17 +608,22 @@ void xen_set_pgd(pgd_t *ptr, pgd_t val) { pgd_t *user_ptr = xen_get_user_pgd(ptr); + ADD_STATS(pgd_update, 1); + /* If page is not pinned, we can just update the entry directly */ - if (!page_pinned(ptr)) { + if (!xen_page_pinned(ptr)) { *ptr = val; if (user_ptr) { - WARN_ON(page_pinned(user_ptr)); + WARN_ON(xen_page_pinned(user_ptr)); *user_ptr = val; } return; } + ADD_STATS(pgd_update_pinned, 1); + ADD_STATS(pgd_update_batched, paravirt_get_lazy_mode() == PARAVIRT_LAZY_MMU); + /* If it's pinned, then we can at least batch the kernel and user updates together. */ xen_mc_batch(); @@ -555,9 +651,12 @@ void xen_set_pgd(pgd_t *ptr, pgd_t val) * For 64-bit, we must skip the Xen hole in the middle of the address * space, just after the big x86-64 virtual hole. */ -static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), - unsigned long limit) +static int xen_pgd_walk(struct mm_struct *mm, + int (*func)(struct mm_struct *mm, struct page *, + enum pt_level), + unsigned long limit) { + pgd_t *pgd = mm->pgd; int flush = 0; unsigned hole_low, hole_high; unsigned pgdidx_limit, pudidx_limit, pmdidx_limit; @@ -590,8 +689,6 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), pmdidx_limit = 0; #endif - flush |= (*func)(virt_to_page(pgd), PT_PGD); - for (pgdidx = 0; pgdidx <= pgdidx_limit; pgdidx++) { pud_t *pud; @@ -604,7 +701,7 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), pud = pud_offset(&pgd[pgdidx], 0); if (PTRS_PER_PUD > 1) /* not folded */ - flush |= (*func)(virt_to_page(pud), PT_PUD); + flush |= (*func)(mm, virt_to_page(pud), PT_PUD); for (pudidx = 0; pudidx < PTRS_PER_PUD; pudidx++) { pmd_t *pmd; @@ -619,7 +716,7 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), pmd = pmd_offset(&pud[pudidx], 0); if (PTRS_PER_PMD > 1) /* not folded */ - flush |= (*func)(virt_to_page(pmd), PT_PMD); + flush |= (*func)(mm, virt_to_page(pmd), PT_PMD); for (pmdidx = 0; pmdidx < PTRS_PER_PMD; pmdidx++) { struct page *pte; @@ -633,28 +730,34 @@ static int pgd_walk(pgd_t *pgd, int (*func)(struct page *, enum pt_level), continue; pte = pmd_page(pmd[pmdidx]); - flush |= (*func)(pte, PT_PTE); + flush |= (*func)(mm, pte, PT_PTE); } } } + out: + /* Do the top level last, so that the callbacks can use it as + a cue to do final things like tlb flushes. */ + flush |= (*func)(mm, virt_to_page(pgd), PT_PGD); return flush; } -static spinlock_t *lock_pte(struct page *page) +/* If we're using split pte locks, then take the page's lock and + return a pointer to it. Otherwise return NULL. */ +static spinlock_t *xen_pte_lock(struct page *page, struct mm_struct *mm) { spinlock_t *ptl = NULL; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS ptl = __pte_lockptr(page); - spin_lock(ptl); + spin_lock_nest_lock(ptl, &mm->page_table_lock); #endif return ptl; } -static void do_unlock(void *v) +static void xen_pte_unlock(void *v) { spinlock_t *ptl = v; spin_unlock(ptl); @@ -672,7 +775,8 @@ static void xen_do_pin(unsigned level, unsigned long pfn) MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); } -static int pin_page(struct page *page, enum pt_level level) +static int xen_pin_page(struct mm_struct *mm, struct page *page, + enum pt_level level) { unsigned pgfl = TestSetPagePinned(page); int flush; @@ -691,21 +795,40 @@ static int pin_page(struct page *page, enum pt_level level) flush = 0; + /* + * We need to hold the pagetable lock between the time + * we make the pagetable RO and when we actually pin + * it. If we don't, then other users may come in and + * attempt to update the pagetable by writing it, + * which will fail because the memory is RO but not + * pinned, so Xen won't do the trap'n'emulate. + * + * If we're using split pte locks, we can't hold the + * entire pagetable's worth of locks during the + * traverse, because we may wrap the preempt count (8 + * bits). The solution is to mark RO and pin each PTE + * page while holding the lock. This means the number + * of locks we end up holding is never more than a + * batch size (~32 entries, at present). + * + * If we're not using split pte locks, we needn't pin + * the PTE pages independently, because we're + * protected by the overall pagetable lock. + */ ptl = NULL; if (level == PT_PTE) - ptl = lock_pte(page); + ptl = xen_pte_lock(page, mm); MULTI_update_va_mapping(mcs.mc, (unsigned long)pt, pfn_pte(pfn, PAGE_KERNEL_RO), level == PT_PGD ? UVMF_TLB_FLUSH : 0); - if (level == PT_PTE) + if (ptl) { xen_do_pin(MMUEXT_PIN_L1_TABLE, pfn); - if (ptl) { /* Queue a deferred unlock for when this batch is completed. */ - xen_mc_callback(do_unlock, ptl); + xen_mc_callback(xen_pte_unlock, ptl); } } @@ -715,11 +838,11 @@ static int pin_page(struct page *page, enum pt_level level) /* This is called just after a mm has been created, but it has not been used yet. We need to make sure that its pagetable is all read-only, and can be pinned. */ -void xen_pgd_pin(pgd_t *pgd) +static void __xen_pgd_pin(struct mm_struct *mm, pgd_t *pgd) { xen_mc_batch(); - if (pgd_walk(pgd, pin_page, USER_LIMIT)) { + if (xen_pgd_walk(mm, xen_pin_page, USER_LIMIT)) { /* re-enable interrupts for kmap_flush_unused */ xen_mc_issue(0); kmap_flush_unused(); @@ -733,25 +856,35 @@ void xen_pgd_pin(pgd_t *pgd) xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(pgd))); if (user_pgd) { - pin_page(virt_to_page(user_pgd), PT_PGD); + xen_pin_page(mm, virt_to_page(user_pgd), PT_PGD); xen_do_pin(MMUEXT_PIN_L4_TABLE, PFN_DOWN(__pa(user_pgd))); } } #else /* CONFIG_X86_32 */ #ifdef CONFIG_X86_PAE /* Need to make sure unshared kernel PMD is pinnable */ - pin_page(virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), PT_PMD); + xen_pin_page(mm, virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), + PT_PMD); #endif xen_do_pin(MMUEXT_PIN_L3_TABLE, PFN_DOWN(__pa(pgd))); #endif /* CONFIG_X86_64 */ xen_mc_issue(0); } +static void xen_pgd_pin(struct mm_struct *mm) +{ + __xen_pgd_pin(mm, mm->pgd); +} + /* * On save, we need to pin all pagetables to make sure they get their * mfns turned into pfns. Search the list for any unpinned pgds and pin * them (unpinned pgds are not currently in use, probably because the * process is under construction or destruction). + * + * Expected to be called in stop_machine() ("equivalent to taking + * every spinlock in the system"), so the locking doesn't really + * matter all that much. */ void xen_mm_pin_all(void) { @@ -762,7 +895,7 @@ void xen_mm_pin_all(void) list_for_each_entry(page, &pgd_list, lru) { if (!PagePinned(page)) { - xen_pgd_pin((pgd_t *)page_address(page)); + __xen_pgd_pin(&init_mm, (pgd_t *)page_address(page)); SetPageSavePinned(page); } } @@ -775,7 +908,8 @@ void xen_mm_pin_all(void) * that's before we have page structures to store the bits. So do all * the book-keeping now. */ -static __init int mark_pinned(struct page *page, enum pt_level level) +static __init int xen_mark_pinned(struct mm_struct *mm, struct page *page, + enum pt_level level) { SetPagePinned(page); return 0; @@ -783,10 +917,11 @@ static __init int mark_pinned(struct page *page, enum pt_level level) void __init xen_mark_init_mm_pinned(void) { - pgd_walk(init_mm.pgd, mark_pinned, FIXADDR_TOP); + xen_pgd_walk(&init_mm, xen_mark_pinned, FIXADDR_TOP); } -static int unpin_page(struct page *page, enum pt_level level) +static int xen_unpin_page(struct mm_struct *mm, struct page *page, + enum pt_level level) { unsigned pgfl = TestClearPagePinned(page); @@ -796,10 +931,18 @@ static int unpin_page(struct page *page, enum pt_level level) spinlock_t *ptl = NULL; struct multicall_space mcs; + /* + * Do the converse to pin_page. If we're using split + * pte locks, we must be holding the lock for while + * the pte page is unpinned but still RO to prevent + * concurrent updates from seeing it in this + * partially-pinned state. + */ if (level == PT_PTE) { - ptl = lock_pte(page); + ptl = xen_pte_lock(page, mm); - xen_do_pin(MMUEXT_UNPIN_TABLE, pfn); + if (ptl) + xen_do_pin(MMUEXT_UNPIN_TABLE, pfn); } mcs = __xen_mc_entry(0); @@ -810,7 +953,7 @@ static int unpin_page(struct page *page, enum pt_level level) if (ptl) { /* unlock when batch completed */ - xen_mc_callback(do_unlock, ptl); + xen_mc_callback(xen_pte_unlock, ptl); } } @@ -818,7 +961,7 @@ static int unpin_page(struct page *page, enum pt_level level) } /* Release a pagetables pages back as normal RW */ -static void xen_pgd_unpin(pgd_t *pgd) +static void __xen_pgd_unpin(struct mm_struct *mm, pgd_t *pgd) { xen_mc_batch(); @@ -830,21 +973,27 @@ static void xen_pgd_unpin(pgd_t *pgd) if (user_pgd) { xen_do_pin(MMUEXT_UNPIN_TABLE, PFN_DOWN(__pa(user_pgd))); - unpin_page(virt_to_page(user_pgd), PT_PGD); + xen_unpin_page(mm, virt_to_page(user_pgd), PT_PGD); } } #endif #ifdef CONFIG_X86_PAE /* Need to make sure unshared kernel PMD is unpinned */ - pin_page(virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), PT_PMD); + xen_unpin_page(mm, virt_to_page(pgd_page(pgd[pgd_index(TASK_SIZE)])), + PT_PMD); #endif - pgd_walk(pgd, unpin_page, USER_LIMIT); + xen_pgd_walk(mm, xen_unpin_page, USER_LIMIT); xen_mc_issue(0); } +static void xen_pgd_unpin(struct mm_struct *mm) +{ + __xen_pgd_unpin(mm, mm->pgd); +} + /* * On resume, undo any pinning done at save, so that the rest of the * kernel doesn't see any unexpected pinned pagetables. @@ -859,7 +1008,7 @@ void xen_mm_unpin_all(void) list_for_each_entry(page, &pgd_list, lru) { if (PageSavePinned(page)) { BUG_ON(!PagePinned(page)); - xen_pgd_unpin((pgd_t *)page_address(page)); + __xen_pgd_unpin(&init_mm, (pgd_t *)page_address(page)); ClearPageSavePinned(page); } } @@ -870,14 +1019,14 @@ void xen_mm_unpin_all(void) void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next) { spin_lock(&next->page_table_lock); - xen_pgd_pin(next->pgd); + xen_pgd_pin(next); spin_unlock(&next->page_table_lock); } void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm) { spin_lock(&mm->page_table_lock); - xen_pgd_pin(mm->pgd); + xen_pgd_pin(mm); spin_unlock(&mm->page_table_lock); } @@ -907,7 +1056,7 @@ static void drop_other_mm_ref(void *info) } } -static void drop_mm_ref(struct mm_struct *mm) +static void xen_drop_mm_ref(struct mm_struct *mm) { cpumask_t mask; unsigned cpu; @@ -937,7 +1086,7 @@ static void drop_mm_ref(struct mm_struct *mm) smp_call_function_mask(mask, drop_other_mm_ref, mm, 1); } #else -static void drop_mm_ref(struct mm_struct *mm) +static void xen_drop_mm_ref(struct mm_struct *mm) { if (current->active_mm == mm) load_cr3(swapper_pg_dir); @@ -961,14 +1110,77 @@ static void drop_mm_ref(struct mm_struct *mm) void xen_exit_mmap(struct mm_struct *mm) { get_cpu(); /* make sure we don't move around */ - drop_mm_ref(mm); + xen_drop_mm_ref(mm); put_cpu(); spin_lock(&mm->page_table_lock); /* pgd may not be pinned in the error exit path of execve */ - if (page_pinned(mm->pgd)) - xen_pgd_unpin(mm->pgd); + if (xen_page_pinned(mm->pgd)) + xen_pgd_unpin(mm); spin_unlock(&mm->page_table_lock); } + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct dentry *d_mmu_debug; + +static int __init xen_mmu_debugfs(void) +{ + struct dentry *d_xen = xen_init_debugfs(); + + if (d_xen == NULL) + return -ENOMEM; + + d_mmu_debug = debugfs_create_dir("mmu", d_xen); + + debugfs_create_u8("zero_stats", 0644, d_mmu_debug, &zero_stats); + + debugfs_create_u32("pgd_update", 0444, d_mmu_debug, &mmu_stats.pgd_update); + debugfs_create_u32("pgd_update_pinned", 0444, d_mmu_debug, + &mmu_stats.pgd_update_pinned); + debugfs_create_u32("pgd_update_batched", 0444, d_mmu_debug, + &mmu_stats.pgd_update_pinned); + + debugfs_create_u32("pud_update", 0444, d_mmu_debug, &mmu_stats.pud_update); + debugfs_create_u32("pud_update_pinned", 0444, d_mmu_debug, + &mmu_stats.pud_update_pinned); + debugfs_create_u32("pud_update_batched", 0444, d_mmu_debug, + &mmu_stats.pud_update_pinned); + + debugfs_create_u32("pmd_update", 0444, d_mmu_debug, &mmu_stats.pmd_update); + debugfs_create_u32("pmd_update_pinned", 0444, d_mmu_debug, + &mmu_stats.pmd_update_pinned); + debugfs_create_u32("pmd_update_batched", 0444, d_mmu_debug, + &mmu_stats.pmd_update_pinned); + + debugfs_create_u32("pte_update", 0444, d_mmu_debug, &mmu_stats.pte_update); +// debugfs_create_u32("pte_update_pinned", 0444, d_mmu_debug, +// &mmu_stats.pte_update_pinned); + debugfs_create_u32("pte_update_batched", 0444, d_mmu_debug, + &mmu_stats.pte_update_pinned); + + debugfs_create_u32("mmu_update", 0444, d_mmu_debug, &mmu_stats.mmu_update); + debugfs_create_u32("mmu_update_extended", 0444, d_mmu_debug, + &mmu_stats.mmu_update_extended); + xen_debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug, + mmu_stats.mmu_update_histo, 20); + + debugfs_create_u32("set_pte_at", 0444, d_mmu_debug, &mmu_stats.set_pte_at); + debugfs_create_u32("set_pte_at_batched", 0444, d_mmu_debug, + &mmu_stats.set_pte_at_batched); + debugfs_create_u32("set_pte_at_current", 0444, d_mmu_debug, + &mmu_stats.set_pte_at_current); + debugfs_create_u32("set_pte_at_kernel", 0444, d_mmu_debug, + &mmu_stats.set_pte_at_kernel); + + debugfs_create_u32("prot_commit", 0444, d_mmu_debug, &mmu_stats.prot_commit); + debugfs_create_u32("prot_commit_batched", 0444, d_mmu_debug, + &mmu_stats.prot_commit_batched); + + return 0; +} +fs_initcall(xen_mmu_debugfs); + +#endif /* CONFIG_XEN_DEBUG_FS */ diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h index 0f59bd03f9e..98d71659da5 100644 --- a/arch/x86/xen/mmu.h +++ b/arch/x86/xen/mmu.h @@ -18,9 +18,6 @@ void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next); void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm); void xen_exit_mmap(struct mm_struct *mm); -void xen_pgd_pin(pgd_t *pgd); -//void xen_pgd_unpin(pgd_t *pgd); - pteval_t xen_pte_val(pte_t); pmdval_t xen_pmd_val(pmd_t); pgdval_t xen_pgd_val(pgd_t); diff --git a/arch/x86/xen/multicalls.c b/arch/x86/xen/multicalls.c index 9efd1c6c977..8ea8a0d0b0d 100644 --- a/arch/x86/xen/multicalls.c +++ b/arch/x86/xen/multicalls.c @@ -21,16 +21,20 @@ */ #include <linux/percpu.h> #include <linux/hardirq.h> +#include <linux/debugfs.h> #include <asm/xen/hypercall.h> #include "multicalls.h" +#include "debugfs.h" + +#define MC_BATCH 32 #define MC_DEBUG 1 -#define MC_BATCH 32 #define MC_ARGS (MC_BATCH * 16) + struct mc_buffer { struct multicall_entry entries[MC_BATCH]; #if MC_DEBUG @@ -47,6 +51,76 @@ struct mc_buffer { static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); +/* flush reasons 0- slots, 1- args, 2- callbacks */ +enum flush_reasons +{ + FL_SLOTS, + FL_ARGS, + FL_CALLBACKS, + + FL_N_REASONS +}; + +#ifdef CONFIG_XEN_DEBUG_FS +#define NHYPERCALLS 40 /* not really */ + +static struct { + unsigned histo[MC_BATCH+1]; + + unsigned issued; + unsigned arg_total; + unsigned hypercalls; + unsigned histo_hypercalls[NHYPERCALLS]; + + unsigned flush[FL_N_REASONS]; +} mc_stats; + +static u8 zero_stats; + +static inline void check_zero(void) +{ + if (unlikely(zero_stats)) { + memset(&mc_stats, 0, sizeof(mc_stats)); + zero_stats = 0; + } +} + +static void mc_add_stats(const struct mc_buffer *mc) +{ + int i; + + check_zero(); + + mc_stats.issued++; + mc_stats.hypercalls += mc->mcidx; + mc_stats.arg_total += mc->argidx; + + mc_stats.histo[mc->mcidx]++; + for(i = 0; i < mc->mcidx; i++) { + unsigned op = mc->entries[i].op; + if (op < NHYPERCALLS) + mc_stats.histo_hypercalls[op]++; + } +} + +static void mc_stats_flush(enum flush_reasons idx) +{ + check_zero(); + + mc_stats.flush[idx]++; +} + +#else /* !CONFIG_XEN_DEBUG_FS */ + +static inline void mc_add_stats(const struct mc_buffer *mc) +{ +} + +static inline void mc_stats_flush(enum flush_reasons idx) +{ +} +#endif /* CONFIG_XEN_DEBUG_FS */ + void xen_mc_flush(void) { struct mc_buffer *b = &__get_cpu_var(mc_buffer); @@ -60,6 +134,8 @@ void xen_mc_flush(void) something in the middle */ local_irq_save(flags); + mc_add_stats(b); + if (b->mcidx) { #if MC_DEBUG memcpy(b->debug, b->entries, @@ -115,6 +191,7 @@ struct multicall_space __xen_mc_entry(size_t args) if (b->mcidx == MC_BATCH || (argidx + args) > MC_ARGS) { + mc_stats_flush(b->mcidx == MC_BATCH ? FL_SLOTS : FL_ARGS); xen_mc_flush(); argidx = roundup(b->argidx, sizeof(u64)); } @@ -158,10 +235,44 @@ void xen_mc_callback(void (*fn)(void *), void *data) struct mc_buffer *b = &__get_cpu_var(mc_buffer); struct callback *cb; - if (b->cbidx == MC_BATCH) + if (b->cbidx == MC_BATCH) { + mc_stats_flush(FL_CALLBACKS); xen_mc_flush(); + } cb = &b->callbacks[b->cbidx++]; cb->fn = fn; cb->data = data; } + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct dentry *d_mc_debug; + +static int __init xen_mc_debugfs(void) +{ + struct dentry *d_xen = xen_init_debugfs(); + + if (d_xen == NULL) + return -ENOMEM; + + d_mc_debug = debugfs_create_dir("multicalls", d_xen); + + debugfs_create_u8("zero_stats", 0644, d_mc_debug, &zero_stats); + + debugfs_create_u32("batches", 0444, d_mc_debug, &mc_stats.issued); + debugfs_create_u32("hypercalls", 0444, d_mc_debug, &mc_stats.hypercalls); + debugfs_create_u32("arg_total", 0444, d_mc_debug, &mc_stats.arg_total); + + xen_debugfs_create_u32_array("batch_histo", 0444, d_mc_debug, + mc_stats.histo, MC_BATCH); + xen_debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug, + mc_stats.histo_hypercalls, NHYPERCALLS); + xen_debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug, + mc_stats.flush, FL_N_REASONS); + + return 0; +} +fs_initcall(xen_mc_debugfs); + +#endif /* CONFIG_XEN_DEBUG_FS */ diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c index d8faf79a0a1..d77da613b1d 100644 --- a/arch/x86/xen/smp.c +++ b/arch/x86/xen/smp.c @@ -11,11 +11,8 @@ * useful topology information for the kernel to make use of. As a * result, all CPUs are treated as if they're single-core and * single-threaded. - * - * This does not handle HOTPLUG_CPU yet. */ #include <linux/sched.h> -#include <linux/kernel_stat.h> #include <linux/err.h> #include <linux/smp.h> @@ -36,8 +33,6 @@ #include "xen-ops.h" #include "mmu.h" -static void __cpuinit xen_init_lock_cpu(int cpu); - cpumask_t xen_cpu_initialized_map; static DEFINE_PER_CPU(int, resched_irq); @@ -64,11 +59,12 @@ static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static __cpuinit void cpu_bringup_and_idle(void) +static __cpuinit void cpu_bringup(void) { int cpu = smp_processor_id(); cpu_init(); + touch_softlockup_watchdog(); preempt_disable(); xen_enable_sysenter(); @@ -89,6 +85,11 @@ static __cpuinit void cpu_bringup_and_idle(void) local_irq_enable(); wmb(); /* make sure everything is out */ +} + +static __cpuinit void cpu_bringup_and_idle(void) +{ + cpu_bringup(); cpu_idle(); } @@ -212,8 +213,6 @@ static void __init xen_smp_prepare_cpus(unsigned int max_cpus) cpu_set(cpu, cpu_present_map); } - - //init_xenbus_allowed_cpumask(); } static __cpuinit int @@ -281,12 +280,6 @@ static int __cpuinit xen_cpu_up(unsigned int cpu) struct task_struct *idle = idle_task(cpu); int rc; -#if 0 - rc = cpu_up_check(cpu); - if (rc) - return rc; -#endif - #ifdef CONFIG_X86_64 /* Allocate node local memory for AP pdas */ WARN_ON(cpu == 0); @@ -339,6 +332,60 @@ static void xen_smp_cpus_done(unsigned int max_cpus) { } +#ifdef CONFIG_HOTPLUG_CPU +static int xen_cpu_disable(void) +{ + unsigned int cpu = smp_processor_id(); + if (cpu == 0) + return -EBUSY; + + cpu_disable_common(); + + load_cr3(swapper_pg_dir); + return 0; +} + +static void xen_cpu_die(unsigned int cpu) +{ + while (HYPERVISOR_vcpu_op(VCPUOP_is_up, cpu, NULL)) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/10); + } + unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); + unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); + unbind_from_irqhandler(per_cpu(debug_irq, cpu), NULL); + unbind_from_irqhandler(per_cpu(callfuncsingle_irq, cpu), NULL); + xen_uninit_lock_cpu(cpu); + xen_teardown_timer(cpu); + + if (num_online_cpus() == 1) + alternatives_smp_switch(0); +} + +static void xen_play_dead(void) +{ + play_dead_common(); + HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); + cpu_bringup(); +} + +#else /* !CONFIG_HOTPLUG_CPU */ +static int xen_cpu_disable(void) +{ + return -ENOSYS; +} + +static void xen_cpu_die(unsigned int cpu) +{ + BUG(); +} + +static void xen_play_dead(void) +{ + BUG(); +} + +#endif static void stop_self(void *v) { int cpu = smp_processor_id(); @@ -419,176 +466,16 @@ static irqreturn_t xen_call_function_single_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -struct xen_spinlock { - unsigned char lock; /* 0 -> free; 1 -> locked */ - unsigned short spinners; /* count of waiting cpus */ -}; - -static int xen_spin_is_locked(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - - return xl->lock != 0; -} - -static int xen_spin_is_contended(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - - /* Not strictly true; this is only the count of contended - lock-takers entering the slow path. */ - return xl->spinners != 0; -} - -static int xen_spin_trylock(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - u8 old = 1; - - asm("xchgb %b0,%1" - : "+q" (old), "+m" (xl->lock) : : "memory"); - - return old == 0; -} - -static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; -static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); - -static inline void spinning_lock(struct xen_spinlock *xl) -{ - __get_cpu_var(lock_spinners) = xl; - wmb(); /* set lock of interest before count */ - asm(LOCK_PREFIX " incw %0" - : "+m" (xl->spinners) : : "memory"); -} - -static inline void unspinning_lock(struct xen_spinlock *xl) -{ - asm(LOCK_PREFIX " decw %0" - : "+m" (xl->spinners) : : "memory"); - wmb(); /* decrement count before clearing lock */ - __get_cpu_var(lock_spinners) = NULL; -} - -static noinline int xen_spin_lock_slow(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - int irq = __get_cpu_var(lock_kicker_irq); - int ret; - - /* If kicker interrupts not initialized yet, just spin */ - if (irq == -1) - return 0; - - /* announce we're spinning */ - spinning_lock(xl); - - /* clear pending */ - xen_clear_irq_pending(irq); - - /* check again make sure it didn't become free while - we weren't looking */ - ret = xen_spin_trylock(lock); - if (ret) - goto out; - - /* block until irq becomes pending */ - xen_poll_irq(irq); - kstat_this_cpu.irqs[irq]++; - -out: - unspinning_lock(xl); - return ret; -} - -static void xen_spin_lock(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - int timeout; - u8 oldval; - - do { - timeout = 1 << 10; - - asm("1: xchgb %1,%0\n" - " testb %1,%1\n" - " jz 3f\n" - "2: rep;nop\n" - " cmpb $0,%0\n" - " je 1b\n" - " dec %2\n" - " jnz 2b\n" - "3:\n" - : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) - : "1" (1) - : "memory"); - - } while (unlikely(oldval != 0 && !xen_spin_lock_slow(lock))); -} - -static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) -{ - int cpu; - - for_each_online_cpu(cpu) { - /* XXX should mix up next cpu selection */ - if (per_cpu(lock_spinners, cpu) == xl) { - xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); - break; - } - } -} - -static void xen_spin_unlock(struct raw_spinlock *lock) -{ - struct xen_spinlock *xl = (struct xen_spinlock *)lock; - - smp_wmb(); /* make sure no writes get moved after unlock */ - xl->lock = 0; /* release lock */ - - /* make sure unlock happens before kick */ - barrier(); - - if (unlikely(xl->spinners)) - xen_spin_unlock_slow(xl); -} - -static __cpuinit void xen_init_lock_cpu(int cpu) -{ - int irq; - const char *name; - - name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); - irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, - cpu, - xen_reschedule_interrupt, - IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, - name, - NULL); - - if (irq >= 0) { - disable_irq(irq); /* make sure it's never delivered */ - per_cpu(lock_kicker_irq, cpu) = irq; - } - - printk("cpu %d spinlock event irq %d\n", cpu, irq); -} - -static void __init xen_init_spinlocks(void) -{ - pv_lock_ops.spin_is_locked = xen_spin_is_locked; - pv_lock_ops.spin_is_contended = xen_spin_is_contended; - pv_lock_ops.spin_lock = xen_spin_lock; - pv_lock_ops.spin_trylock = xen_spin_trylock; - pv_lock_ops.spin_unlock = xen_spin_unlock; -} - static const struct smp_ops xen_smp_ops __initdata = { .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, .smp_prepare_cpus = xen_smp_prepare_cpus, - .cpu_up = xen_cpu_up, .smp_cpus_done = xen_smp_cpus_done, + .cpu_up = xen_cpu_up, + .cpu_die = xen_cpu_die, + .cpu_disable = xen_cpu_disable, + .play_dead = xen_play_dead, + .smp_send_stop = xen_smp_send_stop, .smp_send_reschedule = xen_smp_send_reschedule, diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c new file mode 100644 index 00000000000..dd71e3a021c --- /dev/null +++ b/arch/x86/xen/spinlock.c @@ -0,0 +1,428 @@ +/* + * Split spinlock implementation out into its own file, so it can be + * compiled in a FTRACE-compatible way. + */ +#include <linux/kernel_stat.h> +#include <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/log2.h> + +#include <asm/paravirt.h> + +#include <xen/interface/xen.h> +#include <xen/events.h> + +#include "xen-ops.h" +#include "debugfs.h" + +#ifdef CONFIG_XEN_DEBUG_FS +static struct xen_spinlock_stats +{ + u64 taken; + u32 taken_slow; + u32 taken_slow_nested; + u32 taken_slow_pickup; + u32 taken_slow_spurious; + u32 taken_slow_irqenable; + + u64 released; + u32 released_slow; + u32 released_slow_kicked; + +#define HISTO_BUCKETS 30 + u32 histo_spin_total[HISTO_BUCKETS+1]; + u32 histo_spin_spinning[HISTO_BUCKETS+1]; + u32 histo_spin_blocked[HISTO_BUCKETS+1]; + + u64 time_total; + u64 time_spinning; + u64 time_blocked; +} spinlock_stats; + +static u8 zero_stats; + +static unsigned lock_timeout = 1 << 10; +#define TIMEOUT lock_timeout + +static inline void check_zero(void) +{ + if (unlikely(zero_stats)) { + memset(&spinlock_stats, 0, sizeof(spinlock_stats)); + zero_stats = 0; + } +} + +#define ADD_STATS(elem, val) \ + do { check_zero(); spinlock_stats.elem += (val); } while(0) + +static inline u64 spin_time_start(void) +{ + return xen_clocksource_read(); +} + +static void __spin_time_accum(u64 delta, u32 *array) +{ + unsigned index = ilog2(delta); + + check_zero(); + + if (index < HISTO_BUCKETS) + array[index]++; + else + array[HISTO_BUCKETS]++; +} + +static inline void spin_time_accum_spinning(u64 start) +{ + u32 delta = xen_clocksource_read() - start; + + __spin_time_accum(delta, spinlock_stats.histo_spin_spinning); + spinlock_stats.time_spinning += delta; +} + +static inline void spin_time_accum_total(u64 start) +{ + u32 delta = xen_clocksource_read() - start; + + __spin_time_accum(delta, spinlock_stats.histo_spin_total); + spinlock_stats.time_total += delta; +} + +static inline void spin_time_accum_blocked(u64 start) +{ + u32 delta = xen_clocksource_read() - start; + + __spin_time_accum(delta, spinlock_stats.histo_spin_blocked); + spinlock_stats.time_blocked += delta; +} +#else /* !CONFIG_XEN_DEBUG_FS */ +#define TIMEOUT (1 << 10) +#define ADD_STATS(elem, val) do { (void)(val); } while(0) + +static inline u64 spin_time_start(void) +{ + return 0; +} + +static inline void spin_time_accum_total(u64 start) +{ +} +static inline void spin_time_accum_spinning(u64 start) +{ +} +static inline void spin_time_accum_blocked(u64 start) +{ +} +#endif /* CONFIG_XEN_DEBUG_FS */ + +struct xen_spinlock { + unsigned char lock; /* 0 -> free; 1 -> locked */ + unsigned short spinners; /* count of waiting cpus */ +}; + +static int xen_spin_is_locked(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + + return xl->lock != 0; +} + +static int xen_spin_is_contended(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + + /* Not strictly true; this is only the count of contended + lock-takers entering the slow path. */ + return xl->spinners != 0; +} + +static int xen_spin_trylock(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + u8 old = 1; + + asm("xchgb %b0,%1" + : "+q" (old), "+m" (xl->lock) : : "memory"); + + return old == 0; +} + +static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; +static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); + +/* + * Mark a cpu as interested in a lock. Returns the CPU's previous + * lock of interest, in case we got preempted by an interrupt. + */ +static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) +{ + struct xen_spinlock *prev; + + prev = __get_cpu_var(lock_spinners); + __get_cpu_var(lock_spinners) = xl; + + wmb(); /* set lock of interest before count */ + + asm(LOCK_PREFIX " incw %0" + : "+m" (xl->spinners) : : "memory"); + + return prev; +} + +/* + * Mark a cpu as no longer interested in a lock. Restores previous + * lock of interest (NULL for none). + */ +static inline void unspinning_lock(struct xen_spinlock *xl, struct xen_spinlock *prev) +{ + asm(LOCK_PREFIX " decw %0" + : "+m" (xl->spinners) : : "memory"); + wmb(); /* decrement count before restoring lock */ + __get_cpu_var(lock_spinners) = prev; +} + +static noinline int xen_spin_lock_slow(struct raw_spinlock *lock, bool irq_enable) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + struct xen_spinlock *prev; + int irq = __get_cpu_var(lock_kicker_irq); + int ret; + unsigned long flags; + u64 start; + + /* If kicker interrupts not initialized yet, just spin */ + if (irq == -1) + return 0; + + start = spin_time_start(); + + /* announce we're spinning */ + prev = spinning_lock(xl); + + flags = __raw_local_save_flags(); + if (irq_enable) { + ADD_STATS(taken_slow_irqenable, 1); + raw_local_irq_enable(); + } + + ADD_STATS(taken_slow, 1); + ADD_STATS(taken_slow_nested, prev != NULL); + + do { + /* clear pending */ + xen_clear_irq_pending(irq); + + /* check again make sure it didn't become free while + we weren't looking */ + ret = xen_spin_trylock(lock); + if (ret) { + ADD_STATS(taken_slow_pickup, 1); + + /* + * If we interrupted another spinlock while it + * was blocking, make sure it doesn't block + * without rechecking the lock. + */ + if (prev != NULL) + xen_set_irq_pending(irq); + goto out; + } + + /* + * Block until irq becomes pending. If we're + * interrupted at this point (after the trylock but + * before entering the block), then the nested lock + * handler guarantees that the irq will be left + * pending if there's any chance the lock became free; + * xen_poll_irq() returns immediately if the irq is + * pending. + */ + xen_poll_irq(irq); + ADD_STATS(taken_slow_spurious, !xen_test_irq_pending(irq)); + } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */ + + kstat_this_cpu.irqs[irq]++; + +out: + raw_local_irq_restore(flags); + unspinning_lock(xl, prev); + spin_time_accum_blocked(start); + + return ret; +} + +static inline void __xen_spin_lock(struct raw_spinlock *lock, bool irq_enable) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + unsigned timeout; + u8 oldval; + u64 start_spin; + + ADD_STATS(taken, 1); + + start_spin = spin_time_start(); + + do { + u64 start_spin_fast = spin_time_start(); + + timeout = TIMEOUT; + + asm("1: xchgb %1,%0\n" + " testb %1,%1\n" + " jz 3f\n" + "2: rep;nop\n" + " cmpb $0,%0\n" + " je 1b\n" + " dec %2\n" + " jnz 2b\n" + "3:\n" + : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) + : "1" (1) + : "memory"); + + spin_time_accum_spinning(start_spin_fast); + + } while (unlikely(oldval != 0 && + (TIMEOUT == ~0 || !xen_spin_lock_slow(lock, irq_enable)))); + + spin_time_accum_total(start_spin); +} + +static void xen_spin_lock(struct raw_spinlock *lock) +{ + __xen_spin_lock(lock, false); +} + +static void xen_spin_lock_flags(struct raw_spinlock *lock, unsigned long flags) +{ + __xen_spin_lock(lock, !raw_irqs_disabled_flags(flags)); +} + +static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) +{ + int cpu; + + ADD_STATS(released_slow, 1); + + for_each_online_cpu(cpu) { + /* XXX should mix up next cpu selection */ + if (per_cpu(lock_spinners, cpu) == xl) { + ADD_STATS(released_slow_kicked, 1); + xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); + break; + } + } +} + +static void xen_spin_unlock(struct raw_spinlock *lock) +{ + struct xen_spinlock *xl = (struct xen_spinlock *)lock; + + ADD_STATS(released, 1); + + smp_wmb(); /* make sure no writes get moved after unlock */ + xl->lock = 0; /* release lock */ + + /* make sure unlock happens before kick */ + barrier(); + + if (unlikely(xl->spinners)) + xen_spin_unlock_slow(xl); +} + +static irqreturn_t dummy_handler(int irq, void *dev_id) +{ + BUG(); + return IRQ_HANDLED; +} + +void __cpuinit xen_init_lock_cpu(int cpu) +{ + int irq; + const char *name; + + name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); + irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, + cpu, + dummy_handler, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + name, + NULL); + + if (irq >= 0) { + disable_irq(irq); /* make sure it's never delivered */ + per_cpu(lock_kicker_irq, cpu) = irq; + } + + printk("cpu %d spinlock event irq %d\n", cpu, irq); +} + +void xen_uninit_lock_cpu(int cpu) +{ + unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL); +} + +void __init xen_init_spinlocks(void) +{ + pv_lock_ops.spin_is_locked = xen_spin_is_locked; + pv_lock_ops.spin_is_contended = xen_spin_is_contended; + pv_lock_ops.spin_lock = xen_spin_lock; + pv_lock_ops.spin_lock_flags = xen_spin_lock_flags; + pv_lock_ops.spin_trylock = xen_spin_trylock; + pv_lock_ops.spin_unlock = xen_spin_unlock; +} + +#ifdef CONFIG_XEN_DEBUG_FS + +static struct dentry *d_spin_debug; + +static int __init xen_spinlock_debugfs(void) +{ + struct dentry *d_xen = xen_init_debugfs(); + + if (d_xen == NULL) + return -ENOMEM; + + d_spin_debug = debugfs_create_dir("spinlocks", d_xen); + + debugfs_create_u8("zero_stats", 0644, d_spin_debug, &zero_stats); + + debugfs_create_u32("timeout", 0644, d_spin_debug, &lock_timeout); + + debugfs_create_u64("taken", 0444, d_spin_debug, &spinlock_stats.taken); + debugfs_create_u32("taken_slow", 0444, d_spin_debug, + &spinlock_stats.taken_slow); + debugfs_create_u32("taken_slow_nested", 0444, d_spin_debug, + &spinlock_stats.taken_slow_nested); + debugfs_create_u32("taken_slow_pickup", 0444, d_spin_debug, + &spinlock_stats.taken_slow_pickup); + debugfs_create_u32("taken_slow_spurious", 0444, d_spin_debug, + &spinlock_stats.taken_slow_spurious); + debugfs_create_u32("taken_slow_irqenable", 0444, d_spin_debug, + &spinlock_stats.taken_slow_irqenable); + + debugfs_create_u64("released", 0444, d_spin_debug, &spinlock_stats.released); + debugfs_create_u32("released_slow", 0444, d_spin_debug, + &spinlock_stats.released_slow); + debugfs_create_u32("released_slow_kicked", 0444, d_spin_debug, + &spinlock_stats.released_slow_kicked); + + debugfs_create_u64("time_spinning", 0444, d_spin_debug, + &spinlock_stats.time_spinning); + debugfs_create_u64("time_blocked", 0444, d_spin_debug, + &spinlock_stats.time_blocked); + debugfs_create_u64("time_total", 0444, d_spin_debug, + &spinlock_stats.time_total); + + xen_debugfs_create_u32_array("histo_total", 0444, d_spin_debug, + spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1); + xen_debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug, + spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1); + xen_debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug, + spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1); + + return 0; +} +fs_initcall(xen_spinlock_debugfs); + +#endif /* CONFIG_XEN_DEBUG_FS */ diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index 685b77470fc..004ba86326a 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -30,8 +30,6 @@ #define TIMER_SLOP 100000 #define NS_PER_TICK (1000000000LL / HZ) -static cycle_t xen_clocksource_read(void); - /* runstate info updated by Xen */ static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); @@ -213,7 +211,7 @@ unsigned long xen_tsc_khz(void) return xen_khz; } -static cycle_t xen_clocksource_read(void) +cycle_t xen_clocksource_read(void) { struct pvclock_vcpu_time_info *src; cycle_t ret; @@ -452,6 +450,14 @@ void xen_setup_timer(int cpu) setup_runstate_info(cpu); } +void xen_teardown_timer(int cpu) +{ + struct clock_event_device *evt; + BUG_ON(cpu == 0); + evt = &per_cpu(xen_clock_events, cpu); + unbind_from_irqhandler(evt->irq, NULL); +} + void xen_setup_cpu_clockevents(void) { BUG_ON(preemptible()); diff --git a/arch/x86/xen/xen-asm_32.S b/arch/x86/xen/xen-asm_32.S index 2497a30f41d..42786f59d9c 100644 --- a/arch/x86/xen/xen-asm_32.S +++ b/arch/x86/xen/xen-asm_32.S @@ -298,7 +298,7 @@ check_events: push %eax push %ecx push %edx - call force_evtchn_callback + call xen_force_evtchn_callback pop %edx pop %ecx pop %eax diff --git a/arch/x86/xen/xen-asm_64.S b/arch/x86/xen/xen-asm_64.S index 7f58304fafb..05794c566e8 100644 --- a/arch/x86/xen/xen-asm_64.S +++ b/arch/x86/xen/xen-asm_64.S @@ -26,8 +26,15 @@ /* Pseudo-flag used for virtual NMI, which we don't implement yet */ #define XEN_EFLAGS_NMI 0x80000000 -#if 0 -#include <asm/percpu.h> +#if 1 +/* + x86-64 does not yet support direct access to percpu variables + via a segment override, so we just need to make sure this code + never gets used + */ +#define BUG ud2a +#define PER_CPU_VAR(var, off) 0xdeadbeef +#endif /* Enable events. This clears the event mask and tests the pending @@ -35,6 +42,8 @@ events, then enter the hypervisor to get them handled. */ ENTRY(xen_irq_enable_direct) + BUG + /* Unmask events */ movb $0, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) @@ -58,6 +67,8 @@ ENDPATCH(xen_irq_enable_direct) non-zero. */ ENTRY(xen_irq_disable_direct) + BUG + movb $1, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) ENDPATCH(xen_irq_disable_direct) ret @@ -74,6 +85,8 @@ ENDPATCH(xen_irq_disable_direct) Xen and x86 use opposite senses (mask vs enable). */ ENTRY(xen_save_fl_direct) + BUG + testb $0xff, PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) setz %ah addb %ah,%ah @@ -91,6 +104,8 @@ ENDPATCH(xen_save_fl_direct) if so. */ ENTRY(xen_restore_fl_direct) + BUG + testb $X86_EFLAGS_IF>>8, %ah setz PER_CPU_VAR(xen_vcpu_info, XEN_vcpu_info_mask) /* Preempt here doesn't matter because that will deal with @@ -122,7 +137,7 @@ check_events: push %r9 push %r10 push %r11 - call force_evtchn_callback + call xen_force_evtchn_callback pop %r11 pop %r10 pop %r9 @@ -133,7 +148,6 @@ check_events: pop %rcx pop %rax ret -#endif ENTRY(xen_adjust_exception_frame) mov 8+0(%rsp),%rcx diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index dd3c23152a2..d7422dc2a55 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -2,6 +2,7 @@ #define XEN_OPS_H #include <linux/init.h> +#include <linux/clocksource.h> #include <linux/irqreturn.h> #include <xen/xen-ops.h> @@ -31,7 +32,10 @@ void xen_vcpu_restore(void); void __init xen_build_dynamic_phys_to_machine(void); +void xen_init_irq_ops(void); void xen_setup_timer(int cpu); +void xen_teardown_timer(int cpu); +cycle_t xen_clocksource_read(void); void xen_setup_cpu_clockevents(void); unsigned long xen_tsc_khz(void); void __init xen_time_init(void); @@ -50,6 +54,10 @@ void __init xen_setup_vcpu_info_placement(void); #ifdef CONFIG_SMP void xen_smp_init(void); +void __init xen_init_spinlocks(void); +__cpuinit void xen_init_lock_cpu(int cpu); +void xen_uninit_lock_cpu(int cpu); + extern cpumask_t xen_cpu_initialized_map; #else static inline void xen_smp_init(void) {} diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 12cf5d491f0..da49b006bcc 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -259,7 +259,7 @@ config ACPI_ASUS config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" - depends on X86 + depends on X86 && INPUT select INPUT_POLLDEV select NET select RFKILL diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 8dd3336efd7..3c578ef78c4 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -369,7 +369,6 @@ static int __init acpi_rtc_init(void) DBG("RTC unavailable?\n"); return 0; } -/* do this between RTC subsys_initcall() and rtc_cmos driver_initcall() */ -fs_initcall(acpi_rtc_init); +module_init(acpi_rtc_init); #endif diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index f17cd4b572f..78fbec8ceda 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -7,7 +7,6 @@ menuconfig ATA depends on HAS_IOMEM depends on BLOCK depends on !(M32R || M68K) || BROKEN - depends on !SUN4 || BROKEN select SCSI ---help--- If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index 73338d231db..937c9c0ef4c 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -47,8 +47,9 @@ #include <asm/atomic.h> #ifdef CONFIG_SBUS +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/idprom.h> -#include <asm/sbus.h> #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/pgtable.h> @@ -661,249 +662,189 @@ fore200e_pca_proc_read(struct fore200e* fore200e, char *page) #ifdef CONFIG_SBUS -static u32 -fore200e_sba_read(volatile u32 __iomem *addr) +static u32 fore200e_sba_read(volatile u32 __iomem *addr) { return sbus_readl(addr); } - -static void -fore200e_sba_write(u32 val, volatile u32 __iomem *addr) +static void fore200e_sba_write(u32 val, volatile u32 __iomem *addr) { sbus_writel(val, addr); } - -static u32 -fore200e_sba_dma_map(struct fore200e* fore200e, void* virt_addr, int size, int direction) +static u32 fore200e_sba_dma_map(struct fore200e *fore200e, void* virt_addr, int size, int direction) { - u32 dma_addr = sbus_map_single((struct sbus_dev*)fore200e->bus_dev, virt_addr, size, direction); + struct of_device *op = fore200e->bus_dev; + u32 dma_addr; - DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d --> dma_addr = 0x%08x\n", - virt_addr, size, direction, dma_addr); + dma_addr = dma_map_single(&op->dev, virt_addr, size, direction); + + DPRINTK(3, "SBUS DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d --> dma_addr = 0x%08x\n", + virt_addr, size, direction, dma_addr); - return dma_addr; + return dma_addr; } - -static void -fore200e_sba_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size, int direction) +static void fore200e_sba_dma_unmap(struct fore200e *fore200e, u32 dma_addr, int size, int direction) { - DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n", - dma_addr, size, direction); + struct of_device *op = fore200e->bus_dev; - sbus_unmap_single((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction); -} + DPRINTK(3, "SBUS DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d,\n", + dma_addr, size, direction); + dma_unmap_single(&op->dev, dma_addr, size, direction); +} -static void -fore200e_sba_dma_sync_for_cpu(struct fore200e* fore200e, u32 dma_addr, int size, int direction) +static void fore200e_sba_dma_sync_for_cpu(struct fore200e *fore200e, u32 dma_addr, int size, int direction) { - DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); + struct of_device *op = fore200e->bus_dev; + + DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); - sbus_dma_sync_single_for_cpu((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction); + dma_sync_single_for_cpu(&op->dev, dma_addr, size, direction); } -static void -fore200e_sba_dma_sync_for_device(struct fore200e* fore200e, u32 dma_addr, int size, int direction) +static void fore200e_sba_dma_sync_for_device(struct fore200e *fore200e, u32 dma_addr, int size, int direction) { - DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); - - sbus_dma_sync_single_for_device((struct sbus_dev*)fore200e->bus_dev, dma_addr, size, direction); -} + struct of_device *op = fore200e->bus_dev; + DPRINTK(3, "SBUS DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); -/* allocate a DVMA consistent chunk of memory intended to act as a communication mechanism - (to hold descriptors, status, queues, etc.) shared by the driver and the adapter */ + dma_sync_single_for_device(&op->dev, dma_addr, size, direction); +} -static int -fore200e_sba_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, - int size, int nbr, int alignment) +/* Allocate a DVMA consistent chunk of memory intended to act as a communication mechanism + * (to hold descriptors, status, queues, etc.) shared by the driver and the adapter. + */ +static int fore200e_sba_dma_chunk_alloc(struct fore200e *fore200e, struct chunk *chunk, + int size, int nbr, int alignment) { - chunk->alloc_size = chunk->align_size = size * nbr; + struct of_device *op = fore200e->bus_dev; - /* returned chunks are page-aligned */ - chunk->alloc_addr = sbus_alloc_consistent((struct sbus_dev*)fore200e->bus_dev, - chunk->alloc_size, - &chunk->dma_addr); + chunk->alloc_size = chunk->align_size = size * nbr; - if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0)) - return -ENOMEM; + /* returned chunks are page-aligned */ + chunk->alloc_addr = dma_alloc_coherent(&op->dev, chunk->alloc_size, + &chunk->dma_addr, GFP_ATOMIC); - chunk->align_addr = chunk->alloc_addr; + if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0)) + return -ENOMEM; + + chunk->align_addr = chunk->alloc_addr; - return 0; + return 0; } - /* free a DVMA consistent chunk of memory */ - -static void -fore200e_sba_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk) +static void fore200e_sba_dma_chunk_free(struct fore200e *fore200e, struct chunk *chunk) { - sbus_free_consistent((struct sbus_dev*)fore200e->bus_dev, - chunk->alloc_size, - chunk->alloc_addr, - chunk->dma_addr); -} + struct of_device *op = fore200e->bus_dev; + dma_free_coherent(&op->dev, chunk->alloc_size, + chunk->alloc_addr, chunk->dma_addr); +} -static void -fore200e_sba_irq_enable(struct fore200e* fore200e) +static void fore200e_sba_irq_enable(struct fore200e *fore200e) { - u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY; - fore200e->bus->write(hcr | SBA200E_HCR_INTR_ENA, fore200e->regs.sba.hcr); + u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY; + fore200e->bus->write(hcr | SBA200E_HCR_INTR_ENA, fore200e->regs.sba.hcr); } - -static int -fore200e_sba_irq_check(struct fore200e* fore200e) +static int fore200e_sba_irq_check(struct fore200e *fore200e) { - return fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_INTR_REQ; + return fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_INTR_REQ; } - -static void -fore200e_sba_irq_ack(struct fore200e* fore200e) +static void fore200e_sba_irq_ack(struct fore200e *fore200e) { - u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY; - fore200e->bus->write(hcr | SBA200E_HCR_INTR_CLR, fore200e->regs.sba.hcr); + u32 hcr = fore200e->bus->read(fore200e->regs.sba.hcr) & SBA200E_HCR_STICKY; + fore200e->bus->write(hcr | SBA200E_HCR_INTR_CLR, fore200e->regs.sba.hcr); } - -static void -fore200e_sba_reset(struct fore200e* fore200e) +static void fore200e_sba_reset(struct fore200e *fore200e) { - fore200e->bus->write(SBA200E_HCR_RESET, fore200e->regs.sba.hcr); - fore200e_spin(10); - fore200e->bus->write(0, fore200e->regs.sba.hcr); + fore200e->bus->write(SBA200E_HCR_RESET, fore200e->regs.sba.hcr); + fore200e_spin(10); + fore200e->bus->write(0, fore200e->regs.sba.hcr); } - -static int __init -fore200e_sba_map(struct fore200e* fore200e) +static int __init fore200e_sba_map(struct fore200e *fore200e) { - struct sbus_dev* sbus_dev = (struct sbus_dev*)fore200e->bus_dev; - unsigned int bursts; + struct of_device *op = fore200e->bus_dev; + unsigned int bursts; - /* gain access to the SBA specific registers */ - fore200e->regs.sba.hcr = sbus_ioremap(&sbus_dev->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR"); - fore200e->regs.sba.bsr = sbus_ioremap(&sbus_dev->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR"); - fore200e->regs.sba.isr = sbus_ioremap(&sbus_dev->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR"); - fore200e->virt_base = sbus_ioremap(&sbus_dev->resource[3], 0, SBA200E_RAM_LENGTH, "SBA RAM"); + /* gain access to the SBA specific registers */ + fore200e->regs.sba.hcr = of_ioremap(&op->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR"); + fore200e->regs.sba.bsr = of_ioremap(&op->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR"); + fore200e->regs.sba.isr = of_ioremap(&op->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR"); + fore200e->virt_base = of_ioremap(&op->resource[3], 0, SBA200E_RAM_LENGTH, "SBA RAM"); - if (fore200e->virt_base == NULL) { - printk(FORE200E "unable to map RAM of device %s\n", fore200e->name); - return -EFAULT; - } + if (!fore200e->virt_base) { + printk(FORE200E "unable to map RAM of device %s\n", fore200e->name); + return -EFAULT; + } - DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base); + DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base); - fore200e->bus->write(0x02, fore200e->regs.sba.isr); /* XXX hardwired interrupt level */ + fore200e->bus->write(0x02, fore200e->regs.sba.isr); /* XXX hardwired interrupt level */ - /* get the supported DVMA burst sizes */ - bursts = prom_getintdefault(sbus_dev->bus->prom_node, "burst-sizes", 0x00); + /* get the supported DVMA burst sizes */ + bursts = of_getintprop_default(op->node->parent, "burst-sizes", 0x00); - if (sbus_can_dma_64bit(sbus_dev)) - sbus_set_sbus64(sbus_dev, bursts); + if (sbus_can_dma_64bit()) + sbus_set_sbus64(&op->dev, bursts); - fore200e->state = FORE200E_STATE_MAP; - return 0; + fore200e->state = FORE200E_STATE_MAP; + return 0; } - -static void -fore200e_sba_unmap(struct fore200e* fore200e) +static void fore200e_sba_unmap(struct fore200e *fore200e) { - sbus_iounmap(fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH); - sbus_iounmap(fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH); - sbus_iounmap(fore200e->regs.sba.isr, SBA200E_ISR_LENGTH); - sbus_iounmap(fore200e->virt_base, SBA200E_RAM_LENGTH); -} + struct of_device *op = fore200e->bus_dev; + of_iounmap(&op->resource[0], fore200e->regs.sba.hcr, SBA200E_HCR_LENGTH); + of_iounmap(&op->resource[1], fore200e->regs.sba.bsr, SBA200E_BSR_LENGTH); + of_iounmap(&op->resource[2], fore200e->regs.sba.isr, SBA200E_ISR_LENGTH); + of_iounmap(&op->resource[3], fore200e->virt_base, SBA200E_RAM_LENGTH); +} -static int __init -fore200e_sba_configure(struct fore200e* fore200e) +static int __init fore200e_sba_configure(struct fore200e *fore200e) { - fore200e->state = FORE200E_STATE_CONFIGURE; - return 0; + fore200e->state = FORE200E_STATE_CONFIGURE; + return 0; } - -static struct fore200e* __init -fore200e_sba_detect(const struct fore200e_bus* bus, int index) +static int __init fore200e_sba_prom_read(struct fore200e *fore200e, struct prom_data *prom) { - struct fore200e* fore200e; - struct sbus_bus* sbus_bus; - struct sbus_dev* sbus_dev = NULL; - - unsigned int count = 0; - - for_each_sbus (sbus_bus) { - for_each_sbusdev (sbus_dev, sbus_bus) { - if (strcmp(sbus_dev->prom_name, SBA200E_PROM_NAME) == 0) { - if (count >= index) - goto found; - count++; - } - } - } - return NULL; - - found: - if (sbus_dev->num_registers != 4) { - printk(FORE200E "this %s device has %d instead of 4 registers\n", - bus->model_name, sbus_dev->num_registers); - return NULL; - } - - fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL); - if (fore200e == NULL) - return NULL; + struct of_device *op = fore200e->bus_dev; + const u8 *prop; + int len; - fore200e->bus = bus; - fore200e->bus_dev = sbus_dev; - fore200e->irq = sbus_dev->irqs[ 0 ]; + prop = of_get_property(op->node, "madaddrlo2", &len); + if (!prop) + return -ENODEV; + memcpy(&prom->mac_addr[4], prop, 4); - fore200e->phys_base = (unsigned long)sbus_dev; + prop = of_get_property(op->node, "madaddrhi4", &len); + if (!prop) + return -ENODEV; + memcpy(&prom->mac_addr[2], prop, 4); - sprintf(fore200e->name, "%s-%d", bus->model_name, index - 1); + prom->serial_number = of_getintprop_default(op->node, "serialnumber", 0); + prom->hw_revision = of_getintprop_default(op->node, "promversion", 0); - return fore200e; + return 0; } - -static int __init -fore200e_sba_prom_read(struct fore200e* fore200e, struct prom_data* prom) +static int fore200e_sba_proc_read(struct fore200e *fore200e, char *page) { - struct sbus_dev* sbus_dev = (struct sbus_dev*) fore200e->bus_dev; - int len; - - len = prom_getproperty(sbus_dev->prom_node, "macaddrlo2", &prom->mac_addr[ 4 ], 4); - if (len < 0) - return -EBUSY; - - len = prom_getproperty(sbus_dev->prom_node, "macaddrhi4", &prom->mac_addr[ 2 ], 4); - if (len < 0) - return -EBUSY; - - prom_getproperty(sbus_dev->prom_node, "serialnumber", - (char*)&prom->serial_number, sizeof(prom->serial_number)); - - prom_getproperty(sbus_dev->prom_node, "promversion", - (char*)&prom->hw_revision, sizeof(prom->hw_revision)); - - return 0; -} + struct of_device *op = fore200e->bus_dev; + const struct linux_prom_registers *regs; + regs = of_get_property(op->node, "reg", NULL); -static int -fore200e_sba_proc_read(struct fore200e* fore200e, char *page) -{ - struct sbus_dev* sbus_dev = (struct sbus_dev*)fore200e->bus_dev; - - return sprintf(page, " SBUS slot/device:\t\t%d/'%s'\n", sbus_dev->slot, sbus_dev->prom_name); + return sprintf(page, " SBUS slot/device:\t\t%d/'%s'\n", + (regs ? regs->which_io : 0), op->node->name); } #endif /* CONFIG_SBUS */ @@ -2572,7 +2513,7 @@ fore200e_load_and_start_fw(struct fore200e* fore200e) device = &((struct pci_dev *) fore200e->bus_dev)->dev; #ifdef CONFIG_SBUS else if (strcmp(fore200e->bus->model_name, "SBA-200E") == 0) - device = &((struct sbus_dev *) fore200e->bus_dev)->ofdev.dev; + device = &((struct of_device *) fore200e->bus_dev)->dev; #endif else return err; @@ -2701,6 +2642,66 @@ fore200e_init(struct fore200e* fore200e) return 0; } +#ifdef CONFIG_SBUS +static int __devinit fore200e_sba_probe(struct of_device *op, + const struct of_device_id *match) +{ + const struct fore200e_bus *bus = match->data; + struct fore200e *fore200e; + static int index = 0; + int err; + + fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL); + if (!fore200e) + return -ENOMEM; + + fore200e->bus = bus; + fore200e->bus_dev = op; + fore200e->irq = op->irqs[0]; + fore200e->phys_base = op->resource[0].start; + + sprintf(fore200e->name, "%s-%d", bus->model_name, index); + + err = fore200e_init(fore200e); + if (err < 0) { + fore200e_shutdown(fore200e); + kfree(fore200e); + return err; + } + + index++; + dev_set_drvdata(&op->dev, fore200e); + + return 0; +} + +static int __devexit fore200e_sba_remove(struct of_device *op) +{ + struct fore200e *fore200e = dev_get_drvdata(&op->dev); + + fore200e_shutdown(fore200e); + kfree(fore200e); + + return 0; +} + +static const struct of_device_id fore200e_sba_match[] = { + { + .name = SBA200E_PROM_NAME, + .data = (void *) &fore200e_bus[1], + }, + {}, +}; +MODULE_DEVICE_TABLE(of, fore200e_sba_match); + +static struct of_platform_driver fore200e_sba_driver = { + .name = "fore_200e", + .match_table = fore200e_sba_match, + .probe = fore200e_sba_probe, + .remove = __devexit_p(fore200e_sba_remove), +}; +#endif + #ifdef CONFIG_PCI static int __devinit fore200e_pca_detect(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent) @@ -2784,67 +2785,40 @@ static struct pci_driver fore200e_pca_driver = { }; #endif - -static int __init -fore200e_module_init(void) +static int __init fore200e_module_init(void) { - const struct fore200e_bus* bus; - struct fore200e* fore200e; - int index; - - printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n"); + int err; - /* for each configured bus interface */ - for (bus = fore200e_bus; bus->model_name; bus++) { + printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n"); - /* detect all boards present on that bus */ - for (index = 0; bus->detect && (fore200e = bus->detect(bus, index)); index++) { - - printk(FORE200E "device %s found at 0x%lx, IRQ %s\n", - fore200e->bus->model_name, - fore200e->phys_base, fore200e_irq_itoa(fore200e->irq)); - - sprintf(fore200e->name, "%s-%d", bus->model_name, index); - - if (fore200e_init(fore200e) < 0) { - - fore200e_shutdown(fore200e); - break; - } - - list_add(&fore200e->entry, &fore200e_boards); - } - } +#ifdef CONFIG_SBUS + err = of_register_driver(&fore200e_sba_driver, &of_bus_type); + if (err) + return err; +#endif #ifdef CONFIG_PCI - if (!pci_register_driver(&fore200e_pca_driver)) - return 0; + err = pci_register_driver(&fore200e_pca_driver); #endif - if (!list_empty(&fore200e_boards)) - return 0; +#ifdef CONFIG_SBUS + if (err) + of_unregister_driver(&fore200e_sba_driver); +#endif - return -ENODEV; + return err; } - -static void __exit -fore200e_module_cleanup(void) +static void __exit fore200e_module_cleanup(void) { - struct fore200e *fore200e, *next; - #ifdef CONFIG_PCI - pci_unregister_driver(&fore200e_pca_driver); + pci_unregister_driver(&fore200e_pca_driver); +#endif +#ifdef CONFIG_SBUS + of_unregister_driver(&fore200e_sba_driver); #endif - - list_for_each_entry_safe(fore200e, next, &fore200e_boards, entry) { - fore200e_shutdown(fore200e); - kfree(fore200e); - } - DPRINTK(1, "module being removed\n"); } - static int fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page) { @@ -3163,7 +3137,6 @@ static const struct fore200e_bus fore200e_bus[] = { fore200e_pca_dma_sync_for_device, fore200e_pca_dma_chunk_alloc, fore200e_pca_dma_chunk_free, - NULL, fore200e_pca_configure, fore200e_pca_map, fore200e_pca_reset, @@ -3185,7 +3158,6 @@ static const struct fore200e_bus fore200e_bus[] = { fore200e_sba_dma_sync_for_device, fore200e_sba_dma_chunk_alloc, fore200e_sba_dma_chunk_free, - fore200e_sba_detect, fore200e_sba_configure, fore200e_sba_map, fore200e_sba_reset, diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h index 5c6e7adcb19..7f97c09aaea 100644 --- a/drivers/atm/fore200e.h +++ b/drivers/atm/fore200e.h @@ -778,9 +778,9 @@ typedef struct fore200e_pca_regs { /* SBA-200E registers */ typedef struct fore200e_sba_regs { - volatile u32 __iomem *hcr; /* address of host control register */ - volatile u32 __iomem *bsr; /* address of burst transfer size register */ - volatile u32 __iomem *isr; /* address of interrupt level selection register */ + u32 __iomem *hcr; /* address of host control register */ + u32 __iomem *bsr; /* address of burst transfer size register */ + u32 __iomem *isr; /* address of interrupt level selection register */ } fore200e_sba_regs_t; @@ -810,7 +810,6 @@ typedef struct fore200e_bus { void (*dma_sync_for_device)(struct fore200e*, u32, int, int); int (*dma_chunk_alloc)(struct fore200e*, struct chunk*, int, int, int); void (*dma_chunk_free)(struct fore200e*, struct chunk*); - struct fore200e* (*detect)(const struct fore200e_bus*, int); int (*configure)(struct fore200e*); int (*map)(struct fore200e*); void (*reset)(struct fore200e*); diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index a8de037ecd4..953c0b83d75 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -1,6 +1,6 @@ /* sunvdc.c: Sun LDOM Virtual Disk Client. * - * Copyright (C) 2007 David S. Miller <davem@davemloft.net> + * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/module.h> @@ -834,7 +834,7 @@ static int vdc_port_remove(struct vio_dev *vdev) return 0; } -static struct vio_device_id vdc_port_match[] = { +static const struct vio_device_id vdc_port_match[] = { { .type = "vdc-port", }, diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index bff602ccccf..1a50ae70f71 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -1066,7 +1066,7 @@ static struct xenbus_driver blkfront = { static int __init xlblk_init(void) { - if (!is_running_on_xen()) + if (!xen_domain()) return -ENODEV; if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) { diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 8dfcf77cb71..4426bb552bd 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -484,7 +484,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, return -EUNATCH; default: - err = n_tty_ioctl(tty, file, cmd, arg); + err = n_tty_ioctl_helper(tty, file, cmd, arg); break; }; diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index caff85149b9..700ff967945 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -350,7 +350,7 @@ config STALDRV config STALLION tristate "Stallion EasyIO or EC8/32 support" - depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) + depends on STALDRV && (ISA || EISA || PCI) help If you have an EasyIO or EasyConnection 8/32 multiport Stallion card, then this is for you; say Y. Make sure to read @@ -361,7 +361,7 @@ config STALLION config ISTALLION tristate "Stallion EC8/64, ONboard, Brumby support" - depends on STALDRV && BROKEN_ON_SMP && (ISA || EISA || PCI) + depends on STALDRV && (ISA || EISA || PCI) help If you have an EasyConnection 8/64, ONboard, Brumby or Stallion serial multiport card, say Y here. Make sure to read diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 6850f6da757..1a4247dccac 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -7,7 +7,7 @@ # FONTMAPFILE = cp437.uni -obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o +obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o obj-$(CONFIG_LEGACY_PTYS) += pty.o obj-$(CONFIG_UNIX98_PTYS) += pty.o diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c index 6e763e3f5a8..98821f97583 100644 --- a/drivers/char/amiserial.c +++ b/drivers/char/amiserial.c @@ -837,9 +837,6 @@ static int rs_put_char(struct tty_struct *tty, unsigned char ch) struct async_struct *info; unsigned long flags; - if (!tty) - return 0; - info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_put_char")) @@ -892,9 +889,6 @@ static int rs_write(struct tty_struct * tty, const unsigned char *buf, int count struct async_struct *info; unsigned long flags; - if (!tty) - return 0; - info = tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_write")) diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 31d08b641f5..b899d9182c7 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -712,8 +712,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un IndexCard = adgl->num_card-1; - if(cmd != 0 && cmd != 6 && - ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { + if(cmd != 6 && ((IndexCard >= MAX_BOARD) || !apbs[IndexCard].RamIO)) { static int warncount = 10; if (warncount) { printk( KERN_WARNING "APPLICOM driver IOCTL, bad board number %d\n",(int)IndexCard+1); @@ -832,8 +831,7 @@ static int ac_ioctl(struct inode *inode, struct file *file, unsigned int cmd, un } break; default: - printk(KERN_INFO "APPLICOM driver ioctl, unknown function code %d\n",cmd) ; - ret = -EINVAL; + ret = -ENOTTY; break; } Dummy = readb(apbs[IndexCard].RamIO + VERS); diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index fe6d774fe2e..5e5b1dc1a0a 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -4993,12 +4993,14 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) { card_name = "Cyclom-Y"; - addr0 = pci_iomap(pdev, 0, CyPCI_Yctl); + addr0 = ioremap_nocache(pci_resource_start(pdev, 0), + CyPCI_Yctl); if (addr0 == NULL) { dev_err(&pdev->dev, "can't remap ctl region\n"); goto err_reg; } - addr2 = pci_iomap(pdev, 2, CyPCI_Ywin); + addr2 = ioremap_nocache(pci_resource_start(pdev, 2), + CyPCI_Ywin); if (addr2 == NULL) { dev_err(&pdev->dev, "can't remap base region\n"); goto err_unmap; @@ -5013,7 +5015,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) { struct RUNTIME_9060 __iomem *ctl_addr; - ctl_addr = addr0 = pci_iomap(pdev, 0, CyPCI_Zctl); + ctl_addr = addr0 = ioremap_nocache(pci_resource_start(pdev, 0), + CyPCI_Zctl); if (addr0 == NULL) { dev_err(&pdev->dev, "can't remap ctl region\n"); goto err_reg; @@ -5026,8 +5029,8 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, mailbox = (u32)readl(&ctl_addr->mail_box_0); - addr2 = pci_iomap(pdev, 2, mailbox == ZE_V1 ? - CyPCI_Ze_win : CyPCI_Zwin); + addr2 = ioremap_nocache(pci_resource_start(pdev, 2), + mailbox == ZE_V1 ? CyPCI_Ze_win : CyPCI_Zwin); if (addr2 == NULL) { dev_err(&pdev->dev, "can't remap base region\n"); goto err_unmap; @@ -5159,9 +5162,9 @@ err_null: cy_card[card_no].base_addr = NULL; free_irq(irq, &cy_card[card_no]); err_unmap: - pci_iounmap(pdev, addr0); + iounmap(addr0); if (addr2) - pci_iounmap(pdev, addr2); + iounmap(addr2); err_reg: pci_release_regions(pdev); err_dis: @@ -5186,9 +5189,9 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev) cy_writew(cinfo->ctl_addr + 0x68, readw(cinfo->ctl_addr + 0x68) & ~0x0900); - pci_iounmap(pdev, cinfo->base_addr); + iounmap(cinfo->base_addr); if (cinfo->ctl_addr) - pci_iounmap(pdev, cinfo->ctl_addr); + iounmap(cinfo->ctl_addr); if (cinfo->irq #ifndef CONFIG_CYZ_INTR && !IS_CYC_Z(*cinfo) diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 456e4ede049..4998b2761e8 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -1376,6 +1376,7 @@ static void post_fep_init(unsigned int crd) unsigned long flags; u16 tseg, rseg; + tty_port_init(&ch->port); ch->brdchan = bc; ch->mailbox = gd; INIT_WORK(&ch->tqueue, do_softint); @@ -1510,10 +1511,6 @@ static void post_fep_init(unsigned int crd) ch->fepstopca = 0; ch->close_delay = 50; - ch->port.count = 0; - ch->port.blocked_open = 0; - init_waitqueue_head(&ch->port.open_wait); - init_waitqueue_head(&ch->port.close_wait); spin_unlock_irqrestore(&epca_lock, flags); } diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 19d3afb0e50..c6090f84a2e 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -54,8 +54,6 @@ int gs_put_char(struct tty_struct * tty, unsigned char ch) func_enter (); - if (!tty) return 0; - port = tty->driver_data; if (!port) return 0; @@ -97,8 +95,6 @@ int gs_write(struct tty_struct * tty, func_enter (); - if (!tty) return 0; - port = tty->driver_data; if (!port) return 0; @@ -185,7 +181,6 @@ static int gs_real_chars_in_buffer(struct tty_struct *tty) struct gs_port *port; func_enter (); - if (!tty) return 0; port = tty->driver_data; if (!port->rd) return 0; @@ -274,8 +269,6 @@ void gs_flush_buffer(struct tty_struct *tty) func_enter (); - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -296,8 +289,6 @@ void gs_flush_chars(struct tty_struct * tty) func_enter (); - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -321,8 +312,6 @@ void gs_stop(struct tty_struct * tty) func_enter (); - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -341,8 +330,6 @@ void gs_start(struct tty_struct * tty) { struct gs_port *port; - if (!tty) return; - port = tty->driver_data; if (!port) return; @@ -393,8 +380,6 @@ void gs_hangup(struct tty_struct *tty) func_enter (); - if (!tty) return; - port = tty->driver_data; tty = port->port.tty; if (!tty) @@ -426,8 +411,6 @@ int gs_block_til_ready(void *port_, struct file * filp) tty = port->port.tty; - if (!tty) return 0; - gs_dprintk (GS_DEBUG_BTR, "Entering gs_block_till_ready.\n"); /* * If the device is in the middle of being closed, then block @@ -523,8 +506,6 @@ void gs_close(struct tty_struct * tty, struct file * filp) func_enter (); - if (!tty) return; - port = (struct gs_port *) tty->driver_data; if (!port) return; @@ -621,8 +602,6 @@ void gs_set_termios (struct tty_struct * tty, func_enter(); - if (!tty) return; - port = tty->driver_data; if (!port) return; diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index b3f5dbc6d88..f3cfb4c7612 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -53,6 +53,11 @@ #define HPET_RANGE_SIZE 1024 /* from HPET spec */ + +/* WARNING -- don't get confused. These macros are never used + * to write the (single) counter, and rarely to read it. + * They're badly named; to fix, someday. + */ #if BITS_PER_LONG == 64 #define write_counter(V, MC) writeq(V, MC) #define read_counter(MC) readq(MC) @@ -77,7 +82,7 @@ static struct clocksource clocksource_hpet = { .rating = 250, .read = read_hpet, .mask = CLOCKSOURCE_MASK(64), - .mult = 0, /*to be caluclated*/ + .mult = 0, /* to be calculated */ .shift = 10, .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; @@ -86,8 +91,6 @@ static struct clocksource *hpet_clocksource; /* A lock for concurrent access by app and isr hpet activity. */ static DEFINE_SPINLOCK(hpet_lock); -/* A lock for concurrent intermodule access to hpet and isr hpet activity. */ -static DEFINE_SPINLOCK(hpet_task_lock); #define HPET_DEV_NAME (7) @@ -99,7 +102,6 @@ struct hpet_dev { unsigned long hd_irqdata; wait_queue_head_t hd_waitqueue; struct fasync_struct *hd_async_queue; - struct hpet_task *hd_task; unsigned int hd_flags; unsigned int hd_irq; unsigned int hd_hdwirq; @@ -173,11 +175,6 @@ static irqreturn_t hpet_interrupt(int irq, void *data) writel(isr, &devp->hd_hpet->hpet_isr); spin_unlock(&hpet_lock); - spin_lock(&hpet_task_lock); - if (devp->hd_task) - devp->hd_task->ht_func(devp->hd_task->ht_data); - spin_unlock(&hpet_task_lock); - wake_up_interruptible(&devp->hd_waitqueue); kill_fasync(&devp->hd_async_queue, SIGIO, POLL_IN); @@ -185,6 +182,67 @@ static irqreturn_t hpet_interrupt(int irq, void *data) return IRQ_HANDLED; } +static void hpet_timer_set_irq(struct hpet_dev *devp) +{ + unsigned long v; + int irq, gsi; + struct hpet_timer __iomem *timer; + + spin_lock_irq(&hpet_lock); + if (devp->hd_hdwirq) { + spin_unlock_irq(&hpet_lock); + return; + } + + timer = devp->hd_timer; + + /* we prefer level triggered mode */ + v = readl(&timer->hpet_config); + if (!(v & Tn_INT_TYPE_CNF_MASK)) { + v |= Tn_INT_TYPE_CNF_MASK; + writel(v, &timer->hpet_config); + } + spin_unlock_irq(&hpet_lock); + + v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >> + Tn_INT_ROUTE_CAP_SHIFT; + + /* + * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by + * legacy device. In IO APIC mode, we skip all the legacy IRQS. + */ + if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) + v &= ~0xf3df; + else + v &= ~0xffff; + + for (irq = find_first_bit(&v, HPET_MAX_IRQ); irq < HPET_MAX_IRQ; + irq = find_next_bit(&v, HPET_MAX_IRQ, 1 + irq)) { + + if (irq >= NR_IRQS) { + irq = HPET_MAX_IRQ; + break; + } + + gsi = acpi_register_gsi(irq, ACPI_LEVEL_SENSITIVE, + ACPI_ACTIVE_LOW); + if (gsi > 0) + break; + + /* FIXME: Setup interrupt source table */ + } + + if (irq < HPET_MAX_IRQ) { + spin_lock_irq(&hpet_lock); + v = readl(&timer->hpet_config); + v |= irq << Tn_INT_ROUTE_CNF_SHIFT; + writel(v, &timer->hpet_config); + devp->hd_hdwirq = gsi; + spin_unlock_irq(&hpet_lock); + } + return; +} + static int hpet_open(struct inode *inode, struct file *file) { struct hpet_dev *devp; @@ -199,8 +257,7 @@ static int hpet_open(struct inode *inode, struct file *file) for (devp = NULL, hpetp = hpets; hpetp && !devp; hpetp = hpetp->hp_next) for (i = 0; i < hpetp->hp_ntimer; i++) - if (hpetp->hp_dev[i].hd_flags & HPET_OPEN - || hpetp->hp_dev[i].hd_task) + if (hpetp->hp_dev[i].hd_flags & HPET_OPEN) continue; else { devp = &hpetp->hp_dev[i]; @@ -219,6 +276,8 @@ static int hpet_open(struct inode *inode, struct file *file) spin_unlock_irq(&hpet_lock); unlock_kernel(); + hpet_timer_set_irq(devp); + return 0; } @@ -441,7 +500,11 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) devp->hd_irq = irq; t = devp->hd_ireqfreq; v = readq(&timer->hpet_config); - g = v | Tn_INT_ENB_CNF_MASK; + + /* 64-bit comparators are not yet supported through the ioctls, + * so force this into 32-bit mode if it supports both modes + */ + g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK; if (devp->hd_flags & HPET_PERIODIC) { write_counter(t, &timer->hpet_compare); @@ -451,6 +514,12 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) v |= Tn_VAL_SET_CNF_MASK; writeq(v, &timer->hpet_config); local_irq_save(flags); + + /* NOTE: what we modify here is a hidden accumulator + * register supported by periodic-capable comparators. + * We never want to modify the (single) counter; that + * would affect all the comparators. + */ m = read_counter(&hpet->hpet_mc); write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); } else { @@ -604,57 +673,6 @@ static int hpet_is_known(struct hpet_data *hdp) return 0; } -static inline int hpet_tpcheck(struct hpet_task *tp) -{ - struct hpet_dev *devp; - struct hpets *hpetp; - - devp = tp->ht_opaque; - - if (!devp) - return -ENXIO; - - for (hpetp = hpets; hpetp; hpetp = hpetp->hp_next) - if (devp >= hpetp->hp_dev - && devp < (hpetp->hp_dev + hpetp->hp_ntimer) - && devp->hd_hpet == hpetp->hp_hpet) - return 0; - - return -ENXIO; -} - -#if 0 -int hpet_unregister(struct hpet_task *tp) -{ - struct hpet_dev *devp; - struct hpet_timer __iomem *timer; - int err; - - if ((err = hpet_tpcheck(tp))) - return err; - - spin_lock_irq(&hpet_task_lock); - spin_lock(&hpet_lock); - - devp = tp->ht_opaque; - if (devp->hd_task != tp) { - spin_unlock(&hpet_lock); - spin_unlock_irq(&hpet_task_lock); - return -ENXIO; - } - - timer = devp->hd_timer; - writeq((readq(&timer->hpet_config) & ~Tn_INT_ENB_CNF_MASK), - &timer->hpet_config); - devp->hd_flags &= ~(HPET_IE | HPET_PERIODIC); - devp->hd_task = NULL; - spin_unlock(&hpet_lock); - spin_unlock_irq(&hpet_task_lock); - - return 0; -} -#endif /* 0 */ - static ctl_table hpet_table[] = { { .ctl_name = CTL_UNNUMBERED, @@ -746,6 +764,7 @@ int hpet_alloc(struct hpet_data *hdp) static struct hpets *last = NULL; unsigned long period; unsigned long long temp; + u32 remainder; /* * hpet_alloc can be called by platform dependent code. @@ -809,9 +828,13 @@ int hpet_alloc(struct hpet_data *hdp) printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]); printk("\n"); - printk(KERN_INFO "hpet%u: %u %d-bit timers, %Lu Hz\n", - hpetp->hp_which, hpetp->hp_ntimer, - cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, hpetp->hp_tick_freq); + temp = hpetp->hp_tick_freq; + remainder = do_div(temp, 1000000); + printk(KERN_INFO + "hpet%u: %u comparators, %d-bit %u.%06u MHz counter\n", + hpetp->hp_which, hpetp->hp_ntimer, + cap & HPET_COUNTER_SIZE_MASK ? 64 : 32, + (unsigned) temp, remainder); mcfg = readq(&hpet->hpet_config); if ((mcfg & HPET_ENABLE_CNF_MASK) == 0) { @@ -874,8 +897,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) hdp->hd_address = ioremap(addr.minimum, addr.address_length); if (hpet_is_known(hdp)) { - printk(KERN_DEBUG "%s: 0x%lx is busy\n", - __func__, hdp->hd_phys_address); iounmap(hdp->hd_address); return AE_ALREADY_EXISTS; } @@ -891,8 +912,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) HPET_RANGE_SIZE); if (hpet_is_known(hdp)) { - printk(KERN_DEBUG "%s: 0x%lx is busy\n", - __func__, hdp->hd_phys_address); iounmap(hdp->hd_address); return AE_ALREADY_EXISTS; } diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index fd64137b1ab..ec7aded0a2d 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -819,11 +819,11 @@ static int hvc_init(void) hvc_driver = drv; return 0; -put_tty: - put_tty_driver(hvc_driver); stop_thread: kthread_stop(hvc_task); hvc_task = NULL; +put_tty: + put_tty_driver(drv); out: return err; } diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c index 6b70aa66a58..538ceea5e7d 100644 --- a/drivers/char/hvc_xen.c +++ b/drivers/char/hvc_xen.c @@ -108,8 +108,8 @@ static int __init xen_init(void) { struct hvc_struct *hp; - if (!is_running_on_xen() || - is_initial_xendomain() || + if (!xen_pv_domain() || + xen_initial_domain() || !xen_start_info->console.domU.evtchn) return -ENODEV; @@ -142,7 +142,7 @@ static void __exit xen_fini(void) static int xen_cons_init(void) { - if (!is_running_on_xen()) + if (!xen_pv_domain()) return 0; hvc_instantiate(HVC_COOKIE, 0, &hvc_ops); diff --git a/drivers/char/hw_random/n2-drv.c b/drivers/char/hw_random/n2-drv.c index 5220f541df2..8859aeac2d2 100644 --- a/drivers/char/hw_random/n2-drv.c +++ b/drivers/char/hw_random/n2-drv.c @@ -736,7 +736,7 @@ static int __devexit n2rng_remove(struct of_device *op) return 0; } -static struct of_device_id n2rng_match[] = { +static const struct of_device_id n2rng_match[] = { { .name = "random-number-generator", .compatible = "SUNW,n2-rng", diff --git a/drivers/char/ip2/Makefile b/drivers/char/ip2/Makefile index 939618f62fe..bc397d92b49 100644 --- a/drivers/char/ip2/Makefile +++ b/drivers/char/ip2/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_COMPUTONE) += ip2.o -ip2-objs := ip2base.o ip2main.o +ip2-objs := ip2main.o diff --git a/drivers/char/ip2/i2ellis.c b/drivers/char/ip2/i2ellis.c index 3601017f58c..29db44de399 100644 --- a/drivers/char/ip2/i2ellis.c +++ b/drivers/char/ip2/i2ellis.c @@ -69,38 +69,6 @@ static DEFINE_RWLOCK(Dl_spinlock); //======================================================= //****************************************************************************** -// Function: iiEllisInit() -// Parameters: None -// -// Returns: Nothing -// -// Description: -// -// This routine performs any required initialization of the iiEllis subsystem. -// -//****************************************************************************** -static void -iiEllisInit(void) -{ -} - -//****************************************************************************** -// Function: iiEllisCleanup() -// Parameters: None -// -// Returns: Nothing -// -// Description: -// -// This routine performs any required cleanup of the iiEllis subsystem. -// -//****************************************************************************** -static void -iiEllisCleanup(void) -{ -} - -//****************************************************************************** // Function: iiSetAddress(pB, address, delay) // Parameters: pB - pointer to the board structure // address - the purported I/O address of the board diff --git a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h index c88a64e527a..fb6df245601 100644 --- a/drivers/char/ip2/i2ellis.h +++ b/drivers/char/ip2/i2ellis.h @@ -511,7 +511,6 @@ typedef void (*delayFunc_t)(unsigned int); // // Initialization of a board & structure is in four (five!) parts: // -// 0) iiEllisInit() - Initialize iiEllis subsystem. // 1) iiSetAddress() - Define the board address & delay function for a board. // 2) iiReset() - Reset the board (provided it exists) // -- Note you may do this to several boards -- @@ -523,7 +522,6 @@ typedef void (*delayFunc_t)(unsigned int); // loadware. To change loadware, you must begin again with step 2, resetting // the board again (step 1 not needed). -static void iiEllisInit(void); static int iiSetAddress(i2eBordStrPtr, int, delayFunc_t ); static int iiReset(i2eBordStrPtr); static int iiResetDelay(i2eBordStrPtr); diff --git a/drivers/char/ip2/ip2base.c b/drivers/char/ip2/ip2base.c deleted file mode 100644 index 8155e247c04..00000000000 --- a/drivers/char/ip2/ip2base.c +++ /dev/null @@ -1,108 +0,0 @@ -// ip2.c -// This is a dummy module to make the firmware available when needed -// and allows it to be unloaded when not. Rumor is the __initdata -// macro doesn't always works on all platforms so we use this kludge. -// If not compiled as a module it just makes fip_firm avaliable then -// __initdata should work as advertized -// - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/wait.h> - -#ifndef __init -#define __init -#endif -#ifndef __initfunc -#define __initfunc(a) a -#endif -#ifndef __initdata -#define __initdata -#endif - -#include "ip2types.h" - -int -ip2_loadmain(int *, int *); // ref into ip2main.c - -/* Note: Add compiled in defaults to these arrays, not to the structure - in ip2.h any longer. That structure WILL get overridden - by these values, or command line values, or insmod values!!! =mhw= -*/ -static int io[IP2_MAX_BOARDS]= { 0, 0, 0, 0 }; -static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 }; - -static int poll_only = 0; - -MODULE_AUTHOR("Doug McNash"); -MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); -module_param_array(irq, int, NULL, 0); -MODULE_PARM_DESC(irq,"Interrupts for IntelliPort Cards"); -module_param_array(io, int, NULL, 0); -MODULE_PARM_DESC(io,"I/O ports for IntelliPort Cards"); -module_param(poll_only, bool, 0); -MODULE_PARM_DESC(poll_only,"Do not use card interrupts"); - - -static int __init ip2_init(void) -{ - if( poll_only ) { - /* Hard lock the interrupts to zero */ - irq[0] = irq[1] = irq[2] = irq[3] = 0; - } - - return ip2_loadmain(io, irq); -} -module_init(ip2_init); - -MODULE_LICENSE("GPL"); - -#ifndef MODULE -/****************************************************************************** - * ip2_setup: - * str: kernel command line string - * - * Can't autoprobe the boards so user must specify configuration on - * kernel command line. Sane people build it modular but the others - * come here. - * - * Alternating pairs of io,irq for up to 4 boards. - * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3 - * - * io=0 => No board - * io=1 => PCI - * io=2 => EISA - * else => ISA I/O address - * - * irq=0 or invalid for ISA will revert to polling mode - * - * Any value = -1, do not overwrite compiled in value. - * - ******************************************************************************/ -static int __init ip2_setup(char *str) -{ - int ints[10]; /* 4 boards, 2 parameters + 2 */ - int i, j; - - str = get_options (str, ARRAY_SIZE(ints), ints); - - for( i = 0, j = 1; i < 4; i++ ) { - if( j > ints[0] ) { - break; - } - if( ints[j] >= 0 ) { - io[i] = ints[j]; - } - j++; - if( j > ints[0] ) { - break; - } - if( ints[j] >= 0 ) { - irq[i] = ints[j]; - } - j++; - } - return 1; -} -__setup("ip2=", ip2_setup); -#endif /* !MODULE */ diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c index 689f9dcd3b8..6774572d375 100644 --- a/drivers/char/ip2/ip2main.c +++ b/drivers/char/ip2/ip2main.c @@ -150,15 +150,12 @@ static int ip2_read_proc(char *, char **, off_t, int, int *, void * ); /*************/ /* String constants to identify ourselves */ -static char *pcName = "Computone IntelliPort Plus multiport driver"; -static char *pcVersion = "1.2.14"; +static const char pcName[] = "Computone IntelliPort Plus multiport driver"; +static const char pcVersion[] = "1.2.14"; /* String constants for port names */ -static char *pcDriver_name = "ip2"; -static char *pcIpl = "ip2ipl"; - -// cheezy kludge or genius - you decide? -int ip2_loadmain(int *, int *); +static const char pcDriver_name[] = "ip2"; +static const char pcIpl[] = "ip2ipl"; /***********************/ /* Function Prototypes */ @@ -240,8 +237,8 @@ static const struct file_operations ip2_ipl = { .open = ip2_ipl_open, }; -static unsigned long irq_counter = 0; -static unsigned long bh_counter = 0; +static unsigned long irq_counter; +static unsigned long bh_counter; // Use immediate queue to service interrupts #define USE_IQI @@ -252,7 +249,6 @@ static unsigned long bh_counter = 0; */ #define POLL_TIMEOUT (jiffies + 1) static DEFINE_TIMER(PollTimer, ip2_poll, 0, 0); -static char TimerOn; #ifdef IP2DEBUG_TRACE /* Trace (debug) buffer data */ @@ -268,8 +264,8 @@ static int tracewrap; /**********/ #if defined(MODULE) && defined(IP2DEBUG_OPEN) -#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, ttyc=%d, modc=%x -> %s\n", \ - tty->name,(pCh->flags),ip2_tty_driver->refcount, \ +#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] ttyc=%d, modc=%x -> %s\n", \ + tty->name,(pCh->flags), \ tty->count,/*GET_USE_COUNT(module)*/0,s) #else #define DBG_CNT(s) @@ -287,8 +283,9 @@ static int tracewrap; MODULE_AUTHOR("Doug McNash"); MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +MODULE_LICENSE("GPL"); -static int poll_only = 0; +static int poll_only; static int Eisa_irq; static int Eisa_slot; @@ -297,34 +294,46 @@ static int iindx; static char rirqs[IP2_MAX_BOARDS]; static int Valid_Irqs[] = { 3, 4, 5, 7, 10, 11, 12, 15, 0}; +/* Note: Add compiled in defaults to these arrays, not to the structure + in ip2.h any longer. That structure WILL get overridden + by these values, or command line values, or insmod values!!! =mhw= +*/ +static int io[IP2_MAX_BOARDS]; +static int irq[IP2_MAX_BOARDS] = { -1, -1, -1, -1 }; + +MODULE_AUTHOR("Doug McNash"); +MODULE_DESCRIPTION("Computone IntelliPort Plus Driver"); +module_param_array(irq, int, NULL, 0); +MODULE_PARM_DESC(irq, "Interrupts for IntelliPort Cards"); +module_param_array(io, int, NULL, 0); +MODULE_PARM_DESC(io, "I/O ports for IntelliPort Cards"); +module_param(poll_only, bool, 0); +MODULE_PARM_DESC(poll_only, "Do not use card interrupts"); + /* for sysfs class support */ static struct class *ip2_class; -// Some functions to keep track of what irq's we have +/* Some functions to keep track of what irqs we have */ -static int -is_valid_irq(int irq) +static int __init is_valid_irq(int irq) { int *i = Valid_Irqs; - while ((*i != 0) && (*i != irq)) { + while (*i != 0 && *i != irq) i++; - } - return (*i); + + return *i; } -static void -mark_requested_irq( char irq ) +static void __init mark_requested_irq(char irq) { rirqs[iindx++] = irq; } -#ifdef MODULE -static int -clear_requested_irq( char irq ) +static int __exit clear_requested_irq(char irq) { int i; - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { if (rirqs[i] == irq) { rirqs[i] = 0; return 1; @@ -332,17 +341,15 @@ clear_requested_irq( char irq ) } return 0; } -#endif -static int -have_requested_irq( char irq ) +static int have_requested_irq(char irq) { - // array init to zeros so 0 irq will not be requested as a side effect + /* array init to zeros so 0 irq will not be requested as a side + * effect */ int i; - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) if (rirqs[i] == irq) return 1; - } return 0; } @@ -361,53 +368,45 @@ have_requested_irq( char irq ) /* handle subsequent installations of the driver. All memory allocated by the */ /* driver should be returned since it may be unloaded from memory. */ /******************************************************************************/ -#ifdef MODULE -void __exit -ip2_cleanup_module(void) +static void __exit ip2_cleanup_module(void) { int err; int i; -#ifdef IP2DEBUG_INIT - printk (KERN_DEBUG "Unloading %s: version %s\n", pcName, pcVersion ); -#endif - /* Stop poll timer if we had one. */ - if ( TimerOn ) { - del_timer ( &PollTimer ); - TimerOn = 0; - } + del_timer_sync(&PollTimer); /* Reset the boards we have. */ - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( i2BoardPtrTable[i] ) { - iiReset( i2BoardPtrTable[i] ); - } - } + for (i = 0; i < IP2_MAX_BOARDS; i++) + if (i2BoardPtrTable[i]) + iiReset(i2BoardPtrTable[i]); /* The following is done at most once, if any boards were installed. */ - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( i2BoardPtrTable[i] ) { - iiResetDelay( i2BoardPtrTable[i] ); + for (i = 0; i < IP2_MAX_BOARDS; i++) { + if (i2BoardPtrTable[i]) { + iiResetDelay(i2BoardPtrTable[i]); /* free io addresses and Tibet */ - release_region( ip2config.addr[i], 8 ); + release_region(ip2config.addr[i], 8); device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i)); - device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, 4 * i + 1)); + device_destroy(ip2_class, MKDEV(IP2_IPL_MAJOR, + 4 * i + 1)); } /* Disable and remove interrupt handler. */ - if ( (ip2config.irq[i] > 0) && have_requested_irq(ip2config.irq[i]) ) { - free_irq ( ip2config.irq[i], (void *)&pcName); - clear_requested_irq( ip2config.irq[i]); + if (ip2config.irq[i] > 0 && + have_requested_irq(ip2config.irq[i])) { + free_irq(ip2config.irq[i], (void *)&pcName); + clear_requested_irq(ip2config.irq[i]); } } class_destroy(ip2_class); - if ( ( err = tty_unregister_driver ( ip2_tty_driver ) ) ) { - printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", err); - } + err = tty_unregister_driver(ip2_tty_driver); + if (err) + printk(KERN_ERR "IP2: failed to unregister tty driver (%d)\n", + err); put_tty_driver(ip2_tty_driver); unregister_chrdev(IP2_IPL_MAJOR, pcIpl); remove_proc_entry("ip2mem", NULL); - // free memory + /* free memory */ for (i = 0; i < IP2_MAX_BOARDS; i++) { void *pB; #ifdef CONFIG_PCI @@ -417,24 +416,18 @@ ip2_cleanup_module(void) ip2config.pci_dev[i] = NULL; } #endif - if ((pB = i2BoardPtrTable[i]) != 0 ) { - kfree ( pB ); + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + kfree(pB); i2BoardPtrTable[i] = NULL; } - if ((DevTableMem[i]) != NULL ) { - kfree ( DevTableMem[i] ); + if (DevTableMem[i] != NULL) { + kfree(DevTableMem[i]); DevTableMem[i] = NULL; } } - - /* Cleanup the iiEllis subsystem. */ - iiEllisCleanup(); -#ifdef IP2DEBUG_INIT - printk (KERN_DEBUG "IP2 Unloaded\n" ); -#endif } module_exit(ip2_cleanup_module); -#endif /* MODULE */ static const struct tty_operations ip2_ops = { .open = ip2_open, @@ -494,139 +487,168 @@ static const struct firmware *ip2_request_firmware(void) return fw; } -int -ip2_loadmain(int *iop, int *irqp) +#ifndef MODULE +/****************************************************************************** + * ip2_setup: + * str: kernel command line string + * + * Can't autoprobe the boards so user must specify configuration on + * kernel command line. Sane people build it modular but the others + * come here. + * + * Alternating pairs of io,irq for up to 4 boards. + * ip2=io0,irq0,io1,irq1,io2,irq2,io3,irq3 + * + * io=0 => No board + * io=1 => PCI + * io=2 => EISA + * else => ISA I/O address + * + * irq=0 or invalid for ISA will revert to polling mode + * + * Any value = -1, do not overwrite compiled in value. + * + ******************************************************************************/ +static int __init ip2_setup(char *str) +{ + int j, ints[10]; /* 4 boards, 2 parameters + 2 */ + unsigned int i; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + for (i = 0, j = 1; i < 4; i++) { + if (j > ints[0]) + break; + if (ints[j] >= 0) + io[i] = ints[j]; + j++; + if (j > ints[0]) + break; + if (ints[j] >= 0) + irq[i] = ints[j]; + j++; + } + return 1; +} +__setup("ip2=", ip2_setup); +#endif /* !MODULE */ + +static int __init ip2_loadmain(void) { int i, j, box; int err = 0; - static int loaded; i2eBordStrPtr pB = NULL; int rc = -1; - static struct pci_dev *pci_dev_i = NULL; + struct pci_dev *pdev = NULL; const struct firmware *fw = NULL; - ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 ); + if (poll_only) { + /* Hard lock the interrupts to zero */ + irq[0] = irq[1] = irq[2] = irq[3] = poll_only = 0; + } + + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0); /* process command line arguments to modprobe or insmod i.e. iop & irqp */ /* irqp and iop should ALWAYS be specified now... But we check them individually just to be sure, anyways... */ - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if (iop) { - ip2config.addr[i] = iop[i]; - if (irqp) { - if( irqp[i] >= 0 ) { - ip2config.irq[i] = irqp[i]; - } else { - ip2config.irq[i] = 0; - } - // This is a little bit of a hack. If poll_only=1 on command - // line back in ip2.c OR all IRQs on all specified boards are - // explicitly set to 0, then drop to poll only mode and override - // PCI or EISA interrupts. This superceeds the old hack of - // triggering if all interrupts were zero (like da default). - // Still a hack but less prone to random acts of terrorism. - // - // What we really should do, now that the IRQ default is set - // to -1, is to use 0 as a hard coded, do not probe. - // - // /\/\|=mhw=|\/\/ - poll_only |= irqp[i]; - } - } + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + ip2config.addr[i] = io[i]; + if (irq[i] >= 0) + ip2config.irq[i] = irq[i]; + else + ip2config.irq[i] = 0; + /* This is a little bit of a hack. If poll_only=1 on command + line back in ip2.c OR all IRQs on all specified boards are + explicitly set to 0, then drop to poll only mode and override + PCI or EISA interrupts. This superceeds the old hack of + triggering if all interrupts were zero (like da default). + Still a hack but less prone to random acts of terrorism. + + What we really should do, now that the IRQ default is set + to -1, is to use 0 as a hard coded, do not probe. + + /\/\|=mhw=|\/\/ + */ + poll_only |= irq[i]; } poll_only = !poll_only; /* Announce our presence */ - printk( KERN_INFO "%s version %s\n", pcName, pcVersion ); - - // ip2 can be unloaded and reloaded for no good reason - // we can't let that happen here or bad things happen - // second load hoses board but not system - fixme later - if (loaded) { - printk( KERN_INFO "Still loaded\n" ); - return 0; - } - loaded++; + printk(KERN_INFO "%s version %s\n", pcName, pcVersion); ip2_tty_driver = alloc_tty_driver(IP2_MAX_PORTS); if (!ip2_tty_driver) return -ENOMEM; - /* Initialise the iiEllis subsystem. */ - iiEllisInit(); - - /* Initialize arrays. */ - memset( i2BoardPtrTable, 0, sizeof i2BoardPtrTable ); - memset( DevTable, 0, sizeof DevTable ); - /* Initialise all the boards we can find (up to the maximum). */ - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - switch ( ip2config.addr[i] ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + switch (ip2config.addr[i]) { case 0: /* skip this slot even if card is present */ break; default: /* ISA */ /* ISA address must be specified */ - if ( (ip2config.addr[i] < 0x100) || (ip2config.addr[i] > 0x3f8) ) { - printk ( KERN_ERR "IP2: Bad ISA board %d address %x\n", - i, ip2config.addr[i] ); + if (ip2config.addr[i] < 0x100 || + ip2config.addr[i] > 0x3f8) { + printk(KERN_ERR "IP2: Bad ISA board %d " + "address %x\n", i, + ip2config.addr[i]); ip2config.addr[i] = 0; - } else { - ip2config.type[i] = ISA; - - /* Check for valid irq argument, set for polling if invalid */ - if (ip2config.irq[i] && !is_valid_irq(ip2config.irq[i])) { - printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n",ip2config.irq[i]); - ip2config.irq[i] = 0;// 0 is polling and is valid in that sense - } + break; + } + ip2config.type[i] = ISA; + + /* Check for valid irq argument, set for polling if + * invalid */ + if (ip2config.irq[i] && + !is_valid_irq(ip2config.irq[i])) { + printk(KERN_ERR "IP2: Bad IRQ(%d) specified\n", + ip2config.irq[i]); + /* 0 is polling and is valid in that sense */ + ip2config.irq[i] = 0; } break; case PCI: #ifdef CONFIG_PCI - { - int status; + { + u32 addr; + int status; - pci_dev_i = pci_get_device(PCI_VENDOR_ID_COMPUTONE, - PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i); - if (pci_dev_i != NULL) { - unsigned int addr; - - if (pci_enable_device(pci_dev_i)) { - printk( KERN_ERR "IP2: can't enable PCI device at %s\n", - pci_name(pci_dev_i)); - break; - } - ip2config.type[i] = PCI; - ip2config.pci_dev[i] = pci_dev_get(pci_dev_i); - status = - pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr); - if ( addr & 1 ) { - ip2config.addr[i]=(USHORT)(addr&0xfffe); - } else { - printk( KERN_ERR "IP2: PCI I/O address error\n"); - } + pdev = pci_get_device(PCI_VENDOR_ID_COMPUTONE, + PCI_DEVICE_ID_COMPUTONE_IP2EX, pdev); + if (pdev == NULL) { + ip2config.addr[i] = 0; + printk(KERN_ERR "IP2: PCI board %d not " + "found\n", i); + break; + } -// If the PCI BIOS assigned it, lets try and use it. If we -// can't acquire it or it screws up, deal with it then. - -// if (!is_valid_irq(pci_irq)) { -// printk( KERN_ERR "IP2: Bad PCI BIOS IRQ(%d)\n",pci_irq); -// pci_irq = 0; -// } - ip2config.irq[i] = pci_dev_i->irq; - } else { // ann error - ip2config.addr[i] = 0; - printk(KERN_ERR "IP2: PCI board %d not found\n", i); - } + if (pci_enable_device(pdev)) { + dev_err(&pdev->dev, "can't enable device\n"); + break; } + ip2config.type[i] = PCI; + ip2config.pci_dev[i] = pci_dev_get(pdev); + status = pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, + &addr); + if (addr & 1) + ip2config.addr[i] = (USHORT)(addr & 0xfffe); + else + dev_err(&pdev->dev, "I/O address error\n"); + + ip2config.irq[i] = pdev->irq; + } #else - printk( KERN_ERR "IP2: PCI card specified but PCI support not\n"); - printk( KERN_ERR "IP2: configured in this kernel.\n"); - printk( KERN_ERR "IP2: Recompile kernel with CONFIG_PCI defined!\n"); + printk(KERN_ERR "IP2: PCI card specified but PCI " + "support not enabled.\n"); + printk(KERN_ERR "IP2: Recompile kernel with CONFIG_PCI " + "defined!\n"); #endif /* CONFIG_PCI */ break; case EISA: - if ( (ip2config.addr[i] = find_eisa_board( Eisa_slot + 1 )) != 0) { + ip2config.addr[i] = find_eisa_board(Eisa_slot + 1); + if (ip2config.addr[i] != 0) { /* Eisa_irq set as side effect, boo */ ip2config.type[i] = EISA; } @@ -634,31 +656,32 @@ ip2_loadmain(int *iop, int *irqp) break; } /* switch */ } /* for */ - if (pci_dev_i) - pci_dev_put(pci_dev_i); + pci_dev_put(pdev); - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( ip2config.addr[i] ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (ip2config.addr[i]) { pB = kzalloc(sizeof(i2eBordStr), GFP_KERNEL); if (pB) { i2BoardPtrTable[i] = pB; - iiSetAddress( pB, ip2config.addr[i], ii2DelayTimer ); - iiReset( pB ); - } else { - printk(KERN_ERR "IP2: board memory allocation error\n"); - } + iiSetAddress(pB, ip2config.addr[i], + ii2DelayTimer); + iiReset(pB); + } else + printk(KERN_ERR "IP2: board memory allocation " + "error\n"); } } - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( ( pB = i2BoardPtrTable[i] ) != NULL ) { - iiResetDelay( pB ); + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + iiResetDelay(pB); break; } } - for ( i = 0; i < IP2_MAX_BOARDS; ++i ) { + for (i = 0; i < IP2_MAX_BOARDS; ++i) { /* We don't want to request the firmware unless we have at least one board */ - if ( i2BoardPtrTable[i] != NULL ) { + if (i2BoardPtrTable[i] != NULL) { if (!fw) fw = ip2_request_firmware(); if (!fw) @@ -669,7 +692,7 @@ ip2_loadmain(int *iop, int *irqp) if (fw) release_firmware(fw); - ip2trace (ITRC_NO_PORT, ITRC_INIT, 2, 0 ); + ip2trace(ITRC_NO_PORT, ITRC_INIT, 2, 0); ip2_tty_driver->owner = THIS_MODULE; ip2_tty_driver->name = "ttyF"; @@ -680,20 +703,23 @@ ip2_loadmain(int *iop, int *irqp) ip2_tty_driver->subtype = SERIAL_TYPE_NORMAL; ip2_tty_driver->init_termios = tty_std_termios; ip2_tty_driver->init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL; - ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + ip2_tty_driver->flags = TTY_DRIVER_REAL_RAW | + TTY_DRIVER_DYNAMIC_DEV; tty_set_operations(ip2_tty_driver, &ip2_ops); - ip2trace (ITRC_NO_PORT, ITRC_INIT, 3, 0 ); + ip2trace(ITRC_NO_PORT, ITRC_INIT, 3, 0); - /* Register the tty devices. */ - if ( ( err = tty_register_driver ( ip2_tty_driver ) ) ) { - printk(KERN_ERR "IP2: failed to register tty driver (%d)\n", err); + err = tty_register_driver(ip2_tty_driver); + if (err) { + printk(KERN_ERR "IP2: failed to register tty driver\n"); put_tty_driver(ip2_tty_driver); - return -EINVAL; - } else - /* Register the IPL driver. */ - if ( ( err = register_chrdev ( IP2_IPL_MAJOR, pcIpl, &ip2_ipl ) ) ) { - printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", err ); + return err; /* leaking resources */ + } + + err = register_chrdev(IP2_IPL_MAJOR, pcIpl, &ip2_ipl); + if (err) { + printk(KERN_ERR "IP2: failed to register IPL device (%d)\n", + err); } else { /* create the sysfs class */ ip2_class = class_create(THIS_MODULE, "ip2"); @@ -705,84 +731,86 @@ ip2_loadmain(int *iop, int *irqp) /* Register the read_procmem thing */ if (!proc_create("ip2mem",0,NULL,&ip2mem_proc_fops)) { printk(KERN_ERR "IP2: failed to register read_procmem\n"); - } else { + return -EIO; /* leaking resources */ + } - ip2trace (ITRC_NO_PORT, ITRC_INIT, 4, 0 ); - /* Register the interrupt handler or poll handler, depending upon the - * specified interrupt. - */ + ip2trace(ITRC_NO_PORT, ITRC_INIT, 4, 0); + /* Register the interrupt handler or poll handler, depending upon the + * specified interrupt. + */ - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( 0 == ip2config.addr[i] ) { - continue; - } + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (ip2config.addr[i] == 0) + continue; - if ( NULL != ( pB = i2BoardPtrTable[i] ) ) { - device_create_drvdata(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i), - NULL, "ipl%d", i); - device_create_drvdata(ip2_class, NULL, - MKDEV(IP2_IPL_MAJOR, 4 * i + 1), - NULL, "stat%d", i); - - for ( box = 0; box < ABS_MAX_BOXES; ++box ) - { - for ( j = 0; j < ABS_BIGGEST_BOX; ++j ) - { - if ( pB->i2eChannelMap[box] & (1 << j) ) - { - tty_register_device(ip2_tty_driver, - j + ABS_BIGGEST_BOX * - (box+i*ABS_MAX_BOXES), NULL); - } - } - } - } + pB = i2BoardPtrTable[i]; + if (pB != NULL) { + device_create_drvdata(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i), + NULL, "ipl%d", i); + device_create_drvdata(ip2_class, NULL, + MKDEV(IP2_IPL_MAJOR, 4 * i + 1), + NULL, "stat%d", i); + + for (box = 0; box < ABS_MAX_BOXES; box++) + for (j = 0; j < ABS_BIGGEST_BOX; j++) + if (pB->i2eChannelMap[box] & (1 << j)) + tty_register_device( + ip2_tty_driver, + j + ABS_BIGGEST_BOX * + (box+i*ABS_MAX_BOXES), + NULL); + } - if (poll_only) { -// Poll only forces driver to only use polling and -// to ignore the probed PCI or EISA interrupts. - ip2config.irq[i] = CIR_POLL; - } - if ( ip2config.irq[i] == CIR_POLL ) { + if (poll_only) { + /* Poll only forces driver to only use polling and + to ignore the probed PCI or EISA interrupts. */ + ip2config.irq[i] = CIR_POLL; + } + if (ip2config.irq[i] == CIR_POLL) { retry: - if (!TimerOn) { - PollTimer.expires = POLL_TIMEOUT; - add_timer ( &PollTimer ); - TimerOn = 1; - printk( KERN_INFO "IP2: polling\n"); - } - } else { - if (have_requested_irq(ip2config.irq[i])) - continue; - rc = request_irq( ip2config.irq[i], ip2_interrupt, - IP2_SA_FLAGS | (ip2config.type[i] == PCI ? IRQF_SHARED : 0), - pcName, i2BoardPtrTable[i]); - if (rc) { - printk(KERN_ERR "IP2: an request_irq failed: error %d\n",rc); - ip2config.irq[i] = CIR_POLL; - printk( KERN_INFO "IP2: Polling %ld/sec.\n", - (POLL_TIMEOUT - jiffies)); - goto retry; - } - mark_requested_irq(ip2config.irq[i]); - /* Initialise the interrupt handler bottom half (aka slih). */ + if (!timer_pending(&PollTimer)) { + mod_timer(&PollTimer, POLL_TIMEOUT); + printk(KERN_INFO "IP2: polling\n"); } - } - for( i = 0; i < IP2_MAX_BOARDS; ++i ) { - if ( i2BoardPtrTable[i] ) { - set_irq( i, ip2config.irq[i] ); /* set and enable board interrupt */ + } else { + if (have_requested_irq(ip2config.irq[i])) + continue; + rc = request_irq(ip2config.irq[i], ip2_interrupt, + IP2_SA_FLAGS | + (ip2config.type[i] == PCI ? IRQF_SHARED : 0), + pcName, i2BoardPtrTable[i]); + if (rc) { + printk(KERN_ERR "IP2: request_irq failed: " + "error %d\n", rc); + ip2config.irq[i] = CIR_POLL; + printk(KERN_INFO "IP2: Polling %ld/sec.\n", + (POLL_TIMEOUT - jiffies)); + goto retry; } + mark_requested_irq(ip2config.irq[i]); + /* Initialise the interrupt handler bottom half + * (aka slih). */ } } - ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 ); - goto out; + + for (i = 0; i < IP2_MAX_BOARDS; ++i) { + if (i2BoardPtrTable[i]) { + /* set and enable board interrupt */ + set_irq(i, ip2config.irq[i]); + } + } + + ip2trace(ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0); + + return 0; out_chrdev: unregister_chrdev(IP2_IPL_MAJOR, "ip2"); -out: + /* unregister and put tty here */ return err; } +module_init(ip2_loadmain); /******************************************************************************/ /* Function: ip2_init_board() */ @@ -1199,9 +1227,8 @@ ip2_polled_interrupt(void) { int i; i2eBordStrPtr pB; - const int irq = 0; - ip2trace (ITRC_NO_PORT, ITRC_INTR, 99, 1, irq ); + ip2trace(ITRC_NO_PORT, ITRC_INTR, 99, 1, 0); /* Service just the boards on the list using this irq */ for( i = 0; i < i2nBoards; ++i ) { @@ -1210,9 +1237,8 @@ ip2_polled_interrupt(void) // Only process those boards which match our IRQ. // IRQ = 0 for polled boards, we won't poll "IRQ" boards - if ( pB && (pB->i2eUsingIrq == irq) ) { + if (pB && pB->i2eUsingIrq == 0) ip2_irq_work(pB); - } } ++irq_counter; @@ -1250,16 +1276,12 @@ ip2_poll(unsigned long arg) { ip2trace (ITRC_NO_PORT, ITRC_INTR, 100, 0 ); - TimerOn = 0; // it's the truth but not checked in service - // Just polled boards, IRQ = 0 will hit all non-interrupt boards. // It will NOT poll boards handled by hard interrupts. // The issue of queued BH interrupts is handled in ip2_interrupt(). ip2_polled_interrupt(); - PollTimer.expires = POLL_TIMEOUT; - add_timer( &PollTimer ); - TimerOn = 1; + mod_timer(&PollTimer, POLL_TIMEOUT); ip2trace (ITRC_NO_PORT, ITRC_INTR, ITRC_RETURN, 0 ); } @@ -2871,7 +2893,7 @@ ip2_ipl_ioctl (struct file *pFile, UINT cmd, ULONG arg ) case 13: switch ( cmd ) { case 64: /* Driver - ip2stat */ - rc = put_user(ip2_tty_driver->refcount, pIndex++ ); + rc = put_user(-1, pIndex++ ); rc = put_user(irq_counter, pIndex++ ); rc = put_user(bh_counter, pIndex++ ); break; diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 8f7cc190b62..7d30ee1d3fc 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -421,17 +421,16 @@ static void isicom_tx(unsigned long _data) if (retries >= 100) goto unlock; + tty = tty_port_tty_get(&port->port); + if (tty == NULL) + goto put_unlock; + for (; count > 0; count--, port++) { /* port not active or tx disabled to force flow control */ if (!(port->port.flags & ASYNC_INITIALIZED) || !(port->status & ISI_TXOK)) continue; - tty = port->port.tty; - - if (tty == NULL) - continue; - txcount = min_t(short, TX_SIZE, port->xmit_cnt); if (txcount <= 0 || tty->stopped || tty->hw_stopped) continue; @@ -489,6 +488,8 @@ static void isicom_tx(unsigned long _data) tty_wakeup(tty); } +put_unlock: + tty_kref_put(tty); unlock: spin_unlock_irqrestore(&isi_card[card].card_lock, flags); /* schedule another tx for hopefully in about 10ms */ @@ -547,7 +548,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty == NULL) { word_count = byte_count >> 1; while (byte_count > 1) { @@ -588,7 +589,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) } if (port->port.flags & ASYNC_CTS_FLOW) { - if (port->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (header & ISI_CTS) { port->port.tty->hw_stopped = 0; /* start tx ing */ @@ -597,7 +598,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) tty_wakeup(tty); } } else if (!(header & ISI_CTS)) { - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; /* stop tx ing */ port->status &= ~(ISI_TXOK | ISI_CTS); } @@ -660,24 +661,21 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id) } outw(0x0000, base+0x04); /* enable interrupts */ spin_unlock(&card->card_lock); + tty_kref_put(tty); return IRQ_HANDLED; } -static void isicom_config_port(struct isi_port *port) +static void isicom_config_port(struct tty_struct *tty) { + struct isi_port *port = tty->driver_data; struct isi_board *card = port->card; - struct tty_struct *tty; unsigned long baud; unsigned long base = card->base; u16 channel_setup, channel = port->channel, shift_count = card->shift_count; unsigned char flow_ctrl; - tty = port->port.tty; - - if (tty == NULL) - return; /* FIXME: Switch to new tty baud API */ baud = C_BAUD(tty); if (baud & CBAUDEX) { @@ -690,7 +688,7 @@ static void isicom_config_port(struct isi_port *port) /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */ if (baud < 1 || baud > 4) - port->port.tty->termios->c_cflag &= ~CBAUDEX; + tty->termios->c_cflag &= ~CBAUDEX; else baud += 15; } @@ -797,8 +795,9 @@ static inline void isicom_setup_board(struct isi_board *bp) spin_unlock_irqrestore(&bp->card_lock, flags); } -static int isicom_setup_port(struct isi_port *port) +static int isicom_setup_port(struct tty_struct *tty) { + struct isi_port *port = tty->driver_data; struct isi_board *card = port->card; unsigned long flags; @@ -808,8 +807,7 @@ static int isicom_setup_port(struct isi_port *port) return -ENOMEM; spin_lock_irqsave(&card->card_lock, flags); - if (port->port.tty) - clear_bit(TTY_IO_ERROR, &port->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); if (port->port.count == 1) card->count++; @@ -823,7 +821,7 @@ static int isicom_setup_port(struct isi_port *port) InterruptTheCard(card->base); } - isicom_config_port(port); + isicom_config_port(tty); port->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); @@ -934,8 +932,8 @@ static int isicom_open(struct tty_struct *tty, struct file *filp) port->port.count++; tty->driver_data = port; - port->port.tty = tty; - error = isicom_setup_port(port); + tty_port_tty_set(&port->port, tty); + error = isicom_setup_port(tty); if (error == 0) error = block_til_ready(tty, filp, port); return error; @@ -955,15 +953,17 @@ static void isicom_shutdown_port(struct isi_port *port) struct isi_board *card = port->card; struct tty_struct *tty; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); - if (!(port->port.flags & ASYNC_INITIALIZED)) + if (!(port->port.flags & ASYNC_INITIALIZED)) { + tty_kref_put(tty); return; + } tty_port_free_xmit_buf(&port->port); port->port.flags &= ~ASYNC_INITIALIZED; /* 3rd October 2000 : Vinayak P Risbud */ - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); /*Fix done by Anil .S on 30-04-2001 remote login through isi port has dtr toggle problem @@ -1243,9 +1243,10 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file, return 0; } -static int isicom_set_serial_info(struct isi_port *port, - struct serial_struct __user *info) +static int isicom_set_serial_info(struct tty_struct *tty, + struct serial_struct __user *info) { + struct isi_port *port = tty->driver_data; struct serial_struct newinfo; int reconfig_port; @@ -1276,7 +1277,7 @@ static int isicom_set_serial_info(struct isi_port *port, if (reconfig_port) { unsigned long flags; spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(port); + isicom_config_port(tty); spin_unlock_irqrestore(&port->card->card_lock, flags); } unlock_kernel(); @@ -1318,7 +1319,7 @@ static int isicom_ioctl(struct tty_struct *tty, struct file *filp, return isicom_get_serial_info(port, argp); case TIOCSSERIAL: - return isicom_set_serial_info(port, argp); + return isicom_set_serial_info(tty, argp); default: return -ENOIOCTLCMD; @@ -1341,7 +1342,7 @@ static void isicom_set_termios(struct tty_struct *tty, return; spin_lock_irqsave(&port->card->card_lock, flags); - isicom_config_port(port); + isicom_config_port(tty); spin_unlock_irqrestore(&port->card->card_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && @@ -1419,7 +1420,7 @@ static void isicom_hangup(struct tty_struct *tty) port->port.count = 0; port->port.flags &= ~ASYNC_NORMAL_ACTIVE; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); wake_up_interruptible(&port->port.open_wait); } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index 843a2afaf20..505d7a1f6b8 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -623,24 +623,25 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp); static void stli_poll(unsigned long arg); static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp); -static int stli_initopen(struct stlibrd *brdp, struct stliport *portp); +static int stli_initopen(struct tty_struct *tty, struct stlibrd *brdp, struct stliport *portp); static int stli_rawopen(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); static int stli_rawclose(struct stlibrd *brdp, struct stliport *portp, unsigned long arg, int wait); -static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp); -static int stli_setport(struct stliport *portp); +static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, + struct stliport *portp, struct file *filp); +static int stli_setport(struct tty_struct *tty); static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void __stli_sendcmd(struct stlibrd *brdp, struct stliport *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_dodelaycmd(struct stliport *portp, cdkctrl_t __iomem *cp); -static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermios *tiosp); +static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, asyport_t *pp, struct ktermios *tiosp); static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts); static long stli_mktiocm(unsigned long sigvalue); static void stli_read(struct stlibrd *brdp, struct stliport *portp); static int stli_getserial(struct stliport *portp, struct serial_struct __user *sp); -static int stli_setserial(struct stliport *portp, struct serial_struct __user *sp); +static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp); static int stli_getbrdstats(combrd_t __user *bp); -static int stli_getportstats(struct stliport *portp, comstats_t __user *cp); -static int stli_portcmdstats(struct stliport *portp); +static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, comstats_t __user *cp); +static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp); static int stli_clrportstats(struct stliport *portp, comstats_t __user *cp); static int stli_getportstruct(struct stliport __user *arg); static int stli_getbrdstruct(struct stlibrd __user *arg); @@ -731,12 +732,16 @@ static void stli_cleanup_ports(struct stlibrd *brdp) { struct stliport *portp; unsigned int j; + struct tty_struct *tty; for (j = 0; j < STL_MAXPORTS; j++) { portp = brdp->ports[j]; if (portp != NULL) { - if (portp->port.tty != NULL) - tty_hangup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty != NULL) { + tty_hangup(tty); + tty_kref_put(tty); + } kfree(portp); } } @@ -824,7 +829,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * requires several commands to the board we will need to wait for any * other open that is already initializing the port. */ - portp->port.tty = tty; + tty_port_tty_set(&portp->port, tty); tty->driver_data = portp; portp->port.count++; @@ -835,7 +840,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) if ((portp->port.flags & ASYNC_INITIALIZED) == 0) { set_bit(ST_INITIALIZING, &portp->state); - if ((rc = stli_initopen(brdp, portp)) >= 0) { + if ((rc = stli_initopen(tty, brdp, portp)) >= 0) { portp->port.flags |= ASYNC_INITIALIZED; clear_bit(TTY_IO_ERROR, &tty->flags); } @@ -864,7 +869,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp) * then also we might have to wait for carrier. */ if (!(filp->f_flags & O_NONBLOCK)) { - if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0) + if ((rc = stli_waitcarrier(tty, brdp, portp, filp)) != 0) return rc; } portp->port.flags |= ASYNC_NORMAL_ACTIVE; @@ -930,7 +935,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp) stli_flushbuffer(tty); tty->closing = 0; - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); if (portp->openwaitcnt) { if (portp->close_delay) @@ -952,9 +957,9 @@ static void stli_close(struct tty_struct *tty, struct file *filp) * this still all happens pretty quickly. */ -static int stli_initopen(struct stlibrd *brdp, struct stliport *portp) +static int stli_initopen(struct tty_struct *tty, + struct stlibrd *brdp, struct stliport *portp) { - struct tty_struct *tty; asynotify_t nt; asyport_t aport; int rc; @@ -969,10 +974,7 @@ static int stli_initopen(struct stlibrd *brdp, struct stliport *portp) sizeof(asynotify_t), 0)) < 0) return rc; - tty = portp->port.tty; - if (tty == NULL) - return -ENODEV; - stli_mkasyport(portp, &aport, tty->termios); + stli_mkasyport(tty, portp, &aport, tty->termios); if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) return rc; @@ -1161,22 +1163,21 @@ static int stli_cmdwait(struct stlibrd *brdp, struct stliport *portp, unsigned l * waiting for the command to complete - so must have user context. */ -static int stli_setport(struct stliport *portp) +static int stli_setport(struct tty_struct *tty) { + struct stliport *portp = tty->driver_data; struct stlibrd *brdp; asyport_t aport; if (portp == NULL) return -ENODEV; - if (portp->port.tty == NULL) - return -ENODEV; if (portp->brdnr >= stli_nrbrds) return -ENODEV; brdp = stli_brds[portp->brdnr]; if (brdp == NULL) return -ENODEV; - stli_mkasyport(portp, &aport, portp->port.tty->termios); + stli_mkasyport(tty, portp, &aport, tty->termios); return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)); } @@ -1187,7 +1188,8 @@ static int stli_setport(struct stliport *portp) * maybe because if we are clocal then we don't need to wait... */ -static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct file *filp) +static int stli_waitcarrier(struct tty_struct *tty, struct stlibrd *brdp, + struct stliport *portp, struct file *filp) { unsigned long flags; int rc, doclocal; @@ -1195,7 +1197,7 @@ static int stli_waitcarrier(struct stlibrd *brdp, struct stliport *portp, struct rc = 0; doclocal = 0; - if (portp->port.tty->termios->c_cflag & CLOCAL) + if (tty->termios->c_cflag & CLOCAL) doclocal++; spin_lock_irqsave(&stli_lock, flags); @@ -1373,8 +1375,6 @@ static void stli_flushchars(struct tty_struct *tty) stli_txcookrealsize = 0; stli_txcooktty = NULL; - if (tty == NULL) - return; if (cooktty == NULL) return; if (tty != cooktty) @@ -1572,10 +1572,11 @@ static int stli_getserial(struct stliport *portp, struct serial_struct __user *s * just quietly ignore any requests to change irq, etc. */ -static int stli_setserial(struct stliport *portp, struct serial_struct __user *sp) +static int stli_setserial(struct tty_struct *tty, struct serial_struct __user *sp) { struct serial_struct sio; int rc; + struct stliport *portp = tty->driver_data; if (copy_from_user(&sio, sp, sizeof(struct serial_struct))) return -EFAULT; @@ -1594,7 +1595,7 @@ static int stli_setserial(struct stliport *portp, struct serial_struct __user *s portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; - if ((rc = stli_setport(portp)) < 0) + if ((rc = stli_setport(tty)) < 0) return rc; return 0; } @@ -1685,17 +1686,17 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm rc = stli_getserial(portp, argp); break; case TIOCSSERIAL: - rc = stli_setserial(portp, argp); + rc = stli_setserial(tty, argp); break; case STL_GETPFLAG: rc = put_user(portp->pflag, (unsigned __user *)argp); break; case STL_SETPFLAG: if ((rc = get_user(portp->pflag, (unsigned __user *)argp)) == 0) - stli_setport(portp); + stli_setport(tty); break; case COM_GETPORTSTATS: - rc = stli_getportstats(portp, argp); + rc = stli_getportstats(tty, portp, argp); break; case COM_CLRPORTSTATS: rc = stli_clrportstats(portp, argp); @@ -1729,8 +1730,6 @@ static void stli_settermios(struct tty_struct *tty, struct ktermios *old) struct ktermios *tiosp; asyport_t aport; - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1742,7 +1741,7 @@ static void stli_settermios(struct tty_struct *tty, struct ktermios *old) tiosp = tty->termios; - stli_mkasyport(portp, &aport, tiosp); + stli_mkasyport(tty, portp, &aport, tiosp); stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_mkasysigs(&portp->asig, ((tiosp->c_cflag & CBAUD) ? 1 : 0), -1); stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, @@ -1854,7 +1853,7 @@ static void stli_hangup(struct tty_struct *tty) clear_bit(ST_TXBUSY, &portp->state); clear_bit(ST_RXSTOP, &portp->state); set_bit(TTY_IO_ERROR, &tty->flags); - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; portp->port.count = 0; spin_unlock_irqrestore(&stli_lock, flags); @@ -1935,8 +1934,6 @@ static void stli_waituntilsent(struct tty_struct *tty, int timeout) struct stliport *portp; unsigned long tend; - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1998,7 +1995,7 @@ static int stli_portinfo(struct stlibrd *brdp, struct stliport *portp, int portn char *sp, *uart; int rc, cnt; - rc = stli_portcmdstats(portp); + rc = stli_portcmdstats(NULL, portp); uart = "UNKNOWN"; if (brdp->state & BST_STARTED) { @@ -2188,7 +2185,7 @@ static void stli_read(struct stlibrd *brdp, struct stliport *portp) if (test_bit(ST_RXSTOP, &portp->state)) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -2230,6 +2227,7 @@ static void stli_read(struct stlibrd *brdp, struct stliport *portp) set_bit(ST_RXING, &portp->state); tty_schedule_flip(tty); + tty_kref_put(tty); } /*****************************************************************************/ @@ -2362,7 +2360,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) if (ap->notify) { nt = ap->changed; ap->notify = 0; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (nt.signal & SG_DCD) { oldsigs = portp->sigs; @@ -2399,6 +2397,7 @@ static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp) tty_schedule_flip(tty); } } + tty_kref_put(tty); if (nt.data & DT_RXBUSY) { donerx++; @@ -2535,14 +2534,15 @@ static void stli_poll(unsigned long arg) * the slave. */ -static void stli_mkasyport(struct stliport *portp, asyport_t *pp, struct ktermios *tiosp) +static void stli_mkasyport(struct tty_struct *tty, struct stliport *portp, + asyport_t *pp, struct ktermios *tiosp) { memset(pp, 0, sizeof(asyport_t)); /* * Start of by setting the baud, char size, parity and stop bit info. */ - pp->baudout = tty_get_baud_rate(portp->port.tty); + pp->baudout = tty_get_baud_rate(tty); if ((tiosp->c_cflag & CBAUD) == B38400) { if ((portp->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) pp->baudout = 57600; @@ -2695,7 +2695,7 @@ static int stli_initports(struct stlibrd *brdp) printk("STALLION: failed to allocate port structure\n"); continue; } - + tty_port_init(&portp->port); portp->magic = STLI_PORTMAGIC; portp->portnr = i; portp->brdnr = brdp->brdnr; @@ -4220,7 +4220,7 @@ static struct stliport *stli_getport(unsigned int brdnr, unsigned int panelnr, * what port to get stats for (used through board control device). */ -static int stli_portcmdstats(struct stliport *portp) +static int stli_portcmdstats(struct tty_struct *tty, struct stliport *portp) { unsigned long flags; struct stlibrd *brdp; @@ -4249,15 +4249,15 @@ static int stli_portcmdstats(struct stliport *portp) stli_comstats.flags = portp->port.flags; spin_lock_irqsave(&brd_lock, flags); - if (portp->port.tty != NULL) { - if (portp->port.tty->driver_data == portp) { - stli_comstats.ttystate = portp->port.tty->flags; + if (tty != NULL) { + if (portp->port.tty == tty) { + stli_comstats.ttystate = tty->flags; stli_comstats.rxbuffered = -1; - if (portp->port.tty->termios != NULL) { - stli_comstats.cflags = portp->port.tty->termios->c_cflag; - stli_comstats.iflags = portp->port.tty->termios->c_iflag; - stli_comstats.oflags = portp->port.tty->termios->c_oflag; - stli_comstats.lflags = portp->port.tty->termios->c_lflag; + if (tty->termios != NULL) { + stli_comstats.cflags = tty->termios->c_cflag; + stli_comstats.iflags = tty->termios->c_iflag; + stli_comstats.oflags = tty->termios->c_oflag; + stli_comstats.lflags = tty->termios->c_lflag; } } } @@ -4294,7 +4294,8 @@ static int stli_portcmdstats(struct stliport *portp) * what port to get stats for (used through board control device). */ -static int stli_getportstats(struct stliport *portp, comstats_t __user *cp) +static int stli_getportstats(struct tty_struct *tty, struct stliport *portp, + comstats_t __user *cp) { struct stlibrd *brdp; int rc; @@ -4312,7 +4313,7 @@ static int stli_getportstats(struct stliport *portp, comstats_t __user *cp) if (!brdp) return -ENODEV; - if ((rc = stli_portcmdstats(portp)) < 0) + if ((rc = stli_portcmdstats(tty, portp)) < 0) return rc; return copy_to_user(cp, &stli_comstats, sizeof(comstats_t)) ? @@ -4427,7 +4428,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un switch (cmd) { case COM_GETPORTSTATS: - rc = stli_getportstats(NULL, argp); + rc = stli_getportstats(NULL, NULL, argp); done++; break; case COM_CLRPORTSTATS: diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index d3d7864e0c1..5df4003ad87 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -205,7 +205,7 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file, static void moxa_poll(unsigned long); static void moxa_set_tty_param(struct tty_struct *, struct ktermios *); static void moxa_setup_empty_event(struct tty_struct *); -static void moxa_shut_down(struct moxa_port *); +static void moxa_shut_down(struct tty_struct *); /* * moxa board interface functions: */ @@ -217,7 +217,7 @@ static void MoxaPortLineCtrl(struct moxa_port *, int, int); static void MoxaPortFlowCtrl(struct moxa_port *, int, int, int, int, int); static int MoxaPortLineStatus(struct moxa_port *); static void MoxaPortFlushData(struct moxa_port *, int); -static int MoxaPortWriteData(struct moxa_port *, const unsigned char *, int); +static int MoxaPortWriteData(struct tty_struct *, const unsigned char *, int); static int MoxaPortReadData(struct moxa_port *); static int MoxaPortTxQueue(struct moxa_port *); static int MoxaPortRxQueue(struct moxa_port *); @@ -332,6 +332,7 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, for (i = 0; i < MAX_BOARDS; i++) { p = moxa_boards[i].ports; for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) { + struct tty_struct *ttyp; memset(&tmp, 0, sizeof(tmp)); if (!moxa_boards[i].ready) goto copy; @@ -344,10 +345,12 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file, if (status & 4) tmp.dcd = 1; - if (!p->port.tty || !p->port.tty->termios) + ttyp = tty_port_tty_get(&p->port); + if (!ttyp || !ttyp->termios) tmp.cflag = p->cflag; else - tmp.cflag = p->port.tty->termios->c_cflag; + tmp.cflag = ttyp->termios->c_cflag; + tty_kref_put(tty); copy: if (copy_to_user(argm, &tmp, sizeof(tmp))) { mutex_unlock(&moxa_openlock); @@ -880,8 +883,14 @@ static void moxa_board_deinit(struct moxa_board_conf *brd) /* pci hot-un-plug support */ for (a = 0; a < brd->numPorts; a++) - if (brd->ports[a].port.flags & ASYNC_INITIALIZED) - tty_hangup(brd->ports[a].port.tty); + if (brd->ports[a].port.flags & ASYNC_INITIALIZED) { + struct tty_struct *tty = tty_port_tty_get( + &brd->ports[a].port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } + } while (1) { opened = 0; for (a = 0; a < brd->numPorts; a++) @@ -1096,13 +1105,14 @@ static void __exit moxa_exit(void) module_init(moxa_init); module_exit(moxa_exit); -static void moxa_close_port(struct moxa_port *ch) +static void moxa_close_port(struct tty_struct *tty) { - moxa_shut_down(ch); + struct moxa_port *ch = tty->driver_data; + moxa_shut_down(tty); MoxaPortFlushData(ch, 2); ch->port.flags &= ~ASYNC_NORMAL_ACTIVE; - ch->port.tty->driver_data = NULL; - ch->port.tty = NULL; + tty->driver_data = NULL; + tty_port_tty_set(&ch->port, NULL); } static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, @@ -1161,7 +1171,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; ch->port.count++; tty->driver_data = ch; - ch->port.tty = tty; + tty_port_tty_set(&ch->port, tty); if (!(ch->port.flags & ASYNC_INITIALIZED)) { ch->statusflags = 0; moxa_set_tty_param(tty, tty->termios); @@ -1179,7 +1189,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) if (retval) { if (ch->port.count) /* 0 means already hung up... */ if (--ch->port.count == 0) - moxa_close_port(ch); + moxa_close_port(tty); } else ch->port.flags |= ASYNC_NORMAL_ACTIVE; mutex_unlock(&moxa_openlock); @@ -1219,7 +1229,7 @@ static void moxa_close(struct tty_struct *tty, struct file *filp) tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ } - moxa_close_port(ch); + moxa_close_port(tty); unlock: mutex_unlock(&moxa_openlock); } @@ -1234,7 +1244,7 @@ static int moxa_write(struct tty_struct *tty, return 0; spin_lock_bh(&moxa_lock); - len = MoxaPortWriteData(ch, buf, count); + len = MoxaPortWriteData(tty, buf, count); spin_unlock_bh(&moxa_lock); ch->statusflags |= LOWWAIT; @@ -1409,7 +1419,7 @@ static void moxa_hangup(struct tty_struct *tty) return; } ch->port.count = 0; - moxa_close_port(ch); + moxa_close_port(tty); mutex_unlock(&moxa_openlock); wake_up_interruptible(&ch->port.open_wait); @@ -1417,11 +1427,14 @@ static void moxa_hangup(struct tty_struct *tty) static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) { + struct tty_struct *tty; dcd = !!dcd; - if (dcd != p->DCDState && p->port.tty && C_CLOCAL(p->port.tty)) { - if (!dcd) - tty_hangup(p->port.tty); + if (dcd != p->DCDState) { + tty = tty_port_tty_get(&p->port); + if (tty && C_CLOCAL(tty) && !dcd) + tty_hangup(tty); + tty_kref_put(tty); } p->DCDState = dcd; } @@ -1429,7 +1442,7 @@ static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd) static int moxa_poll_port(struct moxa_port *p, unsigned int handle, u16 __iomem *ip) { - struct tty_struct *tty = p->port.tty; + struct tty_struct *tty = tty_port_tty_get(&p->port); void __iomem *ofsAddr; unsigned int inited = p->port.flags & ASYNC_INITIALIZED; u16 intr; @@ -1476,6 +1489,7 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle, tty_insert_flip_char(tty, 0, TTY_BREAK); tty_schedule_flip(tty); } + tty_kref_put(tty); if (intr & IntrLine) moxa_new_dcdstate(p, readb(ofsAddr + FlagStat) & DCD_state); @@ -1560,9 +1574,9 @@ static void moxa_setup_empty_event(struct tty_struct *tty) spin_unlock_bh(&moxa_lock); } -static void moxa_shut_down(struct moxa_port *ch) +static void moxa_shut_down(struct tty_struct *tty) { - struct tty_struct *tp = ch->port.tty; + struct moxa_port *ch = tty->driver_data; if (!(ch->port.flags & ASYNC_INITIALIZED)) return; @@ -1572,7 +1586,7 @@ static void moxa_shut_down(struct moxa_port *ch) /* * If we're a modem control device and HUPCL is on, drop RTS & DTR. */ - if (C_HUPCL(tp)) + if (C_HUPCL(tty)) MoxaPortLineCtrl(ch, 0, 0); spin_lock_bh(&moxa_lock); @@ -1953,9 +1967,10 @@ static int MoxaPortLineStatus(struct moxa_port *port) return val; } -static int MoxaPortWriteData(struct moxa_port *port, +static int MoxaPortWriteData(struct tty_struct *tty, const unsigned char *buffer, int len) { + struct moxa_port *port = tty->driver_data; void __iomem *baseAddr, *ofsAddr, *ofs; unsigned int c, total; u16 head, tail, tx_mask, spage, epage; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index b638403e8e9..8beef50f95a 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -610,15 +610,13 @@ static int mxser_block_til_ready(struct tty_struct *tty, struct file *filp, return 0; } -static int mxser_set_baud(struct mxser_port *info, long newspd) +static int mxser_set_baud(struct tty_struct *tty, long newspd) { + struct mxser_port *info = tty->driver_data; int quot = 0, baud; unsigned char cval; - if (!info->port.tty || !info->port.tty->termios) - return -1; - - if (!(info->ioaddr)) + if (!info->ioaddr) return -1; if (newspd > info->max_baud) @@ -626,13 +624,13 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) if (newspd == 134) { quot = 2 * info->baud_base / 269; - tty_encode_baud_rate(info->port.tty, 134, 134); + tty_encode_baud_rate(tty, 134, 134); } else if (newspd) { quot = info->baud_base / newspd; if (quot == 0) quot = 1; baud = info->baud_base/quot; - tty_encode_baud_rate(info->port.tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); } else { quot = 0; } @@ -658,7 +656,7 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) outb(cval, info->ioaddr + UART_LCR); /* reset DLAB */ #ifdef BOTHER - if (C_BAUD(info->port.tty) == BOTHER) { + if (C_BAUD(tty) == BOTHER) { quot = info->baud_base % newspd; quot *= 8; if (quot % newspd > newspd / 2) { @@ -679,21 +677,20 @@ static int mxser_set_baud(struct mxser_port *info, long newspd) * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */ -static int mxser_change_speed(struct mxser_port *info, - struct ktermios *old_termios) +static int mxser_change_speed(struct tty_struct *tty, + struct ktermios *old_termios) { + struct mxser_port *info = tty->driver_data; unsigned cflag, cval, fcr; int ret = 0; unsigned char status; - if (!info->port.tty || !info->port.tty->termios) - return ret; - cflag = info->port.tty->termios->c_cflag; - if (!(info->ioaddr)) + cflag = tty->termios->c_cflag; + if (!info->ioaddr) return ret; - if (mxser_set_baud_method[info->port.tty->index] == 0) - mxser_set_baud(info, tty_get_baud_rate(info->port.tty)); + if (mxser_set_baud_method[tty->index] == 0) + mxser_set_baud(tty, tty_get_baud_rate(tty)); /* byte size and parity */ switch (cflag & CSIZE) { @@ -762,9 +759,9 @@ static int mxser_change_speed(struct mxser_port *info, info->MCR |= UART_MCR_AFE; } else { status = inb(info->ioaddr + UART_MSR); - if (info->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (status & UART_MSR_CTS) { - info->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; if (info->type != PORT_16550A && !info->board->chip_flag) { outb(info->IER & ~UART_IER_THRI, @@ -774,11 +771,11 @@ static int mxser_change_speed(struct mxser_port *info, outb(info->IER, info->ioaddr + UART_IER); } - tty_wakeup(info->port.tty); + tty_wakeup(tty); } } else { if (!(status & UART_MSR_CTS)) { - info->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; if ((info->type != PORT_16550A) && (!info->board->chip_flag)) { info->IER &= ~UART_IER_THRI; @@ -804,21 +801,21 @@ static int mxser_change_speed(struct mxser_port *info, * Set up parity check flag */ info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; - if (I_INPCK(info->port.tty)) + if (I_INPCK(tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; - if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty)) + if (I_BRKINT(tty) || I_PARMRK(tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0; - if (I_IGNBRK(info->port.tty)) { + if (I_IGNBRK(tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ - if (I_IGNPAR(info->port.tty)) { + if (I_IGNPAR(tty)) { info->ignore_status_mask |= UART_LSR_OE | UART_LSR_PE | @@ -830,16 +827,16 @@ static int mxser_change_speed(struct mxser_port *info, } } if (info->board->chip_flag) { - mxser_set_must_xon1_value(info->ioaddr, START_CHAR(info->port.tty)); - mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(info->port.tty)); - if (I_IXON(info->port.tty)) { + mxser_set_must_xon1_value(info->ioaddr, START_CHAR(tty)); + mxser_set_must_xoff1_value(info->ioaddr, STOP_CHAR(tty)); + if (I_IXON(tty)) { mxser_enable_must_rx_software_flow_control( info->ioaddr); } else { mxser_disable_must_rx_software_flow_control( info->ioaddr); } - if (I_IXOFF(info->port.tty)) { + if (I_IXOFF(tty)) { mxser_enable_must_tx_software_flow_control( info->ioaddr); } else { @@ -855,7 +852,8 @@ static int mxser_change_speed(struct mxser_port *info, return ret; } -static void mxser_check_modem_status(struct mxser_port *port, int status) +static void mxser_check_modem_status(struct tty_struct *tty, + struct mxser_port *port, int status) { /* update input line counters */ if (status & UART_MSR_TERI) @@ -874,10 +872,11 @@ static void mxser_check_modem_status(struct mxser_port *port, int status) wake_up_interruptible(&port->port.open_wait); } + tty = tty_port_tty_get(&port->port); if (port->port.flags & ASYNC_CTS_FLOW) { - if (port->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (status & UART_MSR_CTS) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; if ((port->type != PORT_16550A) && (!port->board->chip_flag)) { @@ -887,11 +886,11 @@ static void mxser_check_modem_status(struct mxser_port *port, int status) outb(port->IER, port->ioaddr + UART_IER); } - tty_wakeup(port->port.tty); + tty_wakeup(tty); } } else { if (!(status & UART_MSR_CTS)) { - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; if (port->type != PORT_16550A && !port->board->chip_flag) { port->IER &= ~UART_IER_THRI; @@ -903,8 +902,9 @@ static void mxser_check_modem_status(struct mxser_port *port, int status) } } -static int mxser_startup(struct mxser_port *info) +static int mxser_startup(struct tty_struct *tty) { + struct mxser_port *info = tty->driver_data; unsigned long page; unsigned long flags; @@ -921,8 +921,7 @@ static int mxser_startup(struct mxser_port *info) } if (!info->ioaddr || !info->type) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); free_page(page); spin_unlock_irqrestore(&info->slock, flags); return 0; @@ -952,8 +951,8 @@ static int mxser_startup(struct mxser_port *info) if (inb(info->ioaddr + UART_LSR) == 0xff) { spin_unlock_irqrestore(&info->slock, flags); if (capable(CAP_SYS_ADMIN)) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + if (tty) + set_bit(TTY_IO_ERROR, &tty->flags); return 0; } else return -ENODEV; @@ -991,14 +990,13 @@ static int mxser_startup(struct mxser_port *info) (void) inb(info->ioaddr + UART_IIR); (void) inb(info->ioaddr + UART_MSR); - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * and set the speed of the serial port */ - mxser_change_speed(info, NULL); + mxser_change_speed(tty, NULL); info->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&info->slock, flags); @@ -1009,8 +1007,9 @@ static int mxser_startup(struct mxser_port *info) * This routine will shutdown a serial port; interrupts maybe disabled, and * DTR is dropped if the hangup on close termio flag is on. */ -static void mxser_shutdown(struct mxser_port *info) +static void mxser_shutdown(struct tty_struct *tty) { + struct mxser_port *info = tty->driver_data; unsigned long flags; if (!(info->port.flags & ASYNC_INITIALIZED)) @@ -1035,7 +1034,7 @@ static void mxser_shutdown(struct mxser_port *info) info->IER = 0; outb(0x00, info->ioaddr + UART_IER); - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) + if (tty->termios->c_cflag & HUPCL) info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS); outb(info->MCR, info->ioaddr + UART_MCR); @@ -1051,8 +1050,7 @@ static void mxser_shutdown(struct mxser_port *info) /* read data port to reset things */ (void) inb(info->ioaddr + UART_RX); - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; @@ -1084,14 +1082,14 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) return -ENODEV; tty->driver_data = info; - info->port.tty = tty; + tty_port_tty_set(&info->port, tty); /* * Start up serial port */ spin_lock_irqsave(&info->slock, flags); info->port.count++; spin_unlock_irqrestore(&info->slock, flags); - retval = mxser_startup(info); + retval = mxser_startup(tty); if (retval) return retval; @@ -1209,13 +1207,13 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) break; } } - mxser_shutdown(info); + mxser_shutdown(tty); mxser_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; - info->port.tty = NULL; + tty_port_tty_set(&info->port, NULL); if (info->port.blocked_open) { if (info->port.close_delay) schedule_timeout_interruptible(info->port.close_delay); @@ -1337,12 +1335,13 @@ static int mxser_chars_in_buffer(struct tty_struct *tty) * friends of mxser_ioctl() * ------------------------------------------------------------ */ -static int mxser_get_serial_info(struct mxser_port *info, +static int mxser_get_serial_info(struct tty_struct *tty, struct serial_struct __user *retinfo) { + struct mxser_port *info = tty->driver_data; struct serial_struct tmp = { .type = info->type, - .line = info->port.tty->index, + .line = tty->index, .port = info->ioaddr, .irq = info->board->irq, .flags = info->port.flags, @@ -1357,9 +1356,10 @@ static int mxser_get_serial_info(struct mxser_port *info, return 0; } -static int mxser_set_serial_info(struct mxser_port *info, +static int mxser_set_serial_info(struct tty_struct *tty, struct serial_struct __user *new_info) { + struct mxser_port *info = tty->driver_data; struct serial_struct new_serial; speed_t baud; unsigned long sl_flags; @@ -1393,14 +1393,14 @@ static int mxser_set_serial_info(struct mxser_port *info, (new_serial.flags & ASYNC_FLAGS)); info->port.close_delay = new_serial.close_delay * HZ / 100; info->port.closing_wait = new_serial.closing_wait * HZ / 100; - info->port.tty->low_latency = - (info->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY) + ? 1 : 0; if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST && (new_serial.baud_base != info->baud_base || new_serial.custom_divisor != info->custom_divisor)) { baud = new_serial.baud_base / new_serial.custom_divisor; - tty_encode_baud_rate(info->port.tty, baud, baud); + tty_encode_baud_rate(tty, baud, baud); } } @@ -1411,11 +1411,11 @@ static int mxser_set_serial_info(struct mxser_port *info, if (info->port.flags & ASYNC_INITIALIZED) { if (flags != (info->port.flags & ASYNC_SPD_MASK)) { spin_lock_irqsave(&info->slock, sl_flags); - mxser_change_speed(info, NULL); + mxser_change_speed(tty, NULL); spin_unlock_irqrestore(&info->slock, sl_flags); } } else - retval = mxser_startup(info); + retval = mxser_startup(tty); return retval; } @@ -1461,7 +1461,7 @@ static int mxser_tiocmget(struct tty_struct *tty, struct file *file) spin_lock_irqsave(&info->slock, flags); status = inb(info->ioaddr + UART_MSR); if (status & UART_MSR_ANY_DELTA) - mxser_check_modem_status(info, status); + mxser_check_modem_status(tty, info, status); spin_unlock_irqrestore(&info->slock, flags); return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | @@ -1606,6 +1606,7 @@ static int __init mxser_read_register(int port, unsigned short *regs) static int mxser_ioctl_special(unsigned int cmd, void __user *argp) { struct mxser_port *port; + struct tty_struct *tty; int result, status; unsigned int i, j; int ret = 0; @@ -1643,12 +1644,14 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) if (!port->ioaddr) goto copy; + + tty = tty_port_tty_get(&port->port); - if (!port->port.tty || !port->port.tty->termios) + if (!tty || !tty->termios) ms.cflag = port->normal_termios.c_cflag; else - ms.cflag = port->port.tty->termios->c_cflag; - + ms.cflag = tty->termios->c_cflag; + tty_kref_put(tty); status = inb(port->ioaddr + UART_MSR); if (status & UART_MSR_DCD) ms.dcd = 1; @@ -1704,15 +1707,18 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp) me->up_txcnt[p] = port->mon_data.up_txcnt; me->modem_status[p] = port->mon_data.modem_status; - me->baudrate[p] = tty_get_baud_rate(port->port.tty); + tty = tty_port_tty_get(&port->port); - if (!port->port.tty || !port->port.tty->termios) { + if (!tty || !tty->termios) { cflag = port->normal_termios.c_cflag; iflag = port->normal_termios.c_iflag; + me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios); } else { - cflag = port->port.tty->termios->c_cflag; - iflag = port->port.tty->termios->c_iflag; + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; + me->baudrate[p] = tty_get_baud_rate(tty); } + tty_kref_put(tty); me->databits[p] = cflag & CSIZE; me->stopbits[p] = cflag & CSTOPB; @@ -1822,12 +1828,12 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, switch (cmd) { case TIOCGSERIAL: lock_kernel(); - retval = mxser_get_serial_info(info, argp); + retval = mxser_get_serial_info(tty, argp); unlock_kernel(); return retval; case TIOCSSERIAL: lock_kernel(); - retval = mxser_set_serial_info(info, argp); + retval = mxser_set_serial_info(tty, argp); unlock_kernel(); return retval; case TIOCSERGETLSR: /* Get line status register */ @@ -1896,7 +1902,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, lock_kernel(); status = mxser_get_msr(info->ioaddr, 1, tty->index); - mxser_check_modem_status(info, status); + mxser_check_modem_status(tty, info, status); mcr = inb(info->ioaddr + UART_MCR); if (mcr & MOXA_MUST_MCR_XON_FLAG) @@ -1909,7 +1915,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, else info->mon_data.hold_reason |= NPPI_NOTIFY_XOFFXENT; - if (info->port.tty->hw_stopped) + if (tty->hw_stopped) info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD; else info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD; @@ -1958,7 +1964,7 @@ static void mxser_stoprx(struct tty_struct *tty) } } - if (info->port.tty->termios->c_cflag & CRTSCTS) { + if (tty->termios->c_cflag & CRTSCTS) { info->MCR &= ~UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -1995,7 +2001,7 @@ static void mxser_unthrottle(struct tty_struct *tty) } } - if (info->port.tty->termios->c_cflag & CRTSCTS) { + if (tty->termios->c_cflag & CRTSCTS) { info->MCR |= UART_MCR_RTS; outb(info->MCR, info->ioaddr + UART_MCR); } @@ -2040,7 +2046,7 @@ static void mxser_set_termios(struct tty_struct *tty, struct ktermios *old_termi unsigned long flags; spin_lock_irqsave(&info->slock, flags); - mxser_change_speed(info, old_termios); + mxser_change_speed(tty, old_termios); spin_unlock_irqrestore(&info->slock, flags); if ((old_termios->c_cflag & CRTSCTS) && @@ -2138,10 +2144,10 @@ static void mxser_hangup(struct tty_struct *tty) struct mxser_port *info = tty->driver_data; mxser_flush_buffer(tty); - mxser_shutdown(info); + mxser_shutdown(tty); info->port.count = 0; info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; + tty_port_tty_set(&info->port, NULL); wake_up_interruptible(&info->port.open_wait); } @@ -2164,9 +2170,9 @@ static int mxser_rs_break(struct tty_struct *tty, int break_state) return 0; } -static void mxser_receive_chars(struct mxser_port *port, int *status) +static void mxser_receive_chars(struct tty_struct *tty, + struct mxser_port *port, int *status) { - struct tty_struct *tty = port->port.tty; unsigned char ch, gdl; int ignored = 0; int cnt = 0; @@ -2174,9 +2180,8 @@ static void mxser_receive_chars(struct mxser_port *port, int *status) int max = 256; recv_room = tty->receive_room; - if ((recv_room == 0) && (!port->ldisc_stop_rx)) + if (recv_room == 0 && !port->ldisc_stop_rx) mxser_stoprx(tty); - if (port->board->chip_flag != MOXA_OTHER_UART) { if (*status & UART_LSR_SPECIAL) @@ -2253,7 +2258,7 @@ intr_old: } while (*status & UART_LSR_DR); end_intr: - mxvar_log.rxcnt[port->port.tty->index] += cnt; + mxvar_log.rxcnt[tty->index] += cnt; port->mon_data.rxcnt += cnt; port->mon_data.up_rxcnt += cnt; @@ -2267,14 +2272,14 @@ end_intr: spin_lock(&port->slock); } -static void mxser_transmit_chars(struct mxser_port *port) +static void mxser_transmit_chars(struct tty_struct *tty, struct mxser_port *port) { int count, cnt; if (port->x_char) { outb(port->x_char, port->ioaddr + UART_TX); port->x_char = 0; - mxvar_log.txcnt[port->port.tty->index]++; + mxvar_log.txcnt[tty->index]++; port->mon_data.txcnt++; port->mon_data.up_txcnt++; port->icount.tx++; @@ -2284,8 +2289,8 @@ static void mxser_transmit_chars(struct mxser_port *port) if (port->port.xmit_buf == NULL) return; - if ((port->xmit_cnt <= 0) || port->port.tty->stopped || - (port->port.tty->hw_stopped && + if (port->xmit_cnt <= 0 || tty->stopped || + (tty->hw_stopped && (port->type != PORT_16550A) && (!port->board->chip_flag))) { port->IER &= ~UART_IER_THRI; @@ -2302,14 +2307,14 @@ static void mxser_transmit_chars(struct mxser_port *port) if (--port->xmit_cnt <= 0) break; } while (--count > 0); - mxvar_log.txcnt[port->port.tty->index] += (cnt - port->xmit_cnt); + mxvar_log.txcnt[tty->index] += (cnt - port->xmit_cnt); port->mon_data.txcnt += (cnt - port->xmit_cnt); port->mon_data.up_txcnt += (cnt - port->xmit_cnt); port->icount.tx += (cnt - port->xmit_cnt); - if (port->xmit_cnt < WAKEUP_CHARS) - tty_wakeup(port->port.tty); + if (port->xmit_cnt < WAKEUP_CHARS && tty) + tty_wakeup(tty); if (port->xmit_cnt <= 0) { port->IER &= ~UART_IER_THRI; @@ -2328,6 +2333,7 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) int max, irqbits, bits, msr; unsigned int int_cnt, pass_counter = 0; int handled = IRQ_NONE; + struct tty_struct *tty; for (i = 0; i < MXSER_BOARDS; i++) if (dev_id == &mxser_boards[i]) { @@ -2360,13 +2366,15 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) if (iir & UART_IIR_NO_INT) break; iir &= MOXA_MUST_IIR_MASK; - if (!port->port.tty || + tty = tty_port_tty_get(&port->port); + if (!tty || (port->port.flags & ASYNC_CLOSING) || !(port->port.flags & ASYNC_INITIALIZED)) { status = inb(port->ioaddr + UART_LSR); outb(0x27, port->ioaddr + UART_FCR); inb(port->ioaddr + UART_MSR); + tty_kref_put(tty); break; } @@ -2387,27 +2395,28 @@ static irqreturn_t mxser_interrupt(int irq, void *dev_id) iir == MOXA_MUST_IIR_RDA || iir == MOXA_MUST_IIR_RTO || iir == MOXA_MUST_IIR_LSR) - mxser_receive_chars(port, + mxser_receive_chars(tty, port, &status); } else { status &= port->read_status_mask; if (status & UART_LSR_DR) - mxser_receive_chars(port, + mxser_receive_chars(tty, port, &status); } msr = inb(port->ioaddr + UART_MSR); if (msr & UART_MSR_ANY_DELTA) - mxser_check_modem_status(port, msr); + mxser_check_modem_status(tty, port, msr); if (port->board->chip_flag) { if (iir == 0x02 && (status & UART_LSR_THRE)) - mxser_transmit_chars(port); + mxser_transmit_chars(tty, port); } else { if (status & UART_LSR_THRE) - mxser_transmit_chars(port); + mxser_transmit_chars(tty, port); } + tty_kref_put(tty); } while (int_cnt++ < MXSER_ISR_PASS_LIMIT); spin_unlock(&port->slock); } diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 69ec6399c71..bacb3e2872a 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -764,7 +764,7 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, break; default: - error = n_tty_ioctl (tty, file, cmd, arg); + error = n_tty_ioctl_helper(tty, file, cmd, arg); break; } return error; diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index ae377aa473b..4a8215a89ad 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -372,14 +372,8 @@ static void remove_from_rx_queue(struct r3964_info *pInfo, static void put_char(struct r3964_info *pInfo, unsigned char ch) { struct tty_struct *tty = pInfo->tty; - - if (tty == NULL) - return; - /* FIXME: put_char should not be called from an IRQ */ - if (tty->ops->put_char) { - tty->ops->put_char(tty, ch); - } + tty_put_char(tty, ch); pInfo->bcc ^= ch; } diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 708c2b1dbe5..efbfe961265 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -26,7 +26,7 @@ * * 2002/03/18 Implemented n_tty_wakeup to send SIGIO POLL_OUTs to * waiting writing processes-Sapan Bhatia <sapan@corewars.org>. - * Also fixed a bug in BLOCKING mode where write_chan returns + * Also fixed a bug in BLOCKING mode where n_tty_write returns * EAGAIN */ @@ -99,6 +99,7 @@ static inline int tty_put_user(struct tty_struct *tty, unsigned char x, static void n_tty_set_room(struct tty_struct *tty) { + /* tty->read_cnt is not read locked ? */ int left = N_TTY_BUF_SIZE - tty->read_cnt - 1; /* @@ -121,6 +122,16 @@ static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty) } } +/** + * put_tty_queue - add character to tty + * @c: character + * @tty: tty device + * + * Add a character to the tty read_buf queue. This is done under the + * read_lock to serialize character addition and also to protect us + * against parallel reads or flushes + */ + static void put_tty_queue(unsigned char c, struct tty_struct *tty) { unsigned long flags; @@ -137,14 +148,11 @@ static void put_tty_queue(unsigned char c, struct tty_struct *tty) * check_unthrottle - allow new receive data * @tty; tty device * - * Check whether to call the driver.unthrottle function. - * We test the TTY_THROTTLED bit first so that it always - * indicates the current state. The decision about whether - * it is worth allowing more input has been taken by the caller. + * Check whether to call the driver unthrottle functions + * * Can sleep, may be called under the atomic_read_lock mutex but * this is not guaranteed. */ - static void check_unthrottle(struct tty_struct *tty) { if (tty->count) @@ -158,6 +166,8 @@ static void check_unthrottle(struct tty_struct *tty) * Reset the read buffer counters, clear the flags, * and make sure the driver is unthrottled. Called * from n_tty_open() and n_tty_flush_buffer(). + * + * Locking: tty_read_lock for read fields. */ static void reset_buffer_flags(struct tty_struct *tty) { @@ -181,7 +191,7 @@ static void reset_buffer_flags(struct tty_struct *tty) * at hangup) or when the N_TTY line discipline internally has to * clean the pending queue (for example some signals). * - * Locking: ctrl_lock + * Locking: ctrl_lock, read_lock. */ static void n_tty_flush_buffer(struct tty_struct *tty) @@ -207,6 +217,8 @@ static void n_tty_flush_buffer(struct tty_struct *tty) * * Report the number of characters buffered to be delivered to user * at this instant in time. + * + * Locking: read_lock */ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty) @@ -346,7 +358,7 @@ static int opost(unsigned char c, struct tty_struct *tty) * the simple cases normally found and helps to generate blocks of * symbols for the console driver and thus improve performance. * - * Called from write_chan under the tty layer write lock. Relies + * Called from n_tty_write under the tty layer write lock. Relies * on lock_kernel for the tty->column state. */ @@ -410,6 +422,8 @@ break_out: * * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * Relies on BKL for tty column locking */ static void echo_char(unsigned char c, struct tty_struct *tty) @@ -422,6 +436,12 @@ static void echo_char(unsigned char c, struct tty_struct *tty) opost(c, tty); } +/** + * finsh_erasing - complete erase + * @tty: tty doing the erase + * + * Relies on BKL for tty column locking + */ static inline void finish_erasing(struct tty_struct *tty) { if (tty->erasing) { @@ -439,6 +459,8 @@ static inline void finish_erasing(struct tty_struct *tty) * Perform erase and necessary output when an erase character is * present in the stream from the driver layer. Handles the complexities * of UTF-8 multibyte symbols. + * + * Locking: read_lock for tty buffers, BKL for column/erasing state */ static void eraser(unsigned char c, struct tty_struct *tty) @@ -447,6 +469,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) int head, seen_alnums, cnt; unsigned long flags; + /* FIXME: locking needed ? */ if (tty->read_head == tty->canon_head) { /* opost('\a', tty); */ /* what do you think? */ return; @@ -481,6 +504,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) } seen_alnums = 0; + /* FIXME: Locking ?? */ while (tty->read_head != tty->canon_head) { head = tty->read_head; @@ -583,6 +607,8 @@ static void eraser(unsigned char c, struct tty_struct *tty) * may caus terminal flushing to take place according to the termios * settings and character used. Called from the driver receive_buf * path so serialized. + * + * Locking: ctrl_lock, read_lock (both via flush buffer) */ static inline void isig(int sig, struct tty_struct *tty, int flush) @@ -1007,12 +1033,26 @@ int is_ignored(int sig) * and is protected from re-entry by the tty layer. The user is * guaranteed that this function will not be re-entered or in progress * when the ldisc is closed. + * + * Locking: Caller holds tty->termios_mutex */ static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old) { - if (!tty) - return; + int canon_change = 1; + BUG_ON(!tty); + + if (old) + canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON; + if (canon_change) { + memset(&tty->read_flags, 0, sizeof tty->read_flags); + tty->canon_head = tty->read_tail; + tty->canon_data = 0; + tty->erasing = 0; + } + + if (canon_change && !L_ICANON(tty) && tty->read_cnt) + wake_up_interruptible(&tty->read_wait); tty->icanon = (L_ICANON(tty) != 0); if (test_bit(TTY_HW_COOK_IN, &tty->flags)) { @@ -1143,7 +1183,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt) * @b: user data * @nr: size of data * - * Helper function to speed up read_chan. It is only called when + * Helper function to speed up n_tty_read. It is only called when * ICANON is off; it copies characters straight from the tty queue to * user space directly. It can be profitably called twice; once to * drain the space from the tail pointer to the (physical) end of the @@ -1210,7 +1250,7 @@ static int job_control(struct tty_struct *tty, struct file *file) if (file->f_op->write != redirected_tty_write && current->signal->tty == tty) { if (!tty->pgrp) - printk(KERN_ERR "read_chan: no tty->pgrp!\n"); + printk(KERN_ERR "n_tty_read: no tty->pgrp!\n"); else if (task_pgrp(current) != tty->pgrp) { if (is_ignored(SIGTTIN) || is_current_pgrp_orphaned()) @@ -1225,7 +1265,7 @@ static int job_control(struct tty_struct *tty, struct file *file) /** - * read_chan - read function for tty + * n_tty_read - read function for tty * @tty: tty device * @file: file object * @buf: userspace buffer pointer @@ -1239,7 +1279,7 @@ static int job_control(struct tty_struct *tty, struct file *file) * This code must be sure never to sleep through a hangup. */ -static ssize_t read_chan(struct tty_struct *tty, struct file *file, +static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr) { unsigned char __user *b = buf; @@ -1254,10 +1294,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, do_it_again: - if (!tty->read_buf) { - printk(KERN_ERR "n_tty_read_chan: read_buf == NULL?!?\n"); - return -EIO; - } + BUG_ON(!tty->read_buf); c = job_control(tty, file); if (c < 0) @@ -1444,7 +1481,7 @@ do_it_again: } /** - * write_chan - write function for tty + * n_tty_write - write function for tty * @tty: tty device * @file: file object * @buf: userspace buffer pointer @@ -1458,7 +1495,7 @@ do_it_again: * This code must be sure never to sleep through a hangup. */ -static ssize_t write_chan(struct tty_struct *tty, struct file *file, +static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr) { const unsigned char *b = buf; @@ -1532,7 +1569,7 @@ break_out: } /** - * normal_poll - poll method for N_TTY + * n_tty_poll - poll method for N_TTY * @tty: terminal device * @file: file accessing it * @wait: poll table @@ -1545,7 +1582,7 @@ break_out: * Called without the kernel lock held - fine */ -static unsigned int normal_poll(struct tty_struct *tty, struct file *file, +static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) { unsigned int mask = 0; @@ -1573,6 +1610,44 @@ static unsigned int normal_poll(struct tty_struct *tty, struct file *file, return mask; } +static unsigned long inq_canon(struct tty_struct *tty) +{ + int nr, head, tail; + + if (!tty->canon_data) + return 0; + head = tty->canon_head; + tail = tty->read_tail; + nr = (head - tail) & (N_TTY_BUF_SIZE-1); + /* Skip EOF-chars.. */ + while (head != tail) { + if (test_bit(tail, tty->read_flags) && + tty->read_buf[tail] == __DISABLED_CHAR) + nr--; + tail = (tail+1) & (N_TTY_BUF_SIZE-1); + } + return nr; +} + +static int n_tty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int retval; + + switch (cmd) { + case TIOCOUTQ: + return put_user(tty_chars_in_buffer(tty), (int __user *) arg); + case TIOCINQ: + /* FIXME: Locking */ + retval = tty->read_cnt; + if (L_ICANON(tty)) + retval = inq_canon(tty); + return put_user(retval, (unsigned int __user *) arg); + default: + return n_tty_ioctl_helper(tty, file, cmd, arg); + } +} + struct tty_ldisc_ops tty_ldisc_N_TTY = { .magic = TTY_LDISC_MAGIC, .name = "n_tty", @@ -1580,11 +1655,11 @@ struct tty_ldisc_ops tty_ldisc_N_TTY = { .close = n_tty_close, .flush_buffer = n_tty_flush_buffer, .chars_in_buffer = n_tty_chars_in_buffer, - .read = read_chan, - .write = write_chan, + .read = n_tty_read, + .write = n_tty_write, .ioctl = n_tty_ioctl, .set_termios = n_tty_set_termios, - .poll = normal_poll, + .poll = n_tty_poll, .receive_buf = n_tty_receive_buf, .write_wakeup = n_tty_write_wakeup }; diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index 66a0f931c66..9a34a193528 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1599,7 +1599,10 @@ static int ntty_open(struct tty_struct *tty, struct file *file) return 0; } -/* Called when the userspace process close the tty, /dev/noz*. */ +/* Called when the userspace process close the tty, /dev/noz*. Also + called immediately if ntty_open fails in which case tty->driver_data + will be NULL an we exit by the first return */ + static void ntty_close(struct tty_struct *tty, struct file *file) { struct nozomi *dc = get_dc_by_tty(tty); diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c index 3a23e7694d5..569f2f7743a 100644 --- a/drivers/char/pcmcia/ipwireless/tty.c +++ b/drivers/char/pcmcia/ipwireless/tty.c @@ -276,6 +276,7 @@ static int ipw_write_room(struct tty_struct *linux_tty) struct ipw_tty *tty = linux_tty->driver_data; int room; + /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; @@ -397,6 +398,7 @@ static int set_control_lines(struct ipw_tty *tty, unsigned int set, static int ipw_tiocmget(struct tty_struct *linux_tty, struct file *file) { struct ipw_tty *tty = linux_tty->driver_data; + /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; @@ -412,6 +414,7 @@ ipw_tiocmset(struct tty_struct *linux_tty, struct file *file, unsigned int set, unsigned int clear) { struct ipw_tty *tty = linux_tty->driver_data; + /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; @@ -433,6 +436,8 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, if (!tty->open_count) return -EINVAL; + /* FIXME: Exactly how is the tty object locked here .. */ + switch (cmd) { case TIOCGSERIAL: return ipwireless_get_serial_info(tty, (void __user *) arg); @@ -467,13 +472,6 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, } return 0; - case TCGETS: - case TCGETA: - return n_tty_ioctl(linux_tty, file, cmd, arg); - - case TCFLSH: - return n_tty_ioctl(linux_tty, file, cmd, arg); - case FIONREAD: { int val = 0; @@ -482,10 +480,11 @@ static int ipw_ioctl(struct tty_struct *linux_tty, struct file *file, return -EFAULT; } return 0; + case TCFLSH: + return tty_perform_flush(linux_tty, arg); } } - - return -ENOIOCTLCMD; + return tty_mode_ioctl(linux_tty, file, cmd , arg); } static int add_tty(dev_node_t *nodesp, int j, @@ -588,6 +587,8 @@ void ipwireless_tty_free(struct ipw_tty *tty) tty_hangup(ttyj->linux_tty); /* Wait till the tty_hangup has completed */ flush_scheduled_work(); + /* FIXME: Exactly how is the tty object locked here + against a parallel ioctl etc */ mutex_lock(&ttyj->ipw_tty_mutex); } while (ttyj->open_count) diff --git a/drivers/char/pty.c b/drivers/char/pty.c index 76b27932d22..6d4582712b1 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -8,10 +8,12 @@ * Added TTY_DO_WRITE_WAKEUP to enable n_tty to send POLL_OUT to * waiting writers -- Sapan Bhatia <sapan@corewars.org> * - * + * When reading this code see also fs/devpts. In particular note that the + * driver_data field is used by the devpts side as a binding to the devpts + * inode. */ -#include <linux/module.h> /* For EXPORT_SYMBOL */ +#include <linux/module.h> #include <linux/errno.h> #include <linux/interrupt.h> @@ -23,26 +25,25 @@ #include <linux/mm.h> #include <linux/init.h> #include <linux/sysctl.h> - -#include <asm/uaccess.h> -#include <asm/system.h> +#include <linux/device.h> +#include <linux/uaccess.h> #include <linux/bitops.h> #include <linux/devpts_fs.h> +#include <asm/system.h> + /* These are global because they are accessed in tty_io.c */ #ifdef CONFIG_UNIX98_PTYS struct tty_driver *ptm_driver; static struct tty_driver *pts_driver; #endif -static void pty_close(struct tty_struct * tty, struct file * filp) +static void pty_close(struct tty_struct *tty, struct file *filp) { - if (!tty) - return; - if (tty->driver->subtype == PTY_TYPE_MASTER) { - if (tty->count > 1) - printk("master pty_close: count = %d!!\n", tty->count); - } else { + BUG_ON(!tty); + if (tty->driver->subtype == PTY_TYPE_MASTER) + WARN_ON(tty->count > 1); + else { if (tty->count > 2) return; } @@ -59,7 +60,7 @@ static void pty_close(struct tty_struct * tty, struct file * filp) set_bit(TTY_OTHER_CLOSED, &tty->flags); #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) - devpts_pty_kill(tty->index); + devpts_pty_kill(tty->link); #endif tty_vhangup(tty->link); } @@ -69,13 +70,13 @@ static void pty_close(struct tty_struct * tty, struct file * filp) * The unthrottle routine is called by the line discipline to signal * that it can receive more characters. For PTY's, the TTY_THROTTLED * flag is always set, to force the line discipline to always call the - * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE + * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE * characters in the queue. This is necessary since each time this * happens, we need to wake up any sleeping processes that could be * (1) trying to send data to the pty, or (2) waiting in wait_until_sent() * for the pty buffer to be drained. */ -static void pty_unthrottle(struct tty_struct * tty) +static void pty_unthrottle(struct tty_struct *tty) { struct tty_struct *o_tty = tty->link; @@ -87,7 +88,7 @@ static void pty_unthrottle(struct tty_struct * tty) } /* - * WSH 05/24/97: modified to + * WSH 05/24/97: modified to * (1) use space in tty->flip instead of a shared temp buffer * The flip buffers aren't being used for a pty, so there's lots * of space available. The buffer is protected by a per-pty @@ -100,7 +101,8 @@ static void pty_unthrottle(struct tty_struct * tty) * not our partners. We can't just take the other one blindly without * risking deadlocks. */ -static int pty_write(struct tty_struct * tty, const unsigned char *buf, int count) +static int pty_write(struct tty_struct *tty, const unsigned char *buf, + int count) { struct tty_struct *to = tty->link; int c; @@ -112,7 +114,7 @@ static int pty_write(struct tty_struct * tty, const unsigned char *buf, int coun if (c > count) c = count; to->ldisc.ops->receive_buf(to, buf, NULL, c); - + return c; } @@ -128,17 +130,17 @@ static int pty_write_room(struct tty_struct *tty) /* * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior - * The chars_in_buffer() value is used by the ldisc select() function + * The chars_in_buffer() value is used by the ldisc select() function * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256). * The pty driver chars_in_buffer() Master/Slave must behave differently: * * The Master side needs to allow typed-ahead commands to accumulate * while being canonicalized, so we report "our buffer" as empty until * some threshold is reached, and then report the count. (Any count > - * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock - * the count returned must be 0 if no canonical data is available to be + * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock + * the count returned must be 0 if no canonical data is available to be * read. (The N_TTY ldisc.chars_in_buffer now knows this.) - * + * * The Slave side passes all characters in raw mode to the Master side's * buffer where they can be read immediately, so in this case we can * return the true count in the buffer. @@ -155,21 +157,22 @@ static int pty_chars_in_buffer(struct tty_struct *tty) /* The ldisc must report 0 if no characters available to be read */ count = to->ldisc.ops->chars_in_buffer(to); - if (tty->driver->subtype == PTY_TYPE_SLAVE) return count; + if (tty->driver->subtype == PTY_TYPE_SLAVE) + return count; - /* Master side driver ... if the other side's read buffer is less than + /* Master side driver ... if the other side's read buffer is less than * half full, return 0 to allow writers to proceed; otherwise return - * the count. This leaves a comfortable margin to avoid overflow, + * the count. This leaves a comfortable margin to avoid overflow, * and still allows half a buffer's worth of typed-ahead commands. */ - return ((count < N_TTY_BUF_SIZE/2) ? 0 : count); + return (count < N_TTY_BUF_SIZE/2) ? 0 : count; } /* Set the lock flag on a pty */ -static int pty_set_lock(struct tty_struct *tty, int __user * arg) +static int pty_set_lock(struct tty_struct *tty, int __user *arg) { int val; - if (get_user(val,arg)) + if (get_user(val, arg)) return -EFAULT; if (val) set_bit(TTY_PTY_LOCK, &tty->flags); @@ -182,13 +185,13 @@ static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; unsigned long flags; - + if (!to) return; - + if (to->ldisc.ops->flush_buffer) to->ldisc.ops->flush_buffer(to); - + if (to->packet) { spin_lock_irqsave(&tty->ctrl_lock, flags); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; @@ -197,7 +200,7 @@ static void pty_flush_buffer(struct tty_struct *tty) } } -static int pty_open(struct tty_struct *tty, struct file * filp) +static int pty_open(struct tty_struct *tty, struct file *filp) { int retval = -ENODEV; @@ -220,13 +223,65 @@ out: return retval; } -static void pty_set_termios(struct tty_struct *tty, struct ktermios *old_termios) +static void pty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + tty->termios->c_cflag &= ~(CSIZE | PARENB); + tty->termios->c_cflag |= (CS8 | CREAD); +} + +static int pty_install(struct tty_driver *driver, struct tty_struct *tty) { - tty->termios->c_cflag &= ~(CSIZE | PARENB); - tty->termios->c_cflag |= (CS8 | CREAD); + struct tty_struct *o_tty; + int idx = tty->index; + int retval; + + o_tty = alloc_tty_struct(); + if (!o_tty) + return -ENOMEM; + if (!try_module_get(driver->other->owner)) { + /* This cannot in fact currently happen */ + free_tty_struct(o_tty); + return -ENOMEM; + } + initialize_tty_struct(o_tty, driver->other, idx); + + /* We always use new tty termios data so we can do this + the easy way .. */ + retval = tty_init_termios(tty); + if (retval) + goto free_mem_out; + + retval = tty_init_termios(o_tty); + if (retval) { + tty_free_termios(tty); + goto free_mem_out; + } + + /* + * Everything allocated ... set up the o_tty structure. + */ + driver->other->ttys[idx] = o_tty; + tty_driver_kref_get(driver->other); + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; + + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; +free_mem_out: + module_put(o_tty->driver->owner); + free_tty_struct(o_tty); + return -ENOMEM; } + static const struct tty_operations pty_ops = { + .install = pty_install, .open = pty_open, .close = pty_close, .write = pty_write, @@ -329,8 +384,11 @@ static inline void legacy_pty_init(void) { } * Otherwise one can eat up all kernel memory by opening /dev/ptmx repeatedly. */ int pty_limit = NR_UNIX98_PTY_DEFAULT; -static int pty_limit_min = 0; +static int pty_limit_min; static int pty_limit_max = NR_UNIX98_PTY_MAX; +static int pty_count; + +static struct cdev ptmx_cdev; static struct ctl_table pty_table[] = { { @@ -348,6 +406,7 @@ static struct ctl_table pty_table[] = { .procname = "nr", .maxlen = sizeof(int), .mode = 0444, + .data = &pty_count, .proc_handler = &proc_dointvec, }, { .ctl_name = 0 @@ -388,7 +447,127 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, return -ENOIOCTLCMD; } +/** + * ptm_unix98_lookup - find a pty master + * @driver: ptm driver + * @idx: tty index + * + * Look up a pty master device. Called under the tty_mutex for now. + * This provides our locking. + */ + +static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver, + struct inode *ptm_inode, int idx) +{ + struct tty_struct *tty = devpts_get_tty(ptm_inode, idx); + if (tty) + tty = tty->link; + return tty; +} + +/** + * pts_unix98_lookup - find a pty slave + * @driver: pts driver + * @idx: tty index + * + * Look up a pty master device. Called under the tty_mutex for now. + * This provides our locking. + */ + +static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver, + struct inode *pts_inode, int idx) +{ + struct tty_struct *tty = devpts_get_tty(pts_inode, idx); + /* Master must be open before slave */ + if (!tty) + return ERR_PTR(-EIO); + return tty; +} + +static void pty_unix98_shutdown(struct tty_struct *tty) +{ + /* We have our own method as we don't use the tty index */ + kfree(tty->termios); +} + +/* We have no need to install and remove our tty objects as devpts does all + the work for us */ + +static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct tty_struct *o_tty; + int idx = tty->index; + + o_tty = alloc_tty_struct(); + if (!o_tty) + return -ENOMEM; + if (!try_module_get(driver->other->owner)) { + /* This cannot in fact currently happen */ + free_tty_struct(o_tty); + return -ENOMEM; + } + initialize_tty_struct(o_tty, driver->other, idx); + + tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (tty->termios == NULL) + goto free_mem_out; + *tty->termios = driver->init_termios; + tty->termios_locked = tty->termios + 1; + + o_tty->termios = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (o_tty->termios == NULL) + goto free_mem_out; + *o_tty->termios = driver->other->init_termios; + o_tty->termios_locked = o_tty->termios + 1; + + tty_driver_kref_get(driver->other); + if (driver->subtype == PTY_TYPE_MASTER) + o_tty->count++; + /* Establish the links in both directions */ + tty->link = o_tty; + o_tty->link = tty; + /* + * All structures have been allocated, so now we install them. + * Failures after this point use release_tty to clean up, so + * there's no need to null out the local pointers. + */ + tty_driver_kref_get(driver); + tty->count++; + pty_count++; + return 0; +free_mem_out: + kfree(o_tty->termios); + module_put(o_tty->driver->owner); + free_tty_struct(o_tty); + kfree(tty->termios); + return -ENOMEM; +} + +static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + pty_count--; +} + +static const struct tty_operations ptm_unix98_ops = { + .lookup = ptm_unix98_lookup, + .install = pty_unix98_install, + .remove = pty_unix98_remove, + .open = pty_open, + .close = pty_close, + .write = pty_write, + .write_room = pty_write_room, + .flush_buffer = pty_flush_buffer, + .chars_in_buffer = pty_chars_in_buffer, + .unthrottle = pty_unthrottle, + .set_termios = pty_set_termios, + .ioctl = pty_unix98_ioctl, + .shutdown = pty_unix98_shutdown +}; + static const struct tty_operations pty_unix98_ops = { + .lookup = pts_unix98_lookup, + .install = pty_unix98_install, + .remove = pty_unix98_remove, .open = pty_open, .close = pty_close, .write = pty_write, @@ -397,9 +576,73 @@ static const struct tty_operations pty_unix98_ops = { .chars_in_buffer = pty_chars_in_buffer, .unthrottle = pty_unthrottle, .set_termios = pty_set_termios, - .ioctl = pty_unix98_ioctl + .shutdown = pty_unix98_shutdown }; +/** + * ptmx_open - open a unix 98 pty master + * @inode: inode of device file + * @filp: file pointer to tty + * + * Allocate a unix98 pty master device from the ptmx driver. + * + * Locking: tty_mutex protects the init_dev work. tty->count should + * protect the rest. + * allocated_ptys_lock handles the list of free pty numbers + */ + +static int __ptmx_open(struct inode *inode, struct file *filp) +{ + struct tty_struct *tty; + int retval; + int index; + + nonseekable_open(inode, filp); + + /* find a device that is not in use. */ + index = devpts_new_index(inode); + if (index < 0) + return index; + + mutex_lock(&tty_mutex); + tty = tty_init_dev(ptm_driver, index, 1); + mutex_unlock(&tty_mutex); + + if (IS_ERR(tty)) { + retval = PTR_ERR(tty); + goto out; + } + + set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ + filp->private_data = tty; + file_move(filp, &tty->tty_files); + + retval = devpts_pty_new(inode, tty->link); + if (retval) + goto out1; + + retval = ptm_driver->ops->open(tty, filp); + if (!retval) + return 0; +out1: + tty_release_dev(filp); + return retval; +out: + devpts_kill_index(inode, index); + return retval; +} + +static int ptmx_open(struct inode *inode, struct file *filp) +{ + int ret; + + lock_kernel(); + ret = __ptmx_open(inode, filp); + unlock_kernel(); + return ret; +} + +static struct file_operations ptmx_fops; static void __init unix98_pty_init(void) { @@ -427,7 +670,7 @@ static void __init unix98_pty_init(void) ptm_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; ptm_driver->other = pts_driver; - tty_set_operations(ptm_driver, &pty_unix98_ops); + tty_set_operations(ptm_driver, &ptm_unix98_ops); pts_driver->owner = THIS_MODULE; pts_driver->driver_name = "pty_slave"; @@ -443,16 +686,26 @@ static void __init unix98_pty_init(void) pts_driver->flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM; pts_driver->other = ptm_driver; - tty_set_operations(pts_driver, &pty_ops); - + tty_set_operations(pts_driver, &pty_unix98_ops); + if (tty_register_driver(ptm_driver)) panic("Couldn't register Unix98 ptm driver"); if (tty_register_driver(pts_driver)) panic("Couldn't register Unix98 pts driver"); - pty_table[1].data = &ptm_driver->refcount; register_sysctl_table(pty_root_table); + + /* Now create the /dev/ptmx special device */ + tty_default_fops(&ptmx_fops); + ptmx_fops.open = ptmx_open; + + cdev_init(&ptmx_cdev, &ptmx_fops); + if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || + register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) + panic("Couldn't register /dev/ptmx driver\n"); + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); } + #else static inline void unix98_pty_init(void) { } #endif diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c index f53d4d00faf..b47710c1788 100644 --- a/drivers/char/rtc.c +++ b/drivers/char/rtc.c @@ -88,12 +88,12 @@ #endif #ifdef CONFIG_SPARC32 -#include <linux/pci.h> -#include <linux/jiffies.h> -#include <asm/ebus.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <asm/io.h> static unsigned long rtc_port; -static int rtc_irq = PCI_IRQ_NONE; +static int rtc_irq; #endif #ifdef CONFIG_HPET_RTC_IRQ @@ -973,8 +973,8 @@ static int __init rtc_init(void) char *guess = NULL; #endif #ifdef CONFIG_SPARC32 - struct linux_ebus *ebus; - struct linux_ebus_device *edev; + struct device_node *ebus_dp; + struct of_device *op; #else void *r; #ifdef RTC_IRQ @@ -983,12 +983,16 @@ static int __init rtc_init(void) #endif #ifdef CONFIG_SPARC32 - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (strcmp(edev->prom_node->name, "rtc") == 0) { - rtc_port = edev->resource[0].start; - rtc_irq = edev->irqs[0]; - goto found; + for_each_node_by_name(ebus_dp, "ebus") { + struct device_node *dp; + for (dp = ebus_dp; dp; dp = dp->sibling) { + if (!strcmp(dp->name, "rtc")) { + op = of_find_device_by_node(dp); + if (op) { + rtc_port = op->resource[0].start; + rtc_irq = op->irqs[0]; + goto found; + } } } } @@ -997,7 +1001,7 @@ static int __init rtc_init(void) return -EIO; found: - if (rtc_irq == PCI_IRQ_NONE) { + if (!rtc_irq) { rtc_has_irq = 0; goto no_irq; } diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 19db1eb87c2..8b8f07a7f50 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -405,9 +405,9 @@ static unsigned int stl_baudrates[] = { static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg); static int stl_brdinit(struct stlbrd *brdp); -static int stl_getportstats(struct stlport *portp, comstats_t __user *cp); +static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp); static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp); -static int stl_waitcarrier(struct stlport *portp, struct file *filp); +static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, struct file *filp); /* * CD1400 uart specific handling functions. @@ -612,8 +612,9 @@ static struct class *stallion_class; static void stl_cd_change(struct stlport *portp) { unsigned int oldsigs = portp->sigs; + struct tty_struct *tty = tty_port_tty_get(&portp->port); - if (!portp->port.tty) + if (!tty) return; portp->sigs = stl_getsignals(portp); @@ -623,7 +624,8 @@ static void stl_cd_change(struct stlport *portp) if ((oldsigs & TIOCM_CD) && ((portp->sigs & TIOCM_CD) == 0)) if (portp->port.flags & ASYNC_CHECK_CD) - tty_hangup(portp->port.tty); + tty_hangup(tty); + tty_kref_put(tty); } /* @@ -734,7 +736,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * On the first open of the device setup the port hardware, and * initialize the per port data structure. */ - portp->port.tty = tty; + tty_port_tty_set(&portp->port, tty); tty->driver_data = portp; portp->port.count++; @@ -774,7 +776,7 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * then also we might have to wait for carrier. */ if (!(filp->f_flags & O_NONBLOCK)) - if ((rc = stl_waitcarrier(portp, filp)) != 0) + if ((rc = stl_waitcarrier(tty, portp, filp)) != 0) return rc; portp->port.flags |= ASYNC_NORMAL_ACTIVE; @@ -789,7 +791,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp) * maybe because if we are clocal then we don't need to wait... */ -static int stl_waitcarrier(struct stlport *portp, struct file *filp) +static int stl_waitcarrier(struct tty_struct *tty, struct stlport *portp, + struct file *filp) { unsigned long flags; int rc, doclocal; @@ -801,7 +804,7 @@ static int stl_waitcarrier(struct stlport *portp, struct file *filp) spin_lock_irqsave(&stallion_lock, flags); - if (portp->port.tty->termios->c_cflag & CLOCAL) + if (tty->termios->c_cflag & CLOCAL) doclocal++; portp->openwaitcnt++; @@ -846,8 +849,6 @@ static void stl_flushbuffer(struct tty_struct *tty) pr_debug("stl_flushbuffer(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -865,8 +866,6 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout) pr_debug("stl_waituntilsent(tty=%p,timeout=%d)\n", tty, timeout); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -949,7 +948,7 @@ static void stl_close(struct tty_struct *tty, struct file *filp) tty_ldisc_flush(tty); tty->closing = 0; - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); if (portp->openwaitcnt) { if (portp->close_delay) @@ -1033,8 +1032,6 @@ static int stl_putchar(struct tty_struct *tty, unsigned char ch) pr_debug("stl_putchar(tty=%p,ch=%x)\n", tty, ch); - if (tty == NULL) - return -EINVAL; portp = tty->driver_data; if (portp == NULL) return -EINVAL; @@ -1070,8 +1067,6 @@ static void stl_flushchars(struct tty_struct *tty) pr_debug("stl_flushchars(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1090,8 +1085,6 @@ static int stl_writeroom(struct tty_struct *tty) pr_debug("stl_writeroom(tty=%p)\n", tty); - if (tty == NULL) - return 0; portp = tty->driver_data; if (portp == NULL) return 0; @@ -1122,8 +1115,6 @@ static int stl_charsinbuffer(struct tty_struct *tty) pr_debug("stl_charsinbuffer(tty=%p)\n", tty); - if (tty == NULL) - return 0; portp = tty->driver_data; if (portp == NULL) return 0; @@ -1183,8 +1174,9 @@ static int stl_getserial(struct stlport *portp, struct serial_struct __user *sp) * just quietly ignore any requests to change irq, etc. */ -static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp) +static int stl_setserial(struct tty_struct *tty, struct serial_struct __user *sp) { + struct stlport * portp = tty->driver_data; struct serial_struct sio; pr_debug("stl_setserial(portp=%p,sp=%p)\n", portp, sp); @@ -1205,7 +1197,7 @@ static int stl_setserial(struct stlport *portp, struct serial_struct __user *sp) portp->close_delay = sio.close_delay; portp->closing_wait = sio.closing_wait; portp->custom_divisor = sio.custom_divisor; - stl_setport(portp, portp->port.tty->termios); + stl_setport(portp, tty->termios); return 0; } @@ -1215,8 +1207,6 @@ static int stl_tiocmget(struct tty_struct *tty, struct file *file) { struct stlport *portp; - if (tty == NULL) - return -ENODEV; portp = tty->driver_data; if (portp == NULL) return -ENODEV; @@ -1232,8 +1222,6 @@ static int stl_tiocmset(struct tty_struct *tty, struct file *file, struct stlport *portp; int rts = -1, dtr = -1; - if (tty == NULL) - return -ENODEV; portp = tty->driver_data; if (portp == NULL) return -ENODEV; @@ -1262,8 +1250,6 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd pr_debug("stl_ioctl(tty=%p,file=%p,cmd=%x,arg=%lx)\n", tty, file, cmd, arg); - if (tty == NULL) - return -ENODEV; portp = tty->driver_data; if (portp == NULL) return -ENODEV; @@ -1282,10 +1268,10 @@ static int stl_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd rc = stl_getserial(portp, argp); break; case TIOCSSERIAL: - rc = stl_setserial(portp, argp); + rc = stl_setserial(tty, argp); break; case COM_GETPORTSTATS: - rc = stl_getportstats(portp, argp); + rc = stl_getportstats(tty, portp, argp); break; case COM_CLRPORTSTATS: rc = stl_clrportstats(portp, argp); @@ -1317,8 +1303,6 @@ static void stl_start(struct tty_struct *tty) pr_debug("stl_start(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1334,8 +1318,6 @@ static void stl_settermios(struct tty_struct *tty, struct ktermios *old) pr_debug("stl_settermios(tty=%p,old=%p)\n", tty, old); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1369,8 +1351,6 @@ static void stl_throttle(struct tty_struct *tty) pr_debug("stl_throttle(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1389,8 +1369,6 @@ static void stl_unthrottle(struct tty_struct *tty) pr_debug("stl_unthrottle(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1410,8 +1388,6 @@ static void stl_stop(struct tty_struct *tty) pr_debug("stl_stop(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1432,8 +1408,6 @@ static void stl_hangup(struct tty_struct *tty) pr_debug("stl_hangup(tty=%p)\n", tty); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1452,7 +1426,7 @@ static void stl_hangup(struct tty_struct *tty) portp->tx.head = NULL; portp->tx.tail = NULL; } - portp->port.tty = NULL; + tty_port_tty_set(&portp->port, NULL); portp->port.flags &= ~ASYNC_NORMAL_ACTIVE; portp->port.count = 0; wake_up_interruptible(&portp->port.open_wait); @@ -1466,8 +1440,6 @@ static int stl_breakctl(struct tty_struct *tty, int state) pr_debug("stl_breakctl(tty=%p,state=%d)\n", tty, state); - if (tty == NULL) - return -EINVAL; portp = tty->driver_data; if (portp == NULL) return -EINVAL; @@ -1484,8 +1456,6 @@ static void stl_sendxchar(struct tty_struct *tty, char ch) pr_debug("stl_sendxchar(tty=%p,ch=%x)\n", tty, ch); - if (tty == NULL) - return; portp = tty->driver_data; if (portp == NULL) return; @@ -1805,7 +1775,7 @@ static int __devinit stl_initports(struct stlbrd *brdp, struct stlpanel *panelp) "(size=%Zd)\n", sizeof(struct stlport)); break; } - + tty_port_init(&portp->port); portp->magic = STL_PORTMAGIC; portp->portnr = i; portp->brdnr = panelp->brdnr; @@ -1832,6 +1802,7 @@ static void stl_cleanup_panels(struct stlbrd *brdp) struct stlpanel *panelp; struct stlport *portp; unsigned int j, k; + struct tty_struct *tty; for (j = 0; j < STL_MAXPANELS; j++) { panelp = brdp->panels[j]; @@ -1841,8 +1812,11 @@ static void stl_cleanup_panels(struct stlbrd *brdp) portp = panelp->ports[k]; if (portp == NULL) continue; - if (portp->port.tty != NULL) - stl_hangup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty != NULL) { + stl_hangup(tty); + tty_kref_put(tty); + } kfree(portp->tx.buf); kfree(portp); } @@ -2498,7 +2472,7 @@ static struct stlport *stl_getport(int brdnr, int panelnr, int portnr) * what port to get stats for (used through board control device). */ -static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) +static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp) { comstats_t stl_comstats; unsigned char *head, *tail; @@ -2525,18 +2499,17 @@ static int stl_getportstats(struct stlport *portp, comstats_t __user *cp) portp->stats.rxbuffered = 0; spin_lock_irqsave(&stallion_lock, flags); - if (portp->port.tty != NULL) - if (portp->port.tty->driver_data == portp) { - portp->stats.ttystate = portp->port.tty->flags; - /* No longer available as a statistic */ - portp->stats.rxbuffered = 1; /*portp->port.tty->flip.count; */ - if (portp->port.tty->termios != NULL) { - portp->stats.cflags = portp->port.tty->termios->c_cflag; - portp->stats.iflags = portp->port.tty->termios->c_iflag; - portp->stats.oflags = portp->port.tty->termios->c_oflag; - portp->stats.lflags = portp->port.tty->termios->c_lflag; - } + if (tty != NULL && portp->port.tty == tty) { + portp->stats.ttystate = tty->flags; + /* No longer available as a statistic */ + portp->stats.rxbuffered = 1; /*tty->flip.count; */ + if (tty->termios != NULL) { + portp->stats.cflags = tty->termios->c_cflag; + portp->stats.iflags = tty->termios->c_iflag; + portp->stats.oflags = tty->termios->c_oflag; + portp->stats.lflags = tty->termios->c_lflag; } + } spin_unlock_irqrestore(&stallion_lock, flags); head = portp->tx.head; @@ -2640,7 +2613,7 @@ static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, uns switch (cmd) { case COM_GETPORTSTATS: - rc = stl_getportstats(NULL, argp); + rc = stl_getportstats(NULL, NULL, argp); break; case COM_CLRPORTSTATS: rc = stl_clrportstats(NULL, argp); @@ -3243,7 +3216,7 @@ static void stl_cd1400flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -3288,6 +3261,7 @@ static void stl_cd1400flowctrl(struct stlport *portp, int state) BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -3305,7 +3279,7 @@ static void stl_cd1400sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -3325,6 +3299,7 @@ static void stl_cd1400sendflow(struct stlport *portp, int state) } BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -3478,6 +3453,7 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) int len, stlen; char *head, *tail; unsigned char ioack, srer; + struct tty_struct *tty; pr_debug("stl_cd1400txisr(panelp=%p,ioaddr=%x)\n", panelp, ioaddr); @@ -3504,8 +3480,11 @@ static void stl_cd1400txisr(struct stlpanel *panelp, int ioaddr) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->port.tty) - tty_wakeup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } } if (len == 0) { @@ -3569,7 +3548,7 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) return; } portp = panelp->ports[(ioack >> 3)]; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { outb((RDCR + portp->uartaddr), ioaddr); @@ -3633,10 +3612,12 @@ static void stl_cd1400rxisr(struct stlpanel *panelp, int ioaddr) } } else { printk("STALLION: bad RX interrupt ack value=%x\n", ioack); + tty_kref_put(tty); return; } stl_rxalldone: + tty_kref_put(tty); outb((EOSRR + portp->uartaddr), ioaddr); outb(0, (ioaddr + EREG_DATA)); } @@ -4175,7 +4156,7 @@ static void stl_sc26198flowctrl(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -4226,6 +4207,7 @@ static void stl_sc26198flowctrl(struct stlport *portp, int state) BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -4244,7 +4226,7 @@ static void stl_sc26198sendflow(struct stlport *portp, int state) if (portp == NULL) return; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); if (tty == NULL) return; @@ -4269,6 +4251,7 @@ static void stl_sc26198sendflow(struct stlport *portp, int state) } BRDDISABLE(portp->brdnr); spin_unlock_irqrestore(&brd_lock, flags); + tty_kref_put(tty); } /*****************************************************************************/ @@ -4408,6 +4391,7 @@ static void stl_sc26198intr(struct stlpanel *panelp, unsigned int iobase) static void stl_sc26198txisr(struct stlport *portp) { + struct tty_struct *tty; unsigned int ioaddr; unsigned char mr0; int len, stlen; @@ -4422,8 +4406,11 @@ static void stl_sc26198txisr(struct stlport *portp) if ((len == 0) || ((len < STL_TXBUFLOW) && (test_bit(ASYI_TXLOW, &portp->istate) == 0))) { set_bit(ASYI_TXLOW, &portp->istate); - if (portp->port.tty) - tty_wakeup(portp->port.tty); + tty = tty_port_tty_get(&portp->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } } if (len == 0) { @@ -4476,7 +4463,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) pr_debug("stl_sc26198rxisr(portp=%p,iack=%x)\n", portp, iack); - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); ioaddr = portp->ioaddr; outb(GIBCR, (ioaddr + XP_ADDR)); len = inb(ioaddr + XP_DATA) + 1; @@ -4515,6 +4502,7 @@ static void stl_sc26198rxisr(struct stlport *portp, unsigned int iack) stl_sc26198txunflow(portp, tty); } } + tty_kref_put(tty); } /*****************************************************************************/ @@ -4528,7 +4516,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char struct tty_struct *tty; unsigned int ioaddr; - tty = portp->port.tty; + tty = tty_port_tty_get(&portp->port); ioaddr = portp->ioaddr; if (status & SR_RXPARITY) @@ -4566,6 +4554,7 @@ static void stl_sc26198rxbadch(struct stlport *portp, unsigned char status, char if (status == 0) portp->stats.rxtotal++; } + tty_kref_put(tty); } /*****************************************************************************/ diff --git a/drivers/char/sx.c b/drivers/char/sx.c index c385206f9db..5b8d7a1aa3e 100644 --- a/drivers/char/sx.c +++ b/drivers/char/sx.c @@ -2504,7 +2504,7 @@ static void __devexit sx_remove_card(struct sx_board *board, del_timer(&board->timer); if (pdev) { #ifdef CONFIG_PCI - pci_iounmap(pdev, board->base); + pci_iounmap(pdev, board->base2); pci_release_region(pdev, IS_CF_BOARD(board) ? 3 : 2); #endif } else { @@ -2703,7 +2703,7 @@ static int __devinit sx_pci_probe(struct pci_dev *pdev, return 0; err_unmap: - pci_iounmap(pdev, board->base); + pci_iounmap(pdev, board->base2); err_reg: pci_release_region(pdev, reg); err_flag: diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index ae766d86845..1fee7034a38 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -954,72 +954,63 @@ EXPORT_SYMBOL_GPL(tpm_store_cancel); /* * Device file system interface to the TPM + * + * It's assured that the chip will be opened just once, + * by the check of is_open variable, which is protected + * by driver_lock. */ int tpm_open(struct inode *inode, struct file *file) { - int rc = 0, minor = iminor(inode); + int minor = iminor(inode); struct tpm_chip *chip = NULL, *pos; - lock_kernel(); - spin_lock(&driver_lock); - - list_for_each_entry(pos, &tpm_chip_list, list) { + rcu_read_lock(); + list_for_each_entry_rcu(pos, &tpm_chip_list, list) { if (pos->vendor.miscdev.minor == minor) { chip = pos; + get_device(chip->dev); break; } } + rcu_read_unlock(); - if (chip == NULL) { - rc = -ENODEV; - goto err_out; - } + if (!chip) + return -ENODEV; - if (chip->num_opens) { + if (test_and_set_bit(0, &chip->is_open)) { dev_dbg(chip->dev, "Another process owns this TPM\n"); - rc = -EBUSY; - goto err_out; + put_device(chip->dev); + return -EBUSY; } - chip->num_opens++; - get_device(chip->dev); - - spin_unlock(&driver_lock); - chip->data_buffer = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); if (chip->data_buffer == NULL) { - chip->num_opens--; + clear_bit(0, &chip->is_open); put_device(chip->dev); - unlock_kernel(); return -ENOMEM; } atomic_set(&chip->data_pending, 0); file->private_data = chip; - unlock_kernel(); return 0; - -err_out: - spin_unlock(&driver_lock); - unlock_kernel(); - return rc; } EXPORT_SYMBOL_GPL(tpm_open); +/* + * Called on file close + */ int tpm_release(struct inode *inode, struct file *file) { struct tpm_chip *chip = file->private_data; + del_singleshot_timer_sync(&chip->user_read_timer); flush_scheduled_work(); - spin_lock(&driver_lock); file->private_data = NULL; - del_singleshot_timer_sync(&chip->user_read_timer); atomic_set(&chip->data_pending, 0); - chip->num_opens--; - put_device(chip->dev); kfree(chip->data_buffer); - spin_unlock(&driver_lock); + clear_bit(0, &chip->is_open); + put_device(chip->dev); return 0; } EXPORT_SYMBOL_GPL(tpm_release); @@ -1093,13 +1084,11 @@ void tpm_remove_hardware(struct device *dev) } spin_lock(&driver_lock); - - list_del(&chip->list); - + list_del_rcu(&chip->list); spin_unlock(&driver_lock); + synchronize_rcu(); misc_deregister(&chip->vendor.miscdev); - sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); tpm_bios_log_teardown(chip->bios_dir); @@ -1144,25 +1133,33 @@ int tpm_pm_resume(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_pm_resume); +/* In case vendor provided release function, call it too.*/ + +void tpm_dev_vendor_release(struct tpm_chip *chip) +{ + if (chip->vendor.release) + chip->vendor.release(chip->dev); + + clear_bit(chip->dev_num, dev_mask); + kfree(chip->vendor.miscdev.name); +} +EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); + + /* * Once all references to platform device are down to 0, * release all allocated structures. - * In case vendor provided release function, - * call it too. */ static void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); - if (chip->vendor.release) - chip->vendor.release(dev); + tpm_dev_vendor_release(chip); chip->release(dev); - - clear_bit(chip->dev_num, dev_mask); - kfree(chip->vendor.miscdev.name); kfree(chip); } +EXPORT_SYMBOL_GPL(tpm_dev_release); /* * Called from tpm_<specific>.c probe function only for devices @@ -1171,8 +1168,8 @@ static void tpm_dev_release(struct device *dev) * upon errant exit from this function specific probe function should call * pci_disable_device */ -struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vendor_specific - *entry) +struct tpm_chip *tpm_register_hardware(struct device *dev, + const struct tpm_vendor_specific *entry) { #define DEVNAME_SIZE 7 @@ -1231,21 +1228,20 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, const struct tpm_vend return NULL; } - spin_lock(&driver_lock); - - list_add(&chip->list, &tpm_chip_list); - - spin_unlock(&driver_lock); - if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { - list_del(&chip->list); misc_deregister(&chip->vendor.miscdev); put_device(chip->dev); + return NULL; } chip->bios_dir = tpm_bios_log_setup(devname); + /* Make chip available */ + spin_lock(&driver_lock); + list_add_rcu(&chip->list, &tpm_chip_list); + spin_unlock(&driver_lock); + return chip; } EXPORT_SYMBOL_GPL(tpm_register_hardware); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index e885148b4cf..8e30df4a438 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -90,7 +90,7 @@ struct tpm_chip { struct device *dev; /* Device stuff */ int dev_num; /* /dev/tpm# */ - int num_opens; /* only one allowed */ + unsigned long is_open; /* only one allowed */ int time_expired; /* Data passed to and from the tpm via the read/write calls */ @@ -132,6 +132,7 @@ extern struct tpm_chip* tpm_register_hardware(struct device *, const struct tpm_vendor_specific *); extern int tpm_open(struct inode *, struct file *); extern int tpm_release(struct inode *, struct file *); +extern void tpm_dev_vendor_release(struct tpm_chip *); extern ssize_t tpm_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ed1879c0dd8..717af7ad1bd 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -630,12 +630,23 @@ static struct pnp_device_id tpm_pnp_tbl[] __devinitdata = { {"", 0} /* Terminator */ }; +static __devexit void tpm_tis_pnp_remove(struct pnp_dev *dev) +{ + struct tpm_chip *chip = pnp_get_drvdata(dev); + + tpm_dev_vendor_release(chip); + + kfree(chip); +} + + static struct pnp_driver tis_pnp_driver = { .name = "tpm_tis", .id_table = tpm_pnp_tbl, .probe = tpm_tis_pnp_init, .suspend = tpm_tis_pnp_suspend, .resume = tpm_tis_pnp_resume, + .remove = tpm_tis_pnp_remove, }; #define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 @@ -683,6 +694,7 @@ static void __exit cleanup_tis(void) spin_lock(&tis_lock); list_for_each_entry_safe(i, j, &tis_chips, list) { chip = to_tpm_chip(i); + tpm_remove_hardware(chip->dev); iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor. @@ -694,9 +706,9 @@ static void __exit cleanup_tis(void) free_irq(chip->vendor.irq, chip); iounmap(i->iobase); list_del(&i->list); - tpm_remove_hardware(chip->dev); } spin_unlock(&tis_lock); + if (force) { platform_device_unregister(pdev); driver_unregister(&tis_drv); diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c index 3582f43345a..5787249934c 100644 --- a/drivers/char/tty_audit.c +++ b/drivers/char/tty_audit.c @@ -93,7 +93,7 @@ static void tty_audit_buf_push(struct task_struct *tsk, uid_t loginuid, get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); audit_log_format(ab, " data="); - audit_log_n_untrustedstring(ab, buf->data, buf->valid); + audit_log_n_hex(ab, buf->data, buf->valid); audit_log_end(ab); } buf->valid = 0; diff --git a/drivers/char/tty_buffer.c b/drivers/char/tty_buffer.c new file mode 100644 index 00000000000..810ee25d66a --- /dev/null +++ b/drivers/char/tty_buffer.c @@ -0,0 +1,511 @@ +/* + * Tty buffer allocation management + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/module.h> + +/** + * tty_buffer_free_all - free buffers used by a tty + * @tty: tty to free from + * + * Remove all the buffers pending on a tty whether queued with data + * or in the free ring. Must be called when the tty is no longer in use + * + * Locking: none + */ + +void tty_buffer_free_all(struct tty_struct *tty) +{ + struct tty_buffer *thead; + while ((thead = tty->buf.head) != NULL) { + tty->buf.head = thead->next; + kfree(thead); + } + while ((thead = tty->buf.free) != NULL) { + tty->buf.free = thead->next; + kfree(thead); + } + tty->buf.tail = NULL; + tty->buf.memory_used = 0; +} + +/** + * tty_buffer_alloc - allocate a tty buffer + * @tty: tty device + * @size: desired size (characters) + * + * Allocate a new tty buffer to hold the desired number of characters. + * Return NULL if out of memory or the allocation would exceed the + * per device queue + * + * Locking: Caller must hold tty->buf.lock + */ + +static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) +{ + struct tty_buffer *p; + + if (tty->buf.memory_used + size > 65536) + return NULL; + p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); + if (p == NULL) + return NULL; + p->used = 0; + p->size = size; + p->next = NULL; + p->commit = 0; + p->read = 0; + p->char_buf_ptr = (char *)(p->data); + p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; + tty->buf.memory_used += size; + return p; +} + +/** + * tty_buffer_free - free a tty buffer + * @tty: tty owning the buffer + * @b: the buffer to free + * + * Free a tty buffer, or add it to the free list according to our + * internal strategy + * + * Locking: Caller must hold tty->buf.lock + */ + +static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) +{ + /* Dumb strategy for now - should keep some stats */ + tty->buf.memory_used -= b->size; + WARN_ON(tty->buf.memory_used < 0); + + if (b->size >= 512) + kfree(b); + else { + b->next = tty->buf.free; + tty->buf.free = b; + } +} + +/** + * __tty_buffer_flush - flush full tty buffers + * @tty: tty to flush + * + * flush all the buffers containing receive data. Caller must + * hold the buffer lock and must have ensured no parallel flush to + * ldisc is running. + * + * Locking: Caller must hold tty->buf.lock + */ + +static void __tty_buffer_flush(struct tty_struct *tty) +{ + struct tty_buffer *thead; + + while ((thead = tty->buf.head) != NULL) { + tty->buf.head = thead->next; + tty_buffer_free(tty, thead); + } + tty->buf.tail = NULL; +} + +/** + * tty_buffer_flush - flush full tty buffers + * @tty: tty to flush + * + * flush all the buffers containing receive data. If the buffer is + * being processed by flush_to_ldisc then we defer the processing + * to that function + * + * Locking: none + */ + +void tty_buffer_flush(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + + /* If the data is being pushed to the tty layer then we can't + process it here. Instead set a flag and the flush_to_ldisc + path will process the flush request before it exits */ + if (test_bit(TTY_FLUSHING, &tty->flags)) { + set_bit(TTY_FLUSHPENDING, &tty->flags); + spin_unlock_irqrestore(&tty->buf.lock, flags); + wait_event(tty->read_wait, + test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); + return; + } else + __tty_buffer_flush(tty); + spin_unlock_irqrestore(&tty->buf.lock, flags); +} + +/** + * tty_buffer_find - find a free tty buffer + * @tty: tty owning the buffer + * @size: characters wanted + * + * Locate an existing suitable tty buffer or if we are lacking one then + * allocate a new one. We round our buffers off in 256 character chunks + * to get better allocation behaviour. + * + * Locking: Caller must hold tty->buf.lock + */ + +static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) +{ + struct tty_buffer **tbh = &tty->buf.free; + while ((*tbh) != NULL) { + struct tty_buffer *t = *tbh; + if (t->size >= size) { + *tbh = t->next; + t->next = NULL; + t->used = 0; + t->commit = 0; + t->read = 0; + tty->buf.memory_used += t->size; + return t; + } + tbh = &((*tbh)->next); + } + /* Round the buffer size out */ + size = (size + 0xFF) & ~0xFF; + return tty_buffer_alloc(tty, size); + /* Should possibly check if this fails for the largest buffer we + have queued and recycle that ? */ +} + +/** + * tty_buffer_request_room - grow tty buffer if needed + * @tty: tty structure + * @size: size desired + * + * Make at least size bytes of linear space available for the tty + * buffer. If we fail return the size we managed to find. + * + * Locking: Takes tty->buf.lock + */ +int tty_buffer_request_room(struct tty_struct *tty, size_t size) +{ + struct tty_buffer *b, *n; + int left; + unsigned long flags; + + spin_lock_irqsave(&tty->buf.lock, flags); + + /* OPTIMISATION: We could keep a per tty "zero" sized buffer to + remove this conditional if its worth it. This would be invisible + to the callers */ + if ((b = tty->buf.tail) != NULL) + left = b->size - b->used; + else + left = 0; + + if (left < size) { + /* This is the slow path - looking for new buffers to use */ + if ((n = tty_buffer_find(tty, size)) != NULL) { + if (b != NULL) { + b->next = n; + b->commit = b->used; + } else + tty->buf.head = n; + tty->buf.tail = n; + } else + size = left; + } + + spin_unlock_irqrestore(&tty->buf.lock, flags); + return size; +} +EXPORT_SYMBOL_GPL(tty_buffer_request_room); + +/** + * tty_insert_flip_string - Add characters to the tty buffer + * @tty: tty structure + * @chars: characters + * @size: size + * + * Queue a series of bytes to the tty buffering. All the characters + * passed are marked as without error. Returns the number added. + * + * Locking: Called functions may take tty->buf.lock + */ + +int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, + size_t size) +{ + int copied = 0; + do { + int space = tty_buffer_request_room(tty, size - copied); + struct tty_buffer *tb = tty->buf.tail; + /* If there is no space then tb may be NULL */ + if (unlikely(space == 0)) + break; + memcpy(tb->char_buf_ptr + tb->used, chars, space); + memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + tb->used += space; + copied += space; + chars += space; + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); + return copied; +} +EXPORT_SYMBOL(tty_insert_flip_string); + +/** + * tty_insert_flip_string_flags - Add characters to the tty buffer + * @tty: tty structure + * @chars: characters + * @flags: flag bytes + * @size: size + * + * Queue a series of bytes to the tty buffering. For each character + * the flags array indicates the status of the character. Returns the + * number added. + * + * Locking: Called functions may take tty->buf.lock + */ + +int tty_insert_flip_string_flags(struct tty_struct *tty, + const unsigned char *chars, const char *flags, size_t size) +{ + int copied = 0; + do { + int space = tty_buffer_request_room(tty, size - copied); + struct tty_buffer *tb = tty->buf.tail; + /* If there is no space then tb may be NULL */ + if (unlikely(space == 0)) + break; + memcpy(tb->char_buf_ptr + tb->used, chars, space); + memcpy(tb->flag_buf_ptr + tb->used, flags, space); + tb->used += space; + copied += space; + chars += space; + flags += space; + /* There is a small chance that we need to split the data over + several buffers. If this is the case we must loop */ + } while (unlikely(size > copied)); + return copied; +} +EXPORT_SYMBOL(tty_insert_flip_string_flags); + +/** + * tty_schedule_flip - push characters to ldisc + * @tty: tty to push from + * + * Takes any pending buffers and transfers their ownership to the + * ldisc side of the queue. It then schedules those characters for + * processing by the line discipline. + * + * Locking: Takes tty->buf.lock + */ + +void tty_schedule_flip(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) + tty->buf.tail->commit = tty->buf.tail->used; + spin_unlock_irqrestore(&tty->buf.lock, flags); + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_schedule_flip); + +/** + * tty_prepare_flip_string - make room for characters + * @tty: tty + * @chars: return pointer for character write area + * @size: desired size + * + * Prepare a block of space in the buffer for data. Returns the length + * available and buffer pointer to the space which is now allocated and + * accounted for as ready for normal characters. This is used for drivers + * that need their own block copy routines into the buffer. There is no + * guarantee the buffer is a DMA target! + * + * Locking: May call functions taking tty->buf.lock + */ + +int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, + size_t size) +{ + int space = tty_buffer_request_room(tty, size); + if (likely(space)) { + struct tty_buffer *tb = tty->buf.tail; + *chars = tb->char_buf_ptr + tb->used; + memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); + tb->used += space; + } + return space; +} +EXPORT_SYMBOL_GPL(tty_prepare_flip_string); + +/** + * tty_prepare_flip_string_flags - make room for characters + * @tty: tty + * @chars: return pointer for character write area + * @flags: return pointer for status flag write area + * @size: desired size + * + * Prepare a block of space in the buffer for data. Returns the length + * available and buffer pointer to the space which is now allocated and + * accounted for as ready for characters. This is used for drivers + * that need their own block copy routines into the buffer. There is no + * guarantee the buffer is a DMA target! + * + * Locking: May call functions taking tty->buf.lock + */ + +int tty_prepare_flip_string_flags(struct tty_struct *tty, + unsigned char **chars, char **flags, size_t size) +{ + int space = tty_buffer_request_room(tty, size); + if (likely(space)) { + struct tty_buffer *tb = tty->buf.tail; + *chars = tb->char_buf_ptr + tb->used; + *flags = tb->flag_buf_ptr + tb->used; + tb->used += space; + } + return space; +} +EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); + + + +/** + * flush_to_ldisc + * @work: tty structure passed from work queue. + * + * This routine is called out of the software interrupt to flush data + * from the buffer chain to the line discipline. + * + * Locking: holds tty->buf.lock to guard buffer list. Drops the lock + * while invoking the line discipline receive_buf method. The + * receive_buf method is single threaded for each tty instance. + */ + +static void flush_to_ldisc(struct work_struct *work) +{ + struct tty_struct *tty = + container_of(work, struct tty_struct, buf.work.work); + unsigned long flags; + struct tty_ldisc *disc; + struct tty_buffer *tbuf, *head; + char *char_buf; + unsigned char *flag_buf; + + disc = tty_ldisc_ref(tty); + if (disc == NULL) /* !TTY_LDISC */ + return; + + spin_lock_irqsave(&tty->buf.lock, flags); + /* So we know a flush is running */ + set_bit(TTY_FLUSHING, &tty->flags); + head = tty->buf.head; + if (head != NULL) { + tty->buf.head = NULL; + for (;;) { + int count = head->commit - head->read; + if (!count) { + if (head->next == NULL) + break; + tbuf = head; + head = head->next; + tty_buffer_free(tty, tbuf); + continue; + } + /* Ldisc or user is trying to flush the buffers + we are feeding to the ldisc, stop feeding the + line discipline as we want to empty the queue */ + if (test_bit(TTY_FLUSHPENDING, &tty->flags)) + break; + if (!tty->receive_room) { + schedule_delayed_work(&tty->buf.work, 1); + break; + } + if (count > tty->receive_room) + count = tty->receive_room; + char_buf = head->char_buf_ptr + head->read; + flag_buf = head->flag_buf_ptr + head->read; + head->read += count; + spin_unlock_irqrestore(&tty->buf.lock, flags); + disc->ops->receive_buf(tty, char_buf, + flag_buf, count); + spin_lock_irqsave(&tty->buf.lock, flags); + } + /* Restore the queue head */ + tty->buf.head = head; + } + /* We may have a deferred request to flush the input buffer, + if so pull the chain under the lock and empty the queue */ + if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { + __tty_buffer_flush(tty); + clear_bit(TTY_FLUSHPENDING, &tty->flags); + wake_up(&tty->read_wait); + } + clear_bit(TTY_FLUSHING, &tty->flags); + spin_unlock_irqrestore(&tty->buf.lock, flags); + + tty_ldisc_deref(disc); +} + +/** + * tty_flip_buffer_push - terminal + * @tty: tty to push + * + * Queue a push of the terminal flip buffers to the line discipline. This + * function must not be called from IRQ context if tty->low_latency is set. + * + * In the event of the queue being busy for flipping the work will be + * held off and retried later. + * + * Locking: tty buffer lock. Driver locks in low latency mode. + */ + +void tty_flip_buffer_push(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) + tty->buf.tail->commit = tty->buf.tail->used; + spin_unlock_irqrestore(&tty->buf.lock, flags); + + if (tty->low_latency) + flush_to_ldisc(&tty->buf.work.work); + else + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_flip_buffer_push); + +/** + * tty_buffer_init - prepare a tty buffer structure + * @tty: tty to initialise + * + * Set up the initial state of the buffer management for a tty device. + * Must be called before the other tty buffer functions are used. + * + * Locking: none + */ + +void tty_buffer_init(struct tty_struct *tty) +{ + spin_lock_init(&tty->buf.lock); + tty->buf.head = NULL; + tty->buf.tail = NULL; + tty->buf.free = NULL; + tty->buf.memory_used = 0; + INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); +} + diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index e4dce870954..7053d633369 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -49,7 +49,7 @@ * implement CONFIG_VT and generalize console device interface. * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 * - * Rewrote init_dev and release_dev to eliminate races. + * Rewrote tty_init_dev and tty_release_dev to eliminate races. * -- Bill Hawes <whawes@star.net>, June 97 * * Added devfs support. @@ -136,13 +136,6 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */ DEFINE_MUTEX(tty_mutex); EXPORT_SYMBOL(tty_mutex); -#ifdef CONFIG_UNIX98_PTYS -extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */ -static int ptmx_open(struct inode *, struct file *); -#endif - -static void initialize_tty_struct(struct tty_struct *tty); - static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *); static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *); ssize_t redirected_tty_write(struct file *, const char __user *, @@ -171,13 +164,11 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty); * Locking: none */ -static struct tty_struct *alloc_tty_struct(void) +struct tty_struct *alloc_tty_struct(void) { return kzalloc(sizeof(struct tty_struct), GFP_KERNEL); } -static void tty_buffer_free_all(struct tty_struct *); - /** * free_tty_struct - free a disused tty * @tty: tty struct to free @@ -187,7 +178,7 @@ static void tty_buffer_free_all(struct tty_struct *); * Locking: none. Must be called after tty is definitely unused */ -static inline void free_tty_struct(struct tty_struct *tty) +void free_tty_struct(struct tty_struct *tty) { kfree(tty->write_buf); tty_buffer_free_all(tty); @@ -263,398 +254,6 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) return 0; } -/* - * Tty buffer allocation management - */ - -/** - * tty_buffer_free_all - free buffers used by a tty - * @tty: tty to free from - * - * Remove all the buffers pending on a tty whether queued with data - * or in the free ring. Must be called when the tty is no longer in use - * - * Locking: none - */ - -static void tty_buffer_free_all(struct tty_struct *tty) -{ - struct tty_buffer *thead; - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - kfree(thead); - } - while ((thead = tty->buf.free) != NULL) { - tty->buf.free = thead->next; - kfree(thead); - } - tty->buf.tail = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_init - prepare a tty buffer structure - * @tty: tty to initialise - * - * Set up the initial state of the buffer management for a tty device. - * Must be called before the other tty buffer functions are used. - * - * Locking: none - */ - -static void tty_buffer_init(struct tty_struct *tty) -{ - spin_lock_init(&tty->buf.lock); - tty->buf.head = NULL; - tty->buf.tail = NULL; - tty->buf.free = NULL; - tty->buf.memory_used = 0; -} - -/** - * tty_buffer_alloc - allocate a tty buffer - * @tty: tty device - * @size: desired size (characters) - * - * Allocate a new tty buffer to hold the desired number of characters. - * Return NULL if out of memory or the allocation would exceed the - * per device queue - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *p; - - if (tty->buf.memory_used + size > 65536) - return NULL; - p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); - if (p == NULL) - return NULL; - p->used = 0; - p->size = size; - p->next = NULL; - p->commit = 0; - p->read = 0; - p->char_buf_ptr = (char *)(p->data); - p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; - tty->buf.memory_used += size; - return p; -} - -/** - * tty_buffer_free - free a tty buffer - * @tty: tty owning the buffer - * @b: the buffer to free - * - * Free a tty buffer, or add it to the free list according to our - * internal strategy - * - * Locking: Caller must hold tty->buf.lock - */ - -static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) -{ - /* Dumb strategy for now - should keep some stats */ - tty->buf.memory_used -= b->size; - WARN_ON(tty->buf.memory_used < 0); - - if (b->size >= 512) - kfree(b); - else { - b->next = tty->buf.free; - tty->buf.free = b; - } -} - -/** - * __tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. Caller must - * hold the buffer lock and must have ensured no parallel flush to - * ldisc is running. - * - * Locking: Caller must hold tty->buf.lock - */ - -static void __tty_buffer_flush(struct tty_struct *tty) -{ - struct tty_buffer *thead; - - while ((thead = tty->buf.head) != NULL) { - tty->buf.head = thead->next; - tty_buffer_free(tty, thead); - } - tty->buf.tail = NULL; -} - -/** - * tty_buffer_flush - flush full tty buffers - * @tty: tty to flush - * - * flush all the buffers containing receive data. If the buffer is - * being processed by flush_to_ldisc then we defer the processing - * to that function - * - * Locking: none - */ - -static void tty_buffer_flush(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - - /* If the data is being pushed to the tty layer then we can't - process it here. Instead set a flag and the flush_to_ldisc - path will process the flush request before it exits */ - if (test_bit(TTY_FLUSHING, &tty->flags)) { - set_bit(TTY_FLUSHPENDING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); - wait_event(tty->read_wait, - test_bit(TTY_FLUSHPENDING, &tty->flags) == 0); - return; - } else - __tty_buffer_flush(tty); - spin_unlock_irqrestore(&tty->buf.lock, flags); -} - -/** - * tty_buffer_find - find a free tty buffer - * @tty: tty owning the buffer - * @size: characters wanted - * - * Locate an existing suitable tty buffer or if we are lacking one then - * allocate a new one. We round our buffers off in 256 character chunks - * to get better allocation behaviour. - * - * Locking: Caller must hold tty->buf.lock - */ - -static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) -{ - struct tty_buffer **tbh = &tty->buf.free; - while ((*tbh) != NULL) { - struct tty_buffer *t = *tbh; - if (t->size >= size) { - *tbh = t->next; - t->next = NULL; - t->used = 0; - t->commit = 0; - t->read = 0; - tty->buf.memory_used += t->size; - return t; - } - tbh = &((*tbh)->next); - } - /* Round the buffer size out */ - size = (size + 0xFF) & ~0xFF; - return tty_buffer_alloc(tty, size); - /* Should possibly check if this fails for the largest buffer we - have queued and recycle that ? */ -} - -/** - * tty_buffer_request_room - grow tty buffer if needed - * @tty: tty structure - * @size: size desired - * - * Make at least size bytes of linear space available for the tty - * buffer. If we fail return the size we managed to find. - * - * Locking: Takes tty->buf.lock - */ -int tty_buffer_request_room(struct tty_struct *tty, size_t size) -{ - struct tty_buffer *b, *n; - int left; - unsigned long flags; - - spin_lock_irqsave(&tty->buf.lock, flags); - - /* OPTIMISATION: We could keep a per tty "zero" sized buffer to - remove this conditional if its worth it. This would be invisible - to the callers */ - if ((b = tty->buf.tail) != NULL) - left = b->size - b->used; - else - left = 0; - - if (left < size) { - /* This is the slow path - looking for new buffers to use */ - if ((n = tty_buffer_find(tty, size)) != NULL) { - if (b != NULL) { - b->next = n; - b->commit = b->used; - } else - tty->buf.head = n; - tty->buf.tail = n; - } else - size = left; - } - - spin_unlock_irqrestore(&tty->buf.lock, flags); - return size; -} -EXPORT_SYMBOL_GPL(tty_buffer_request_room); - -/** - * tty_insert_flip_string - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @size: size - * - * Queue a series of bytes to the tty buffering. All the characters - * passed are marked as without error. Returns the number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, - size_t size) -{ - int copied = 0; - do { - int space = tty_buffer_request_room(tty, size - copied); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); - tb->used += space; - copied += space; - chars += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string); - -/** - * tty_insert_flip_string_flags - Add characters to the tty buffer - * @tty: tty structure - * @chars: characters - * @flags: flag bytes - * @size: size - * - * Queue a series of bytes to the tty buffering. For each character - * the flags array indicates the status of the character. Returns the - * number added. - * - * Locking: Called functions may take tty->buf.lock - */ - -int tty_insert_flip_string_flags(struct tty_struct *tty, - const unsigned char *chars, const char *flags, size_t size) -{ - int copied = 0; - do { - int space = tty_buffer_request_room(tty, size - copied); - struct tty_buffer *tb = tty->buf.tail; - /* If there is no space then tb may be NULL */ - if (unlikely(space == 0)) - break; - memcpy(tb->char_buf_ptr + tb->used, chars, space); - memcpy(tb->flag_buf_ptr + tb->used, flags, space); - tb->used += space; - copied += space; - chars += space; - flags += space; - /* There is a small chance that we need to split the data over - several buffers. If this is the case we must loop */ - } while (unlikely(size > copied)); - return copied; -} -EXPORT_SYMBOL(tty_insert_flip_string_flags); - -/** - * tty_schedule_flip - push characters to ldisc - * @tty: tty to push from - * - * Takes any pending buffers and transfers their ownership to the - * ldisc side of the queue. It then schedules those characters for - * processing by the line discipline. - * - * Locking: Takes tty->buf.lock - */ - -void tty_schedule_flip(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - schedule_delayed_work(&tty->buf.work, 1); -} -EXPORT_SYMBOL(tty_schedule_flip); - -/** - * tty_prepare_flip_string - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for normal characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking tty->buf.lock - */ - -int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, - size_t size) -{ - int space = tty_buffer_request_room(tty, size); - if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); - tb->used += space; - } - return space; -} - -EXPORT_SYMBOL_GPL(tty_prepare_flip_string); - -/** - * tty_prepare_flip_string_flags - make room for characters - * @tty: tty - * @chars: return pointer for character write area - * @flags: return pointer for status flag write area - * @size: desired size - * - * Prepare a block of space in the buffer for data. Returns the length - * available and buffer pointer to the space which is now allocated and - * accounted for as ready for characters. This is used for drivers - * that need their own block copy routines into the buffer. There is no - * guarantee the buffer is a DMA target! - * - * Locking: May call functions taking tty->buf.lock - */ - -int tty_prepare_flip_string_flags(struct tty_struct *tty, - unsigned char **chars, char **flags, size_t size) -{ - int space = tty_buffer_request_room(tty, size); - if (likely(space)) { - struct tty_buffer *tb = tty->buf.tail; - *chars = tb->char_buf_ptr + tb->used; - *flags = tb->flag_buf_ptr + tb->used; - tb->used += space; - } - return space; -} - -EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); - - - /** * get_tty_driver - find device of a tty * @dev_t: device identifier @@ -675,7 +274,7 @@ static struct tty_driver *get_tty_driver(dev_t device, int *index) if (device < base || device >= base + p->num) continue; *index = device - base; - return p; + return tty_driver_kref_get(p); } return NULL; } @@ -719,7 +318,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line) if (tty_line >= 0 && tty_line <= p->num && p->ops && p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) { - res = p; + res = tty_driver_kref_get(p); *line = tty_line; break; } @@ -819,20 +418,6 @@ static const struct file_operations tty_fops = { .fasync = tty_fasync, }; -#ifdef CONFIG_UNIX98_PTYS -static const struct file_operations ptmx_fops = { - .llseek = no_llseek, - .read = tty_read, - .write = tty_write, - .poll = tty_poll, - .unlocked_ioctl = tty_ioctl, - .compat_ioctl = tty_compat_ioctl, - .open = ptmx_open, - .release = tty_release, - .fasync = tty_fasync, -}; -#endif - static const struct file_operations console_fops = { .llseek = no_llseek, .read = tty_read, @@ -953,6 +538,7 @@ static void do_tty_hangup(struct work_struct *work) struct tty_ldisc *ld; int closecount = 0, n; unsigned long flags; + int refs = 0; if (!tty) return; @@ -1019,8 +605,12 @@ static void do_tty_hangup(struct work_struct *work) if (tty->session) { do_each_pid_task(tty->session, PIDTYPE_SID, p) { spin_lock_irq(&p->sighand->siglock); - if (p->signal->tty == tty) + if (p->signal->tty == tty) { p->signal->tty = NULL; + /* We defer the dereferences outside fo + the tasklist lock */ + refs++; + } if (!p->signal->leader) { spin_unlock_irq(&p->sighand->siglock); continue; @@ -1046,6 +636,10 @@ static void do_tty_hangup(struct work_struct *work) tty->ctrl_status = 0; spin_unlock_irqrestore(&tty->ctrl_lock, flags); + /* Account for the p->signal references we killed */ + while (refs--) + tty_kref_put(tty); + /* * If one of the devices matches a console pointer, we * cannot just call hangup() because that will cause @@ -1115,6 +709,23 @@ void tty_vhangup(struct tty_struct *tty) EXPORT_SYMBOL(tty_vhangup); /** + * tty_vhangup_self - process vhangup for own ctty + * + * Perform a vhangup on the current controlling tty + */ + +void tty_vhangup_self(void) +{ + struct tty_struct *tty; + + tty = get_current_tty(); + if (tty) { + tty_vhangup(tty); + tty_kref_put(tty); + } +} + +/** * tty_hung_up_p - was tty hung up * @filp: file pointer of tty * @@ -1167,16 +778,14 @@ void disassociate_ctty(int on_exit) struct pid *tty_pgrp = NULL; - mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { tty_pgrp = get_pid(tty->pgrp); lock_kernel(); - mutex_unlock(&tty_mutex); - /* XXX: here we race, there is nothing protecting tty */ if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); unlock_kernel(); + tty_kref_put(tty); } else if (on_exit) { struct pid *old_pgrp; spin_lock_irq(¤t->sighand->siglock); @@ -1188,7 +797,6 @@ void disassociate_ctty(int on_exit) kill_pgrp(old_pgrp, SIGCONT, on_exit); put_pid(old_pgrp); } - mutex_unlock(&tty_mutex); return; } if (tty_pgrp) { @@ -1203,8 +811,6 @@ void disassociate_ctty(int on_exit) current->signal->tty_old_pgrp = NULL; spin_unlock_irq(¤t->sighand->siglock); - mutex_lock(&tty_mutex); - /* It is possible that do_tty_hangup has free'd this tty */ tty = get_current_tty(); if (tty) { unsigned long flags; @@ -1214,13 +820,13 @@ void disassociate_ctty(int on_exit) tty->session = NULL; tty->pgrp = NULL; spin_unlock_irqrestore(&tty->ctrl_lock, flags); + tty_kref_put(tty); } else { #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "error attempted to write to tty [0x%p]" " = NULL", tty); #endif } - mutex_unlock(&tty_mutex); /* Now clear signal->tty under the lock */ read_lock(&tasklist_lock); @@ -1420,19 +1026,19 @@ static inline ssize_t do_tty_write( /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ if (tty->write_cnt < chunk) { - unsigned char *buf; + unsigned char *buf_chunk; if (chunk < 1024) chunk = 1024; - buf = kmalloc(chunk, GFP_KERNEL); - if (!buf) { + buf_chunk = kmalloc(chunk, GFP_KERNEL); + if (!buf_chunk) { ret = -ENOMEM; goto out; } kfree(tty->write_buf); tty->write_cnt = chunk; - tty->write_buf = buf; + tty->write_buf = buf_chunk; } /* Do the write .. */ @@ -1466,6 +1072,31 @@ out: return ret; } +/** + * tty_write_message - write a message to a certain tty, not just the console. + * @tty: the destination tty_struct + * @msg: the message to write + * + * This is used for messages that need to be redirected to a specific tty. + * We don't put it into the syslog queue right now maybe in the future if + * really needed. + * + * We must still hold the BKL and test the CLOSING flag for the moment. + */ + +void tty_write_message(struct tty_struct *tty, char *msg) +{ + lock_kernel(); + if (tty) { + mutex_lock(&tty->atomic_write_lock); + if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) + tty->ops->write(tty, msg, strlen(msg)); + tty_write_unlock(tty); + } + unlock_kernel(); + return; +} + /** * tty_write - write method for tty device file @@ -1533,42 +1164,6 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf, return tty_write(file, buf, count, ppos); } -void tty_port_init(struct tty_port *port) -{ - memset(port, 0, sizeof(*port)); - init_waitqueue_head(&port->open_wait); - init_waitqueue_head(&port->close_wait); - mutex_init(&port->mutex); - port->close_delay = (50 * HZ) / 100; - port->closing_wait = (3000 * HZ) / 100; -} -EXPORT_SYMBOL(tty_port_init); - -int tty_port_alloc_xmit_buf(struct tty_port *port) -{ - /* We may sleep in get_zeroed_page() */ - mutex_lock(&port->mutex); - if (port->xmit_buf == NULL) - port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); - mutex_unlock(&port->mutex); - if (port->xmit_buf == NULL) - return -ENOMEM; - return 0; -} -EXPORT_SYMBOL(tty_port_alloc_xmit_buf); - -void tty_port_free_xmit_buf(struct tty_port *port) -{ - mutex_lock(&port->mutex); - if (port->xmit_buf != NULL) { - free_page((unsigned long)port->xmit_buf); - port->xmit_buf = NULL; - } - mutex_unlock(&port->mutex); -} -EXPORT_SYMBOL(tty_port_free_xmit_buf); - - static char ptychar[] = "pqrstuvwxyzabcde"; /** @@ -1592,7 +1187,7 @@ static void pty_line_name(struct tty_driver *driver, int index, char *p) } /** - * pty_line_name - generate name for a tty + * tty_line_name - generate name for a tty * @driver: the tty driver in use * @index: the minor number * @p: output buffer of at least 7 bytes @@ -1608,10 +1203,148 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) } /** - * init_dev - initialise a tty device + * tty_driver_lookup_tty() - find an existing tty, if any + * @driver: the driver for the tty + * @idx: the minor number + * + * Return the tty, if found or ERR_PTR() otherwise. + * + * Locking: tty_mutex must be held. If tty is found, the mutex must + * be held until the 'fast-open' is also done. Will change once we + * have refcounting in the driver and per driver locking + */ +struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, + struct inode *inode, int idx) +{ + struct tty_struct *tty; + + if (driver->ops->lookup) + return driver->ops->lookup(driver, inode, idx); + + tty = driver->ttys[idx]; + return tty; +} + +/** + * tty_init_termios - helper for termios setup + * @tty: the tty to set up + * + * Initialise the termios structures for this tty. Thus runs under + * the tty_mutex currently so we can be relaxed about ordering. + */ + +int tty_init_termios(struct tty_struct *tty) +{ + struct ktermios *tp; + int idx = tty->index; + + tp = tty->driver->termios[idx]; + if (tp == NULL) { + tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL); + if (tp == NULL) + return -ENOMEM; + memcpy(tp, &tty->driver->init_termios, + sizeof(struct ktermios)); + tty->driver->termios[idx] = tp; + } + tty->termios = tp; + tty->termios_locked = tp + 1; + + /* Compatibility until drivers always set this */ + tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); + tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); + return 0; +} + +/** + * tty_driver_install_tty() - install a tty entry in the driver + * @driver: the driver for the tty + * @tty: the tty + * + * Install a tty object into the driver tables. The tty->index field + * will be set by the time this is called. This method is responsible + * for ensuring any need additional structures are allocated and + * configured. + * + * Locking: tty_mutex for now + */ +static int tty_driver_install_tty(struct tty_driver *driver, + struct tty_struct *tty) +{ + int idx = tty->index; + + if (driver->ops->install) + return driver->ops->install(driver, tty); + + if (tty_init_termios(tty) == 0) { + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; + } + return -ENOMEM; +} + +/** + * tty_driver_remove_tty() - remove a tty from the driver tables + * @driver: the driver for the tty + * @idx: the minor number + * + * Remvoe a tty object from the driver tables. The tty->index field + * will be set by the time this is called. + * + * Locking: tty_mutex for now + */ +static void tty_driver_remove_tty(struct tty_driver *driver, + struct tty_struct *tty) +{ + if (driver->ops->remove) + driver->ops->remove(driver, tty); + else + driver->ttys[tty->index] = NULL; +} + +/* + * tty_reopen() - fast re-open of an open tty + * @tty - the tty to open + * + * Return 0 on success, -errno on error. + * + * Locking: tty_mutex must be held from the time the tty was found + * till this open completes. + */ +static int tty_reopen(struct tty_struct *tty) +{ + struct tty_driver *driver = tty->driver; + + if (test_bit(TTY_CLOSING, &tty->flags)) + return -EIO; + + if (driver->type == TTY_DRIVER_TYPE_PTY && + driver->subtype == PTY_TYPE_MASTER) { + /* + * special case for PTY masters: only one open permitted, + * and the slave side open count is incremented as well. + */ + if (tty->count) + return -EIO; + + tty->link->count++; + } + tty->count++; + tty->driver = driver; /* N.B. why do this every time?? */ + + WARN_ON(!test_bit(TTY_LDISC, &tty->flags)); + + return 0; +} + +/** + * tty_init_dev - initialise a tty device * @driver: tty driver we are opening a device on * @idx: device index - * @tty: returned tty structure + * @ret_tty: returned tty structure + * @first_ok: ok to open a new device (used by ptmx) * * Prepare a tty device. This may not be a "new" clean device but * could also be an active device. The pty drivers require special @@ -1631,37 +1364,16 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p) * relaxed for the (most common) case of reopening a tty. */ -static int init_dev(struct tty_driver *driver, int idx, - struct tty_struct **ret_tty) +struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok) { - struct tty_struct *tty, *o_tty; - struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc; - struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; - int retval = 0; + struct tty_struct *tty; + int retval; - /* check whether we're reopening an existing tty */ - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - tty = devpts_get_tty(idx); - /* - * If we don't have a tty here on a slave open, it's because - * the master already started the close process and there's - * no relation between devpts file and tty anymore. - */ - if (!tty && driver->subtype == PTY_TYPE_SLAVE) { - retval = -EIO; - goto end_init; - } - /* - * It's safe from now on because init_dev() is called with - * tty_mutex held and release_dev() won't change tty->count - * or tty->flags without having to grab tty_mutex - */ - if (tty && driver->subtype == PTY_TYPE_MASTER) - tty = tty->link; - } else { - tty = driver->ttys[idx]; - } - if (tty) goto fast_track; + /* Check if pty master is being opened multiple times */ + if (driver->subtype == PTY_TYPE_MASTER && + (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) + return ERR_PTR(-EIO); /* * First time open is complex, especially for PTY devices. @@ -1671,189 +1383,69 @@ static int init_dev(struct tty_driver *driver, int idx, * and locked termios may be retained.) */ - if (!try_module_get(driver->owner)) { - retval = -ENODEV; - goto end_init; - } - - o_tty = NULL; - tp = o_tp = NULL; - ltp = o_ltp = NULL; + if (!try_module_get(driver->owner)) + return ERR_PTR(-ENODEV); tty = alloc_tty_struct(); if (!tty) goto fail_no_mem; - initialize_tty_struct(tty); - tty->driver = driver; - tty->ops = driver->ops; - tty->index = idx; - tty_line_name(driver, idx, tty->name); - - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - tp_loc = &tty->termios; - ltp_loc = &tty->termios_locked; - } else { - tp_loc = &driver->termios[idx]; - ltp_loc = &driver->termios_locked[idx]; - } - - if (!*tp_loc) { - tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!tp) - goto free_mem_out; - *tp = driver->init_termios; - } - - if (!*ltp_loc) { - ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!ltp) - goto free_mem_out; - } - - if (driver->type == TTY_DRIVER_TYPE_PTY) { - o_tty = alloc_tty_struct(); - if (!o_tty) - goto free_mem_out; - initialize_tty_struct(o_tty); - o_tty->driver = driver->other; - o_tty->ops = driver->ops; - o_tty->index = idx; - tty_line_name(driver->other, idx, o_tty->name); + initialize_tty_struct(tty, driver, idx); - if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { - o_tp_loc = &o_tty->termios; - o_ltp_loc = &o_tty->termios_locked; - } else { - o_tp_loc = &driver->other->termios[idx]; - o_ltp_loc = &driver->other->termios_locked[idx]; - } - - if (!*o_tp_loc) { - o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!o_tp) - goto free_mem_out; - *o_tp = driver->other->init_termios; - } - - if (!*o_ltp_loc) { - o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL); - if (!o_ltp) - goto free_mem_out; - } - - /* - * Everything allocated ... set up the o_tty structure. - */ - if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) - driver->other->ttys[idx] = o_tty; - if (!*o_tp_loc) - *o_tp_loc = o_tp; - if (!*o_ltp_loc) - *o_ltp_loc = o_ltp; - o_tty->termios = *o_tp_loc; - o_tty->termios_locked = *o_ltp_loc; - driver->other->refcount++; - if (driver->subtype == PTY_TYPE_MASTER) - o_tty->count++; - - /* Establish the links in both directions */ - tty->link = o_tty; - o_tty->link = tty; + retval = tty_driver_install_tty(driver, tty); + if (retval < 0) { + free_tty_struct(tty); + module_put(driver->owner); + return ERR_PTR(retval); } /* - * All structures have been allocated, so now we install them. - * Failures after this point use release_tty to clean up, so - * there's no need to null out the local pointers. - */ - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) - driver->ttys[idx] = tty; - - if (!*tp_loc) - *tp_loc = tp; - if (!*ltp_loc) - *ltp_loc = ltp; - tty->termios = *tp_loc; - tty->termios_locked = *ltp_loc; - /* Compatibility until drivers always set this */ - tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios); - tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); - driver->refcount++; - tty->count++; - - /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ - retval = tty_ldisc_setup(tty, o_tty); - + retval = tty_ldisc_setup(tty, tty->link); if (retval) goto release_mem_out; - goto success; - - /* - * This fast open can be used if the tty is already open. - * No memory is allocated, and the only failures are from - * attempting to open a closing tty or attempting multiple - * opens on a pty master. - */ -fast_track: - if (test_bit(TTY_CLOSING, &tty->flags)) { - retval = -EIO; - goto end_init; - } - if (driver->type == TTY_DRIVER_TYPE_PTY && - driver->subtype == PTY_TYPE_MASTER) { - /* - * special case for PTY masters: only one open permitted, - * and the slave side open count is incremented as well. - */ - if (tty->count) { - retval = -EIO; - goto end_init; - } - tty->link->count++; - } - tty->count++; - tty->driver = driver; /* N.B. why do this every time?? */ - - /* FIXME */ - if (!test_bit(TTY_LDISC, &tty->flags)) - printk(KERN_ERR "init_dev but no ldisc\n"); -success: - *ret_tty = tty; - - /* All paths come through here to release the mutex */ -end_init: - return retval; - - /* Release locally allocated memory ... nothing placed in slots */ -free_mem_out: - kfree(o_tp); - if (o_tty) - free_tty_struct(o_tty); - kfree(ltp); - kfree(tp); - free_tty_struct(tty); + return tty; fail_no_mem: module_put(driver->owner); - retval = -ENOMEM; - goto end_init; + return ERR_PTR(-ENOMEM); /* call the tty release_tty routine to clean out this slot */ release_mem_out: if (printk_ratelimit()) - printk(KERN_INFO "init_dev: ldisc open failed, " + printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_tty(tty, idx); - goto end_init; + return ERR_PTR(retval); } +void tty_free_termios(struct tty_struct *tty) +{ + struct ktermios *tp; + int idx = tty->index; + /* Kill this flag and push into drivers for locking etc */ + if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { + /* FIXME: Locking on ->termios array */ + tp = tty->termios; + tty->driver->termios[idx] = NULL; + kfree(tp); + } +} +EXPORT_SYMBOL(tty_free_termios); + +void tty_shutdown(struct tty_struct *tty) +{ + tty_driver_remove_tty(tty->driver, tty); + tty_free_termios(tty); +} +EXPORT_SYMBOL(tty_shutdown); + /** * release_one_tty - release tty structure memory + * @kref: kref of tty we are obliterating * * Releases memory associated with a tty structure, and clears out the * driver table slots. This function is called when a device is no longer @@ -1863,31 +1455,19 @@ release_mem_out: * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. - * FIXME: should we require tty_mutex is held here ?? */ -static void release_one_tty(struct tty_struct *tty, int idx) +static void release_one_tty(struct kref *kref) { - int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; - struct ktermios *tp; - - if (!devpts) - tty->driver->ttys[idx] = NULL; - - if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { - tp = tty->termios; - if (!devpts) - tty->driver->termios[idx] = NULL; - kfree(tp); - - tp = tty->termios_locked; - if (!devpts) - tty->driver->termios_locked[idx] = NULL; - kfree(tp); - } - + struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + struct tty_driver *driver = tty->driver; + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + else + tty_shutdown(tty); tty->magic = 0; - tty->driver->refcount--; + tty_driver_kref_put(driver); + module_put(driver->owner); file_list_lock(); list_del_init(&tty->tty_files); @@ -1897,6 +1477,21 @@ static void release_one_tty(struct tty_struct *tty, int idx) } /** + * tty_kref_put - release a tty kref + * @tty: tty device + * + * Release a reference to a tty device and if need be let the kref + * layer destruct the object for us + */ + +void tty_kref_put(struct tty_struct *tty) +{ + if (tty) + kref_put(&tty->kref, release_one_tty); +} +EXPORT_SYMBOL(tty_kref_put); + +/** * release_tty - release tty structure memory * * Release both @tty and a possible linked partner (think pty pair), @@ -1907,15 +1502,16 @@ static void release_one_tty(struct tty_struct *tty, int idx) * takes the file list lock internally when working on the list * of ttys that the driver keeps. * FIXME: should we require tty_mutex is held here ?? + * */ static void release_tty(struct tty_struct *tty, int idx) { - struct tty_driver *driver = tty->driver; + /* This should always be true but check for the moment */ + WARN_ON(tty->index != idx); if (tty->link) - release_one_tty(tty->link, idx); - release_one_tty(tty, idx); - module_put(driver->owner); + tty_kref_put(tty->link); + tty_kref_put(tty); } /* @@ -1926,20 +1522,21 @@ static void release_tty(struct tty_struct *tty, int idx) * WSH 09/09/97: rewritten to avoid some nasty race conditions that could * lead to double frees or releasing memory still in use. */ -static void release_dev(struct file *filp) +void tty_release_dev(struct file *filp) { struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts; int idx; char buf[64]; + struct inode *inode; + inode = filp->f_path.dentry->d_inode; tty = (struct tty_struct *)filp->private_data; - if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, - "release_dev")) + if (tty_paranoia_check(tty, inode, "tty_release_dev")) return; - check_tty_count(tty, "release_dev"); + check_tty_count(tty, "tty_release_dev"); tty_fasync(-1, filp, 0); @@ -1951,33 +1548,27 @@ static void release_dev(struct file *filp) #ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { - printk(KERN_DEBUG "release_dev: bad idx when trying to " + printk(KERN_DEBUG "tty_release_dev: bad idx when trying to " "free (%s)\n", tty->name); return; } - if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { + if (!devpts) { if (tty != tty->driver->ttys[idx]) { - printk(KERN_DEBUG "release_dev: driver.table[%d] not tty " + printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty " "for (%s)\n", idx, tty->name); return; } if (tty->termios != tty->driver->termios[idx]) { - printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios " + printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios " "for (%s)\n", idx, tty->name); return; } - if (tty->termios_locked != tty->driver->termios_locked[idx]) { - printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not " - "termios_locked for (%s)\n", - idx, tty->name); - return; - } } #endif #ifdef TTY_DEBUG_HANGUP - printk(KERN_DEBUG "release_dev of %s (tty count=%d)...", + printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...", tty_name(tty, buf), tty->count); #endif @@ -1985,26 +1576,19 @@ static void release_dev(struct file *filp) if (tty->driver->other && !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (o_tty != tty->driver->other->ttys[idx]) { - printk(KERN_DEBUG "release_dev: other->table[%d] " + printk(KERN_DEBUG "tty_release_dev: other->table[%d] " "not o_tty for (%s)\n", idx, tty->name); return; } if (o_tty->termios != tty->driver->other->termios[idx]) { - printk(KERN_DEBUG "release_dev: other->termios[%d] " + printk(KERN_DEBUG "tty_release_dev: other->termios[%d] " "not o_termios for (%s)\n", idx, tty->name); return; } - if (o_tty->termios_locked != - tty->driver->other->termios_locked[idx]) { - printk(KERN_DEBUG "release_dev: other->termios_locked[" - "%d] not o_termios_locked for (%s)\n", - idx, tty->name); - return; - } if (o_tty->link != tty) { - printk(KERN_DEBUG "release_dev: bad pty pointers\n"); + printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n"); return; } } @@ -2062,7 +1646,7 @@ static void release_dev(struct file *filp) if (!do_sleep) break; - printk(KERN_WARNING "release_dev: %s: read/write wait queue " + printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue " "active!\n", tty_name(tty, buf)); mutex_unlock(&tty_mutex); schedule(); @@ -2075,14 +1659,14 @@ static void release_dev(struct file *filp) */ if (pty_master) { if (--o_tty->count < 0) { - printk(KERN_WARNING "release_dev: bad pty slave count " + printk(KERN_WARNING "tty_release_dev: bad pty slave count " "(%d) for %s\n", o_tty->count, tty_name(o_tty, buf)); o_tty->count = 0; } } if (--tty->count < 0) { - printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n", + printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n", tty->count, tty_name(tty, buf)); tty->count = 0; } @@ -2145,11 +1729,11 @@ static void release_dev(struct file *filp) /* Make this pty number available for reallocation */ if (devpts) - devpts_kill_index(idx); + devpts_kill_index(inode, idx); } /** - * tty_open - open a tty device + * __tty_open - open a tty device * @inode: inode of device file * @filp: file pointer to tty * @@ -2164,14 +1748,14 @@ static void release_dev(struct file *filp) * The termios state of a pty is reset on first open so that * settings don't persist across reuse. * - * Locking: tty_mutex protects tty, get_tty_driver and init_dev work. + * Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work. * tty->count should protect the rest. * ->siglock protects ->signal/->sighand */ static int __tty_open(struct inode *inode, struct file *filp) { - struct tty_struct *tty; + struct tty_struct *tty = NULL; int noctty, retval; struct tty_driver *driver; int index; @@ -2193,23 +1777,25 @@ retry_open: mutex_unlock(&tty_mutex); return -ENXIO; } - driver = tty->driver; + driver = tty_driver_kref_get(tty->driver); index = tty->index; filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ /* noctty = 1; */ + /* FIXME: Should we take a driver reference ? */ + tty_kref_put(tty); goto got_driver; } #ifdef CONFIG_VT if (device == MKDEV(TTY_MAJOR, 0)) { extern struct tty_driver *console_driver; - driver = console_driver; + driver = tty_driver_kref_get(console_driver); index = fg_console; noctty = 1; goto got_driver; } #endif if (device == MKDEV(TTYAUX_MAJOR, 1)) { - driver = console_device(&index); + driver = tty_driver_kref_get(console_device(&index)); if (driver) { /* Don't let /dev/console block */ filp->f_flags |= O_NONBLOCK; @@ -2226,10 +1812,25 @@ retry_open: return -ENODEV; } got_driver: - retval = init_dev(driver, index, &tty); + if (!tty) { + /* check whether we're reopening an existing tty */ + tty = tty_driver_lookup_tty(driver, inode, index); + + if (IS_ERR(tty)) + return PTR_ERR(tty); + } + + if (tty) { + retval = tty_reopen(tty); + if (retval) + tty = ERR_PTR(retval); + } else + tty = tty_init_dev(driver, index, 0); + mutex_unlock(&tty_mutex); - if (retval) - return retval; + tty_driver_kref_put(driver); + if (IS_ERR(tty)) + return PTR_ERR(tty); filp->private_data = tty; file_move(filp, &tty->tty_files); @@ -2257,7 +1858,7 @@ got_driver: printk(KERN_DEBUG "error %d in opening %s...", retval, tty->name); #endif - release_dev(filp); + tty_release_dev(filp); if (retval != -ERESTARTSYS) return retval; if (signal_pending(current)) @@ -2296,69 +1897,6 @@ static int tty_open(struct inode *inode, struct file *filp) -#ifdef CONFIG_UNIX98_PTYS -/** - * ptmx_open - open a unix 98 pty master - * @inode: inode of device file - * @filp: file pointer to tty - * - * Allocate a unix98 pty master device from the ptmx driver. - * - * Locking: tty_mutex protects theinit_dev work. tty->count should - * protect the rest. - * allocated_ptys_lock handles the list of free pty numbers - */ - -static int __ptmx_open(struct inode *inode, struct file *filp) -{ - struct tty_struct *tty; - int retval; - int index; - - nonseekable_open(inode, filp); - - /* find a device that is not in use. */ - index = devpts_new_index(); - if (index < 0) - return index; - - mutex_lock(&tty_mutex); - retval = init_dev(ptm_driver, index, &tty); - mutex_unlock(&tty_mutex); - - if (retval) - goto out; - - set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */ - filp->private_data = tty; - file_move(filp, &tty->tty_files); - - retval = devpts_pty_new(tty->link); - if (retval) - goto out1; - - check_tty_count(tty, "ptmx_open"); - retval = ptm_driver->ops->open(tty, filp); - if (!retval) - return 0; -out1: - release_dev(filp); - return retval; -out: - devpts_kill_index(index); - return retval; -} - -static int ptmx_open(struct inode *inode, struct file *filp) -{ - int ret; - - lock_kernel(); - ret = __ptmx_open(inode, filp); - unlock_kernel(); - return ret; -} -#endif /** * tty_release - vfs callback for close @@ -2369,13 +1907,13 @@ static int ptmx_open(struct inode *inode, struct file *filp) * this tty. There may however be several such references. * * Locking: - * Takes bkl. See release_dev + * Takes bkl. See tty_release_dev */ static int tty_release(struct inode *inode, struct file *filp) { lock_kernel(); - release_dev(filp); + tty_release_dev(filp); unlock_kernel(); return 0; } @@ -2524,7 +2062,7 @@ int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, /* For a PTY we need to lock the tty side */ mutex_lock(&real_tty->termios_mutex); - if (!memcmp(ws, &tty->winsize, sizeof(*ws))) + if (!memcmp(ws, &real_tty->winsize, sizeof(*ws))) goto done; /* Get the PID values and reference them so we can avoid holding the tty ctrl lock while sending signals */ @@ -2996,7 +2534,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCSTI: return tiocsti(tty, p); case TIOCGWINSZ: - return tiocgwinsz(tty, p); + return tiocgwinsz(real_tty, p); case TIOCSWINSZ: return tiocswinsz(tty, real_tty, p); case TIOCCONS: @@ -3026,10 +2564,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return put_user(tty->ldisc.ops->num, (int __user *)p); case TIOCSETD: return tiocsetd(tty, p); -#ifdef CONFIG_VT - case TIOCLINUX: - return tioclinux(tty, arg); -#endif /* * Break handling */ @@ -3220,113 +2754,6 @@ void do_SAK(struct tty_struct *tty) EXPORT_SYMBOL(do_SAK); /** - * flush_to_ldisc - * @work: tty structure passed from work queue. - * - * This routine is called out of the software interrupt to flush data - * from the buffer chain to the line discipline. - * - * Locking: holds tty->buf.lock to guard buffer list. Drops the lock - * while invoking the line discipline receive_buf method. The - * receive_buf method is single threaded for each tty instance. - */ - -static void flush_to_ldisc(struct work_struct *work) -{ - struct tty_struct *tty = - container_of(work, struct tty_struct, buf.work.work); - unsigned long flags; - struct tty_ldisc *disc; - struct tty_buffer *tbuf, *head; - char *char_buf; - unsigned char *flag_buf; - - disc = tty_ldisc_ref(tty); - if (disc == NULL) /* !TTY_LDISC */ - return; - - spin_lock_irqsave(&tty->buf.lock, flags); - /* So we know a flush is running */ - set_bit(TTY_FLUSHING, &tty->flags); - head = tty->buf.head; - if (head != NULL) { - tty->buf.head = NULL; - for (;;) { - int count = head->commit - head->read; - if (!count) { - if (head->next == NULL) - break; - tbuf = head; - head = head->next; - tty_buffer_free(tty, tbuf); - continue; - } - /* Ldisc or user is trying to flush the buffers - we are feeding to the ldisc, stop feeding the - line discipline as we want to empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) - break; - if (!tty->receive_room) { - schedule_delayed_work(&tty->buf.work, 1); - break; - } - if (count > tty->receive_room) - count = tty->receive_room; - char_buf = head->char_buf_ptr + head->read; - flag_buf = head->flag_buf_ptr + head->read; - head->read += count; - spin_unlock_irqrestore(&tty->buf.lock, flags); - disc->ops->receive_buf(tty, char_buf, - flag_buf, count); - spin_lock_irqsave(&tty->buf.lock, flags); - } - /* Restore the queue head */ - tty->buf.head = head; - } - /* We may have a deferred request to flush the input buffer, - if so pull the chain under the lock and empty the queue */ - if (test_bit(TTY_FLUSHPENDING, &tty->flags)) { - __tty_buffer_flush(tty); - clear_bit(TTY_FLUSHPENDING, &tty->flags); - wake_up(&tty->read_wait); - } - clear_bit(TTY_FLUSHING, &tty->flags); - spin_unlock_irqrestore(&tty->buf.lock, flags); - - tty_ldisc_deref(disc); -} - -/** - * tty_flip_buffer_push - terminal - * @tty: tty to push - * - * Queue a push of the terminal flip buffers to the line discipline. This - * function must not be called from IRQ context if tty->low_latency is set. - * - * In the event of the queue being busy for flipping the work will be - * held off and retried later. - * - * Locking: tty buffer lock. Driver locks in low latency mode. - */ - -void tty_flip_buffer_push(struct tty_struct *tty) -{ - unsigned long flags; - spin_lock_irqsave(&tty->buf.lock, flags); - if (tty->buf.tail != NULL) - tty->buf.tail->commit = tty->buf.tail->used; - spin_unlock_irqrestore(&tty->buf.lock, flags); - - if (tty->low_latency) - flush_to_ldisc(&tty->buf.work.work); - else - schedule_delayed_work(&tty->buf.work, 1); -} - -EXPORT_SYMBOL(tty_flip_buffer_push); - - -/** * initialize_tty_struct * @tty: tty to initialize * @@ -3336,9 +2763,11 @@ EXPORT_SYMBOL(tty_flip_buffer_push); * Locking: none - tty in question must not be exposed at this point */ -static void initialize_tty_struct(struct tty_struct *tty) +void initialize_tty_struct(struct tty_struct *tty, + struct tty_driver *driver, int idx) { memset(tty, 0, sizeof(struct tty_struct)); + kref_init(&tty->kref); tty->magic = TTY_MAGIC; tty_ldisc_init(tty); tty->session = NULL; @@ -3346,7 +2775,6 @@ static void initialize_tty_struct(struct tty_struct *tty) tty->overrun_time = jiffies; tty->buf.head = tty->buf.tail = NULL; tty_buffer_init(tty); - INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc); mutex_init(&tty->termios_mutex); init_waitqueue_head(&tty->write_wait); init_waitqueue_head(&tty->read_wait); @@ -3357,6 +2785,11 @@ static void initialize_tty_struct(struct tty_struct *tty) spin_lock_init(&tty->ctrl_lock); INIT_LIST_HEAD(&tty->tty_files); INIT_WORK(&tty->SAK_work, do_SAK_work); + + tty->driver = driver; + tty->ops = driver->ops; + tty->index = idx; + tty_line_name(driver, idx, tty->name); } /** @@ -3377,10 +2810,9 @@ int tty_put_char(struct tty_struct *tty, unsigned char ch) return tty->ops->put_char(tty, ch); return tty->ops->write(tty, &ch, 1); } - EXPORT_SYMBOL_GPL(tty_put_char); -static struct class *tty_class; +struct class *tty_class; /** * tty_register_device - register a tty device @@ -3420,6 +2852,7 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index, return device_create_drvdata(tty_class, device, dev, NULL, name); } +EXPORT_SYMBOL(tty_register_device); /** * tty_unregister_device - unregister a tty device @@ -3437,8 +2870,6 @@ void tty_unregister_device(struct tty_driver *driver, unsigned index) device_destroy(tty_class, MKDEV(driver->major, driver->minor_start) + index); } - -EXPORT_SYMBOL(tty_register_device); EXPORT_SYMBOL(tty_unregister_device); struct tty_driver *alloc_tty_driver(int lines) @@ -3447,27 +2878,65 @@ struct tty_driver *alloc_tty_driver(int lines) driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL); if (driver) { + kref_init(&driver->kref); driver->magic = TTY_DRIVER_MAGIC; driver->num = lines; /* later we'll move allocation of tables here */ } return driver; } +EXPORT_SYMBOL(alloc_tty_driver); -void put_tty_driver(struct tty_driver *driver) +static void destruct_tty_driver(struct kref *kref) { + struct tty_driver *driver = container_of(kref, struct tty_driver, kref); + int i; + struct ktermios *tp; + void *p; + + if (driver->flags & TTY_DRIVER_INSTALLED) { + /* + * Free the termios and termios_locked structures because + * we don't want to get memory leaks when modular tty + * drivers are removed from the kernel. + */ + for (i = 0; i < driver->num; i++) { + tp = driver->termios[i]; + if (tp) { + driver->termios[i] = NULL; + kfree(tp); + } + if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) + tty_unregister_device(driver, i); + } + p = driver->ttys; + proc_tty_unregister_driver(driver); + driver->ttys = NULL; + driver->termios = NULL; + kfree(p); + cdev_del(&driver->cdev); + } kfree(driver); } +void tty_driver_kref_put(struct tty_driver *driver) +{ + kref_put(&driver->kref, destruct_tty_driver); +} +EXPORT_SYMBOL(tty_driver_kref_put); + void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op) { driver->ops = op; }; +EXPORT_SYMBOL(tty_set_operations); -EXPORT_SYMBOL(alloc_tty_driver); +void put_tty_driver(struct tty_driver *d) +{ + tty_driver_kref_put(d); +} EXPORT_SYMBOL(put_tty_driver); -EXPORT_SYMBOL(tty_set_operations); /* * Called by a tty driver to register itself. @@ -3479,11 +2948,8 @@ int tty_register_driver(struct tty_driver *driver) dev_t dev; void **p = NULL; - if (driver->flags & TTY_DRIVER_INSTALLED) - return 0; - if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) { - p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL); + p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL); if (!p) return -ENOMEM; } @@ -3507,12 +2973,9 @@ int tty_register_driver(struct tty_driver *driver) if (p) { driver->ttys = (struct tty_struct **)p; driver->termios = (struct ktermios **)(p + driver->num); - driver->termios_locked = (struct ktermios **) - (p + driver->num * 2); } else { driver->ttys = NULL; driver->termios = NULL; - driver->termios_locked = NULL; } cdev_init(&driver->cdev, &tty_fops); @@ -3521,7 +2984,7 @@ int tty_register_driver(struct tty_driver *driver) if (error) { unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; - driver->termios = driver->termios_locked = NULL; + driver->termios = NULL; kfree(p); return error; } @@ -3535,6 +2998,7 @@ int tty_register_driver(struct tty_driver *driver) tty_register_device(driver, i, NULL); } proc_tty_register_driver(driver); + driver->flags |= TTY_DRIVER_INSTALLED; return 0; } @@ -3545,46 +3009,19 @@ EXPORT_SYMBOL(tty_register_driver); */ int tty_unregister_driver(struct tty_driver *driver) { - int i; - struct ktermios *tp; - void *p; - +#if 0 + /* FIXME */ if (driver->refcount) return -EBUSY; - +#endif unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), driver->num); mutex_lock(&tty_mutex); list_del(&driver->tty_drivers); mutex_unlock(&tty_mutex); - - /* - * Free the termios and termios_locked structures because - * we don't want to get memory leaks when modular tty - * drivers are removed from the kernel. - */ - for (i = 0; i < driver->num; i++) { - tp = driver->termios[i]; - if (tp) { - driver->termios[i] = NULL; - kfree(tp); - } - tp = driver->termios_locked[i]; - if (tp) { - driver->termios_locked[i] = NULL; - kfree(tp); - } - if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) - tty_unregister_device(driver, i); - } - p = driver->ttys; - proc_tty_unregister_driver(driver); - driver->ttys = NULL; - driver->termios = driver->termios_locked = NULL; - kfree(p); - cdev_del(&driver->cdev); return 0; } + EXPORT_SYMBOL(tty_unregister_driver); dev_t tty_devnum(struct tty_struct *tty) @@ -3595,9 +3032,12 @@ EXPORT_SYMBOL(tty_devnum); void proc_clear_tty(struct task_struct *p) { + struct tty_struct *tty; spin_lock_irq(&p->sighand->siglock); + tty = p->signal->tty; p->signal->tty = NULL; spin_unlock_irq(&p->sighand->siglock); + tty_kref_put(tty); } /* Called under the sighand lock */ @@ -3613,9 +3053,13 @@ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) tty->pgrp = get_pid(task_pgrp(tsk)); spin_unlock_irqrestore(&tty->ctrl_lock, flags); tty->session = get_pid(task_session(tsk)); + if (tsk->signal->tty) { + printk(KERN_DEBUG "tty not NULL!!\n"); + tty_kref_put(tsk->signal->tty); + } } put_pid(tsk->signal->tty_old_pgrp); - tsk->signal->tty = tty; + tsk->signal->tty = tty_kref_get(tty); tsk->signal->tty_old_pgrp = NULL; } @@ -3629,18 +3073,20 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty) struct tty_struct *get_current_tty(void) { struct tty_struct *tty; - WARN_ON_ONCE(!mutex_is_locked(&tty_mutex)); - tty = current->signal->tty; - /* - * session->tty can be changed/cleared from under us, make sure we - * issue the load. The obtained pointer, when not NULL, is valid as - * long as we hold tty_mutex. - */ - barrier(); + unsigned long flags; + + spin_lock_irqsave(¤t->sighand->siglock, flags); + tty = tty_kref_get(current->signal->tty); + spin_unlock_irqrestore(¤t->sighand->siglock, flags); return tty; } EXPORT_SYMBOL_GPL(get_current_tty); +void tty_default_fops(struct file_operations *fops) +{ + *fops = tty_fops; +} + /* * Initialize the console device. This is called *early*, so * we can't necessarily depend on lots of kernel help here. @@ -3678,12 +3124,6 @@ postcore_initcall(tty_class_init); /* 3/2004 jmc: why do these devices exist? */ static struct cdev tty_cdev, console_cdev; -#ifdef CONFIG_UNIX98_PTYS -static struct cdev ptmx_cdev; -#endif -#ifdef CONFIG_VT -static struct cdev vc0_cdev; -#endif /* * Ok, now we can initialize the rest of the tty devices and can count @@ -3695,32 +3135,18 @@ static int __init tty_init(void) if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) panic("Couldn't register /dev/tty driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty"); cdev_init(&console_cdev, &console_fops); if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) panic("Couldn't register /dev/console driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, + device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console"); -#ifdef CONFIG_UNIX98_PTYS - cdev_init(&ptmx_cdev, &ptmx_fops); - if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) || - register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0) - panic("Couldn't register /dev/ptmx driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx"); -#endif - #ifdef CONFIG_VT - cdev_init(&vc0_cdev, &console_fops); - if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || - register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) - panic("Couldn't register /dev/tty0 driver\n"); - device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); - - vty_init(); + vty_init(&console_fops); #endif return 0; } diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index bf34e459742..a408c8e487e 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -40,6 +40,15 @@ #define TERMIOS_OLD 8 +/** + * tty_chars_in_buffer - characters pending + * @tty: terminal + * + * Return the number of bytes of data in the device private + * output queue. If no private method is supplied there is assumed + * to be no queue on the device. + */ + int tty_chars_in_buffer(struct tty_struct *tty) { if (tty->ops->chars_in_buffer) @@ -47,26 +56,49 @@ int tty_chars_in_buffer(struct tty_struct *tty) else return 0; } - EXPORT_SYMBOL(tty_chars_in_buffer); +/** + * tty_write_room - write queue space + * @tty: terminal + * + * Return the number of bytes that can be queued to this device + * at the present time. The result should be treated as a guarantee + * and the driver cannot offer a value it later shrinks by more than + * the number of bytes written. If no method is provided 2K is always + * returned and data may be lost as there will be no flow control. + */ + int tty_write_room(struct tty_struct *tty) { if (tty->ops->write_room) return tty->ops->write_room(tty); return 2048; } - EXPORT_SYMBOL(tty_write_room); +/** + * tty_driver_flush_buffer - discard internal buffer + * @tty: terminal + * + * Discard the internal output buffer for this device. If no method + * is provided then either the buffer cannot be hardware flushed or + * there is no buffer driver side. + */ void tty_driver_flush_buffer(struct tty_struct *tty) { if (tty->ops->flush_buffer) tty->ops->flush_buffer(tty); } - EXPORT_SYMBOL(tty_driver_flush_buffer); +/** + * tty_throttle - flow control + * @tty: terminal + * + * Indicate that a tty should stop transmitting data down the stack. + */ + void tty_throttle(struct tty_struct *tty) { /* check TTY_THROTTLED first so it indicates our state */ @@ -76,6 +108,13 @@ void tty_throttle(struct tty_struct *tty) } EXPORT_SYMBOL(tty_throttle); +/** + * tty_unthrottle - flow control + * @tty: terminal + * + * Indicate that a tty may continue transmitting data down the stack. + */ + void tty_unthrottle(struct tty_struct *tty) { if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) && @@ -112,6 +151,11 @@ void tty_wait_until_sent(struct tty_struct *tty, long timeout) } EXPORT_SYMBOL(tty_wait_until_sent); + +/* + * Termios Helper Methods + */ + static void unset_locked_termios(struct ktermios *termios, struct ktermios *old, struct ktermios *locked) @@ -346,6 +390,16 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, } EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); +/** + * tty_encode_baud_rate - set baud rate of the tty + * @ibaud: input baud rate + * @obad: output baud rate + * + * Update the current termios data for the tty with the new speed + * settings. The caller must hold the termios_mutex for the tty in + * question. + */ + void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud) { tty_termios_encode_baud_rate(tty->termios, ibaud, obaud); @@ -430,12 +484,11 @@ EXPORT_SYMBOL(tty_termios_hw_change); * is a bit of layering violation here with n_tty in terms of the * internal knowledge of this function. * - * Locking: termios_sem + * Locking: termios_mutex */ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) { - int canon_change; struct ktermios old_termios; struct tty_ldisc *ld; unsigned long flags; @@ -451,18 +504,6 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) old_termios = *tty->termios; *tty->termios = *new_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); - canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; - if (canon_change) { - memset(&tty->read_flags, 0, sizeof tty->read_flags); - tty->canon_head = tty->read_tail; - tty->canon_data = 0; - tty->erasing = 0; - } - - /* This bit should be in the ldisc code */ - if (canon_change && !L_ICANON(tty) && tty->read_cnt) - /* Get characters left over from canonical mode. */ - wake_up_interruptible(&tty->read_wait); /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { @@ -508,7 +549,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) * functions before using change_termios to do the actual changes. * * Locking: - * Called functions take ldisc and termios_sem locks + * Called functions take ldisc and termios_mutex locks */ static int set_termios(struct tty_struct *tty, void __user *arg, int opt) @@ -579,25 +620,51 @@ static int get_termio(struct tty_struct *tty, struct termio __user *termio) return 0; } -static unsigned long inq_canon(struct tty_struct *tty) + +#ifdef TCGETX + +/** + * set_termiox - set termiox fields if possible + * @tty: terminal + * @arg: termiox structure from user + * @opt: option flags for ioctl type + * + * Implement the device calling points for the SYS5 termiox ioctl + * interface in Linux + */ + +static int set_termiox(struct tty_struct *tty, void __user *arg, int opt) { - int nr, head, tail; + struct termiox tnew; + struct tty_ldisc *ld; - if (!tty->canon_data || !tty->read_buf) - return 0; - head = tty->canon_head; - tail = tty->read_tail; - nr = (head - tail) & (N_TTY_BUF_SIZE-1); - /* Skip EOF-chars.. */ - while (head != tail) { - if (test_bit(tail, tty->read_flags) && - tty->read_buf[tail] == __DISABLED_CHAR) - nr--; - tail = (tail+1) & (N_TTY_BUF_SIZE-1); + if (tty->termiox == NULL) + return -EINVAL; + if (copy_from_user(&tnew, arg, sizeof(struct termiox))) + return -EFAULT; + + ld = tty_ldisc_ref(tty); + if (ld != NULL) { + if ((opt & TERMIOS_FLUSH) && ld->ops->flush_buffer) + ld->ops->flush_buffer(tty); + tty_ldisc_deref(ld); } - return nr; + if (opt & TERMIOS_WAIT) { + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + } + + mutex_lock(&tty->termios_mutex); + if (tty->ops->set_termiox) + tty->ops->set_termiox(tty, &tnew); + mutex_unlock(&tty->termios_mutex); + return 0; } +#endif + + #ifdef TIOCGETP /* * These are deprecated, but there is limited support.. @@ -671,7 +738,7 @@ static void set_sgflags(struct ktermios *termios, int flags) * Updates a terminal from the legacy BSD style terminal information * structure. * - * Locking: termios_sem + * Locking: termios_mutex */ static int set_sgttyb(struct tty_struct *tty, struct sgttyb __user *sgttyb) @@ -849,6 +916,7 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, { struct tty_struct *real_tty; void __user *p = (void __user *)arg; + int ret = 0; if (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER) @@ -884,18 +952,24 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, return set_termios(real_tty, p, TERMIOS_OLD); #ifndef TCGETS2 case TCGETS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; #else case TCGETS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TCGETS2: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TCSETSF2: return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT); case TCSETSW2: @@ -913,34 +987,59 @@ int tty_mode_ioctl(struct tty_struct *tty, struct file *file, return set_termios(real_tty, p, TERMIOS_TERMIO); #ifndef TCGETS2 case TIOCGLCKTRMIOS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios_locked)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TIOCSLCKTRMIOS: if (!capable(CAP_SYS_ADMIN)) return -EPERM; + mutex_lock(&real_tty->termios_mutex); if (user_termios_to_kernel_termios(real_tty->termios_locked, (struct termios __user *) arg)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; #else case TIOCGLCKTRMIOS: + mutex_lock(&real_tty->termios_mutex); if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios_locked)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; case TIOCSLCKTRMIOS: if (!capable(CAP_SYS_ADMIN)) - return -EPERM; + ret = -EPERM; + mutex_lock(&real_tty->termios_mutex); if (user_termios_to_kernel_termios_1(real_tty->termios_locked, (struct termios __user *) arg)) - return -EFAULT; - return 0; + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; #endif +#ifdef TCGETX + case TCGETX: + if (real_tty->termiox == NULL) + return -EINVAL; + mutex_lock(&real_tty->termios_mutex); + if (copy_to_user(p, real_tty->termiox, sizeof(struct termiox))) + ret = -EFAULT; + mutex_unlock(&real_tty->termios_mutex); + return ret; + case TCSETX: + return set_termiox(real_tty, p, 0); + case TCSETXW: + return set_termiox(real_tty, p, TERMIOS_WAIT); + case TCSETXF: + return set_termiox(real_tty, p, TERMIOS_FLUSH); +#endif case TIOCGSOFTCAR: - /* FIXME: for correctness we may need to take the termios - lock here - review */ - return put_user(C_CLOCAL(real_tty) ? 1 : 0, + mutex_lock(&real_tty->termios_mutex); + ret = put_user(C_CLOCAL(real_tty) ? 1 : 0, (int __user *)arg); + mutex_unlock(&real_tty->termios_mutex); + return ret; case TIOCSSOFTCAR: if (get_user(arg, (unsigned int __user *) arg)) return -EFAULT; @@ -980,7 +1079,7 @@ int tty_perform_flush(struct tty_struct *tty, unsigned long arg) } EXPORT_SYMBOL_GPL(tty_perform_flush); -int n_tty_ioctl(struct tty_struct *tty, struct file *file, +int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { unsigned long flags; @@ -1018,13 +1117,6 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return 0; case TCFLSH: return tty_perform_flush(tty, arg); - case TIOCOUTQ: - return put_user(tty_chars_in_buffer(tty), (int __user *) arg); - case TIOCINQ: - retval = tty->read_cnt; - if (L_ICANON(tty)) - retval = inq_canon(tty); - return put_user(retval, (unsigned int __user *) arg); case TIOCPKT: { int pktmode; @@ -1050,4 +1142,4 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, return tty_mode_ioctl(tty, file, cmd, arg); } } -EXPORT_SYMBOL(n_tty_ioctl); +EXPORT_SYMBOL(n_tty_ioctl_helper); diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c new file mode 100644 index 00000000000..553b0e9d8d1 --- /dev/null +++ b/drivers/char/tty_port.c @@ -0,0 +1,96 @@ +/* + * Tty port functions + */ + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/tty.h> +#include <linux/tty_driver.h> +#include <linux/tty_flip.h> +#include <linux/timer.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/module.h> + +void tty_port_init(struct tty_port *port) +{ + memset(port, 0, sizeof(*port)); + init_waitqueue_head(&port->open_wait); + init_waitqueue_head(&port->close_wait); + mutex_init(&port->mutex); + spin_lock_init(&port->lock); + port->close_delay = (50 * HZ) / 100; + port->closing_wait = (3000 * HZ) / 100; +} +EXPORT_SYMBOL(tty_port_init); + +int tty_port_alloc_xmit_buf(struct tty_port *port) +{ + /* We may sleep in get_zeroed_page() */ + mutex_lock(&port->mutex); + if (port->xmit_buf == NULL) + port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL); + mutex_unlock(&port->mutex); + if (port->xmit_buf == NULL) + return -ENOMEM; + return 0; +} +EXPORT_SYMBOL(tty_port_alloc_xmit_buf); + +void tty_port_free_xmit_buf(struct tty_port *port) +{ + mutex_lock(&port->mutex); + if (port->xmit_buf != NULL) { + free_page((unsigned long)port->xmit_buf); + port->xmit_buf = NULL; + } + mutex_unlock(&port->mutex); +} +EXPORT_SYMBOL(tty_port_free_xmit_buf); + + +/** + * tty_port_tty_get - get a tty reference + * @port: tty port + * + * Return a refcount protected tty instance or NULL if the port is not + * associated with a tty (eg due to close or hangup) + */ + +struct tty_struct *tty_port_tty_get(struct tty_port *port) +{ + unsigned long flags; + struct tty_struct *tty; + + spin_lock_irqsave(&port->lock, flags); + tty = tty_kref_get(port->tty); + spin_unlock_irqrestore(&port->lock, flags); + return tty; +} +EXPORT_SYMBOL(tty_port_tty_get); + +/** + * tty_port_tty_set - set the tty of a port + * @port: tty port + * @tty: the tty + * + * Associate the port and tty pair. Manages any internal refcounts. + * Pass NULL to deassociate a port + */ + +void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (port->tty) + tty_kref_put(port->tty); + port->tty = tty; + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL(tty_port_tty_set); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 60359c36091..57029fefd64 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -100,10 +100,10 @@ #include <linux/font.h> #include <linux/bitops.h> #include <linux/notifier.h> - -#include <asm/io.h> +#include <linux/device.h> +#include <linux/io.h> #include <asm/system.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #define MAX_NR_CON_DRIVER 16 @@ -2136,27 +2136,9 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co release_console_sem(); return 0; } - release_console_sem(); - orig_buf = buf; orig_count = count; - /* At this point 'buf' is guaranteed to be a kernel buffer - * and therefore no access to userspace (and therefore sleeping) - * will be needed. The con_buf_mtx serializes all tty based - * console rendering and vcs write/read operations. We hold - * the console spinlock during the entire write. - */ - - acquire_console_sem(); - - vc = tty->driver_data; - if (vc == NULL) { - printk(KERN_ERR "vt: argh, driver_data _became_ NULL !\n"); - release_console_sem(); - goto out; - } - himask = vc->vc_hi_font_mask; charmask = himask ? 0x1ff : 0xff; @@ -2370,8 +2352,6 @@ rescan_last_byte: FLUSH console_conditional_schedule(); release_console_sem(); - -out: notify_update(vc); return n; #undef FLUSH @@ -2583,8 +2563,6 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) int lines; int ret; - if (tty->driver->type != TTY_DRIVER_TYPE_CONSOLE) - return -EINVAL; if (current->signal->tty != tty && !capable(CAP_SYS_ADMIN)) return -EPERM; if (get_user(type, p)) @@ -2778,6 +2756,12 @@ static int con_open(struct tty_struct *tty, struct file *filp) ret = vc_allocate(currcons); if (ret == 0) { struct vc_data *vc = vc_cons[currcons].d; + + /* Still being freed */ + if (vc->vc_tty) { + release_console_sem(); + return -ERESTARTSYS; + } tty->driver_data = vc; vc->vc_tty = tty; @@ -2798,34 +2782,20 @@ static int con_open(struct tty_struct *tty, struct file *filp) return ret; } -/* - * We take tty_mutex in here to prevent another thread from coming in via init_dev - * and taking a ref against the tty while we're in the process of forgetting - * about it and cleaning things up. - * - * This is because vcs_remove_sysfs() can sleep and will drop the BKL. - */ static void con_close(struct tty_struct *tty, struct file *filp) { - mutex_lock(&tty_mutex); - acquire_console_sem(); - if (tty && tty->count == 1) { - struct vc_data *vc = tty->driver_data; + /* Nothing to do - we defer to shutdown */ +} - if (vc) - vc->vc_tty = NULL; - tty->driver_data = NULL; - vcs_remove_sysfs(tty); - release_console_sem(); - mutex_unlock(&tty_mutex); - /* - * tty_mutex is released, but we still hold BKL, so there is - * still exclusion against init_dev() - */ - return; - } +static void con_shutdown(struct tty_struct *tty) +{ + struct vc_data *vc = tty->driver_data; + BUG_ON(vc == NULL); + acquire_console_sem(); + vc->vc_tty = NULL; + vcs_remove_sysfs(tty); release_console_sem(); - mutex_unlock(&tty_mutex); + tty_shutdown(tty); } static int default_italic_color = 2; // green (ASCII) @@ -2950,10 +2920,19 @@ static const struct tty_operations con_ops = { .throttle = con_throttle, .unthrottle = con_unthrottle, .resize = vt_resize, + .shutdown = con_shutdown }; -int __init vty_init(void) +static struct cdev vc0_cdev; + +int __init vty_init(const struct file_operations *console_fops) { + cdev_init(&vc0_cdev, console_fops); + if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) || + register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) + panic("Couldn't register /dev/tty0 driver\n"); + device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0"); + vcs_init(); console_driver = alloc_tty_driver(MAX_NR_CONSOLES); @@ -2972,7 +2951,6 @@ int __init vty_init(void) tty_set_operations(console_driver, &con_ops); if (tty_register_driver(console_driver)) panic("Couldn't register console driver\n"); - kbd_init(); console_map_init(); #ifdef CONFIG_PROM_CONSOLE @@ -3466,7 +3444,7 @@ int register_con_driver(const struct consw *csw, int first, int last) if (retval) goto err; - con_driver->dev = device_create_drvdata(vtconsole_class, NULL, + con_driver->dev = device_create(vtconsole_class, NULL, MKDEV(0, con_driver->node), NULL, "vtcon%i", con_driver->node); @@ -3577,7 +3555,7 @@ static int __init vtconsole_class_init(void) struct con_driver *con = ®istered_con_driver[i]; if (con->con && !con->dev) { - con->dev = device_create_drvdata(vtconsole_class, NULL, + con->dev = device_create(vtconsole_class, NULL, MKDEV(0, con->node), NULL, "vtcon%i", con->node); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index c904e9ad4a7..8944ce508e2 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -395,6 +395,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, kbd = kbd_table + console; switch (cmd) { + case TIOCLINUX: + return tioclinux(tty, arg); case KIOCSOUND: if (!perm) goto eperm; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3309e862f31..ebacc0af40f 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -800,6 +800,13 @@ config SENSORS_W83627EHF This driver can also be built as a module. If so, the module will be called w83627ehf. +config SENSORS_ULTRA45 + tristate "Sun Ultra45 PIC16F747" + depends on SPARC64 + help + This driver provides support for the Ultra45 workstation environmental + sensors. + config SENSORS_HDAPS tristate "IBM Hard Drive Active Protection System (hdaps)" depends on INPUT && X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6babc801b34..042d5a78622 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c new file mode 100644 index 00000000000..68e90abeba9 --- /dev/null +++ b/drivers/hwmon/ultra45_env.c @@ -0,0 +1,320 @@ +/* ultra45_env.c: Driver for Ultra45 PIC16F747 environmental monitor. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/of_device.h> +#include <linux/io.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#define DRV_MODULE_VERSION "0.1" + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("Ultra45 environmental monitor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_MODULE_VERSION); + +/* PIC device registers */ +#define REG_CMD 0x00UL +#define REG_CMD_RESET 0x80 +#define REG_CMD_ESTAR 0x01 +#define REG_STAT 0x01UL +#define REG_STAT_FWVER 0xf0 +#define REG_STAT_TGOOD 0x08 +#define REG_STAT_STALE 0x04 +#define REG_STAT_BUSY 0x02 +#define REG_STAT_FAULT 0x01 +#define REG_DATA 0x40UL +#define REG_ADDR 0x41UL +#define REG_SIZE 0x42UL + +/* Registers accessed indirectly via REG_DATA/REG_ADDR */ +#define IREG_FAN0 0x00 +#define IREG_FAN1 0x01 +#define IREG_FAN2 0x02 +#define IREG_FAN3 0x03 +#define IREG_FAN4 0x04 +#define IREG_FAN5 0x05 +#define IREG_LCL_TEMP 0x06 +#define IREG_RMT1_TEMP 0x07 +#define IREG_RMT2_TEMP 0x08 +#define IREG_RMT3_TEMP 0x09 +#define IREG_LM95221_TEMP 0x0a +#define IREG_FIRE_TEMP 0x0b +#define IREG_LSI1064_TEMP 0x0c +#define IREG_FRONT_TEMP 0x0d +#define IREG_FAN_STAT 0x0e +#define IREG_VCORE0 0x0f +#define IREG_VCORE1 0x10 +#define IREG_VMEM0 0x11 +#define IREG_VMEM1 0x12 +#define IREG_PSU_TEMP 0x13 + +struct env { + void __iomem *regs; + spinlock_t lock; + + struct device *hwmon_dev; +}; + +static u8 env_read(struct env *p, u8 ireg) +{ + u8 ret; + + spin_lock(&p->lock); + writeb(ireg, p->regs + REG_ADDR); + ret = readb(p->regs + REG_DATA); + spin_unlock(&p->lock); + + return ret; +} + +static void env_write(struct env *p, u8 ireg, u8 val) +{ + spin_lock(&p->lock); + writeb(ireg, p->regs + REG_ADDR); + writeb(val, p->regs + REG_DATA); + spin_unlock(&p->lock); +} + +/* There seems to be a adr7462 providing these values, thus a lot + * of these calculations are borrowed from the adt7470 driver. + */ +#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) +#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM +#define FAN_PERIOD_INVALID (0xff << 8) +#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) + +static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + int fan_nr = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + int rpm, period; + u8 val; + + val = env_read(p, IREG_FAN0 + fan_nr); + period = (int) val << 8; + if (FAN_DATA_VALID(period)) + rpm = FAN_PERIOD_TO_RPM(period); + else + rpm = 0; + + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t set_fan_speed(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int fan_nr = to_sensor_dev_attr(attr)->index; + int rpm = simple_strtol(buf, NULL, 10); + struct env *p = dev_get_drvdata(dev); + int period; + u8 val; + + if (!rpm) + return -EINVAL; + + period = FAN_RPM_TO_PERIOD(rpm); + val = period >> 8; + env_write(p, IREG_FAN0 + fan_nr, val); + + return count; +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute *attr, char *buf) +{ + int fan_nr = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + u8 val = env_read(p, IREG_FAN_STAT); + return sprintf(buf, "%d\n", (val & (1 << fan_nr)) ? 1 : 0); +} + +#define fan(index) \ +static SENSOR_DEVICE_ATTR(fan##index##_speed, S_IRUGO | S_IWUSR, \ + show_fan_speed, set_fan_speed, index); \ +static SENSOR_DEVICE_ATTR(fan##index##_fault, S_IRUGO, \ + show_fan_fault, NULL, index) + +fan(0); +fan(1); +fan(2); +fan(3); +fan(4); + +static SENSOR_DEVICE_ATTR(psu_fan_fault, S_IRUGO, show_fan_fault, NULL, 6); + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + int temp_nr = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + s8 val; + + val = env_read(p, IREG_LCL_TEMP + temp_nr); + return sprintf(buf, "%d\n", ((int) val) - 64); +} + +static SENSOR_DEVICE_ATTR(adt7462_local_temp, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(cpu0_temp, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(cpu1_temp, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(motherboard_temp, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(lm95221_local_temp, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(fire_temp, S_IRUGO, show_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(lsi1064_local_temp, S_IRUGO, show_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(front_panel_temp, S_IRUGO, show_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(psu_temp, S_IRUGO, show_temp, NULL, 13); + +static ssize_t show_stat_bit(struct device *dev, struct device_attribute *attr, char *buf) +{ + int index = to_sensor_dev_attr(attr)->index; + struct env *p = dev_get_drvdata(dev); + u8 val; + + val = readb(p->regs + REG_STAT); + return sprintf(buf, "%d\n", (val & (1 << index)) ? 1 : 0); +} + +static SENSOR_DEVICE_ATTR(fan_failure, S_IRUGO, show_stat_bit, NULL, 0); +static SENSOR_DEVICE_ATTR(env_bus_busy, S_IRUGO, show_stat_bit, NULL, 1); +static SENSOR_DEVICE_ATTR(env_data_stale, S_IRUGO, show_stat_bit, NULL, 2); +static SENSOR_DEVICE_ATTR(tpm_self_test_passed, S_IRUGO, show_stat_bit, NULL, 3); + +static ssize_t show_fwver(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct env *p = dev_get_drvdata(dev); + u8 val; + + val = readb(p->regs + REG_STAT); + return sprintf(buf, "%d\n", val >> 4); +} + +static SENSOR_DEVICE_ATTR(firmware_version, S_IRUGO, show_fwver, NULL, 0); + +static ssize_t show_name(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ultra45\n"); +} + +static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); + +static struct attribute *env_attributes[] = { + &sensor_dev_attr_fan0_speed.dev_attr.attr, + &sensor_dev_attr_fan0_fault.dev_attr.attr, + &sensor_dev_attr_fan1_speed.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + &sensor_dev_attr_fan2_speed.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + &sensor_dev_attr_fan3_speed.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + &sensor_dev_attr_fan4_speed.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + &sensor_dev_attr_psu_fan_fault.dev_attr.attr, + &sensor_dev_attr_adt7462_local_temp.dev_attr.attr, + &sensor_dev_attr_cpu0_temp.dev_attr.attr, + &sensor_dev_attr_cpu1_temp.dev_attr.attr, + &sensor_dev_attr_motherboard_temp.dev_attr.attr, + &sensor_dev_attr_lm95221_local_temp.dev_attr.attr, + &sensor_dev_attr_fire_temp.dev_attr.attr, + &sensor_dev_attr_lsi1064_local_temp.dev_attr.attr, + &sensor_dev_attr_front_panel_temp.dev_attr.attr, + &sensor_dev_attr_psu_temp.dev_attr.attr, + &sensor_dev_attr_fan_failure.dev_attr.attr, + &sensor_dev_attr_env_bus_busy.dev_attr.attr, + &sensor_dev_attr_env_data_stale.dev_attr.attr, + &sensor_dev_attr_tpm_self_test_passed.dev_attr.attr, + &sensor_dev_attr_firmware_version.dev_attr.attr, + &sensor_dev_attr_name.dev_attr.attr, + NULL, +}; + +static const struct attribute_group env_group = { + .attrs = env_attributes, +}; + +static int __devinit env_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct env *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + + if (!p) + goto out; + + spin_lock_init(&p->lock); + + p->regs = of_ioremap(&op->resource[0], 0, REG_SIZE, "pic16f747"); + if (!p->regs) + goto out_free; + + err = sysfs_create_group(&op->dev.kobj, &env_group); + if (err) + goto out_iounmap; + + p->hwmon_dev = hwmon_device_register(&op->dev); + if (IS_ERR(p->hwmon_dev)) { + err = PTR_ERR(p->hwmon_dev); + goto out_sysfs_remove_group; + } + + dev_set_drvdata(&op->dev, p); + err = 0; + +out: + return err; + +out_sysfs_remove_group: + sysfs_remove_group(&op->dev.kobj, &env_group); + +out_iounmap: + of_iounmap(&op->resource[0], p->regs, REG_SIZE); + +out_free: + kfree(p); + goto out; +} + +static int __devexit env_remove(struct of_device *op) +{ + struct env *p = dev_get_drvdata(&op->dev); + + if (p) { + sysfs_remove_group(&op->dev.kobj, &env_group); + hwmon_device_unregister(p->hwmon_dev); + of_iounmap(&op->resource[0], p->regs, REG_SIZE); + kfree(p); + } + + return 0; +} + +static const struct of_device_id env_match[] = { + { + .name = "env-monitor", + .compatible = "SUNW,ebus-pic16f747-env", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, env_match); + +static struct of_platform_driver env_driver = { + .name = "ultra45_env", + .match_table = env_match, + .probe = env_probe, + .remove = __devexit_p(env_remove), +}; + +static int __init env_init(void) +{ + return of_register_driver(&env_driver, &of_bus_type); +} + +static void __exit env_exit(void) +{ + of_unregister_driver(&env_driver); +} + +module_init(env_init); +module_exit(env_exit); diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index ed7c5f72cb8..5b8b533f290 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1683,7 +1683,7 @@ enum { SRP_OPT_SERVICE_ID), }; -static match_table_t srp_opt_tokens = { +static const match_table_t srp_opt_tokens = { { SRP_OPT_ID_EXT, "id_ext=%s" }, { SRP_OPT_IOC_GUID, "ioc_guid=%s" }, { SRP_OPT_DGID, "dgid=%s" }, diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c index d8765cc93d2..c4f42311fde 100644 --- a/drivers/input/misc/sparcspkr.c +++ b/drivers/input/misc/sparcspkr.c @@ -249,7 +249,7 @@ static int bbc_remove(struct of_device *op) return 0; } -static struct of_device_id bbc_beep_match[] = { +static const struct of_device_id bbc_beep_match[] = { { .name = "beep", .compatible = "SUNW,bbc-beep", @@ -328,7 +328,7 @@ static int grover_remove(struct of_device *op) return 0; } -static struct of_device_id grover_beep_match[] = { +static const struct of_device_id grover_beep_match[] = { { .name = "beep", .compatible = "SUNW,smbus-beep", diff --git a/drivers/input/serio/i8042-sparcio.h b/drivers/input/serio/i8042-sparcio.h index 692a79ec2a2..5071af2c060 100644 --- a/drivers/input/serio/i8042-sparcio.h +++ b/drivers/input/serio/i8042-sparcio.h @@ -87,7 +87,7 @@ static int __devexit sparc_i8042_remove(struct of_device *op) return 0; } -static struct of_device_id sparc_i8042_match[] = { +static const struct of_device_id sparc_i8042_match[] = { { .name = "8042", }, diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c index 9ce3b3baf3a..3ab6362f043 100644 --- a/drivers/input/xen-kbdfront.c +++ b/drivers/input/xen-kbdfront.c @@ -335,11 +335,11 @@ static struct xenbus_driver xenkbd = { static int __init xenkbd_init(void) { - if (!is_running_on_xen()) + if (!xen_domain()) return -ENODEV; /* Nothing to do if running in dom0. */ - if (is_initial_xendomain()) + if (xen_initial_domain()) return -ENODEV; return xenbus_register_frontend(&xenkbd); diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c index 871b0cbca5e..798d7f3e42e 100644 --- a/drivers/isdn/capi/capi.c +++ b/drivers/isdn/capi/capi.c @@ -1231,7 +1231,7 @@ static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file, int error = 0; switch (cmd) { default: - error = n_tty_ioctl (tty, file, cmd, arg); + error = n_tty_ioctl_helper(tty, file, cmd, arg); break; } return error; diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index 5e89fa17781..07052ed2a0c 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -571,6 +571,7 @@ gigaset_tty_close(struct tty_struct *tty) } /* prevent other callers from entering ldisc methods */ + /* FIXME: should use the tty state flags */ tty->disc_data = NULL; if (!cs->hw.ser) @@ -642,10 +643,11 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, return -ENXIO; switch (cmd) { - case TCGETS: - case TCGETA: - /* pass through to underlying serial device */ - rc = n_tty_ioctl(tty, file, cmd, arg); + + case FIONREAD: + /* unused, always return zero */ + val = 0; + rc = put_user(val, p); break; case TCFLSH: @@ -659,20 +661,13 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, flush_send_queue(cs); break; } - /* flush the serial port's buffer */ - rc = n_tty_ioctl(tty, file, cmd, arg); - break; - - case FIONREAD: - /* unused, always return zero */ - val = 0; - rc = put_user(val, p); - break; + /* Pass through */ default: - rc = -ENOIOCTLCMD; + /* pass through to underlying serial device */ + rc = n_tty_ioctl_helper(tty, file, cmd, arg); + break; } - cs_put(cs); return rc; } @@ -680,6 +675,8 @@ gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, /* * Poll on the tty. * Unused, always return zero. + * + * FIXME: should probably return an exception - especially on hangup */ static unsigned int gigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 5b14262af01..e3e40427e00 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -82,6 +82,14 @@ config LEDS_COBALT_RAQ help This option enables support for the Cobalt Raq series LEDs. +config LEDS_SUNFIRE + tristate "LED support for SunFire servers." + depends on LEDS_CLASS && SPARC64 + select LEDS_TRIGGERS + help + This option enables support for the Left, Middle, and Right + LEDs on the I/O and CPU boards of SunFire UltraSPARC servers. + config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" depends on LEDS_CLASS && SH_HP6XX diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 3a8e6a04363..eb186c351a1 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o obj-$(CONFIG_LEDS_H1940) += leds-h1940.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o +obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o obj-$(CONFIG_LEDS_PCA9532) += leds-pca9532.o obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o diff --git a/drivers/leds/leds-sunfire.c b/drivers/leds/leds-sunfire.c new file mode 100644 index 00000000000..6b008f0c3f6 --- /dev/null +++ b/drivers/leds/leds-sunfire.c @@ -0,0 +1,273 @@ +/* leds-sunfire.c: SUNW,Ultra-Enterprise LED driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/leds.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include <asm/fhc.h> +#include <asm/upa.h> + +#define DRIVER_NAME "leds-sunfire" +#define PFX DRIVER_NAME ": " + +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_DESCRIPTION("Sun Fire LED driver"); +MODULE_LICENSE("GPL"); + +struct sunfire_led { + struct led_classdev led_cdev; + void __iomem *reg; +}; +#define to_sunfire_led(d) container_of(d, struct sunfire_led, led_cdev) + +static void __clockboard_set(struct led_classdev *led_cdev, + enum led_brightness led_val, u8 bit) +{ + struct sunfire_led *p = to_sunfire_led(led_cdev); + u8 reg = upa_readb(p->reg); + + switch (bit) { + case CLOCK_CTRL_LLED: + if (led_val) + reg &= ~bit; + else + reg |= bit; + break; + + default: + if (led_val) + reg |= bit; + else + reg &= ~bit; + break; + } + upa_writeb(reg, p->reg); +} + +static void clockboard_left_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __clockboard_set(led_cdev, led_val, CLOCK_CTRL_LLED); +} + +static void clockboard_middle_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __clockboard_set(led_cdev, led_val, CLOCK_CTRL_MLED); +} + +static void clockboard_right_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __clockboard_set(led_cdev, led_val, CLOCK_CTRL_RLED); +} + +static void __fhc_set(struct led_classdev *led_cdev, + enum led_brightness led_val, u32 bit) +{ + struct sunfire_led *p = to_sunfire_led(led_cdev); + u32 reg = upa_readl(p->reg); + + switch (bit) { + case FHC_CONTROL_LLED: + if (led_val) + reg &= ~bit; + else + reg |= bit; + break; + + default: + if (led_val) + reg |= bit; + else + reg &= ~bit; + break; + } + upa_writel(reg, p->reg); +} + +static void fhc_left_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __fhc_set(led_cdev, led_val, FHC_CONTROL_LLED); +} + +static void fhc_middle_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __fhc_set(led_cdev, led_val, FHC_CONTROL_MLED); +} + +static void fhc_right_set(struct led_classdev *led_cdev, + enum led_brightness led_val) +{ + __fhc_set(led_cdev, led_val, FHC_CONTROL_RLED); +} + +typedef void (*set_handler)(struct led_classdev *, enum led_brightness); +struct led_type { + const char *name; + set_handler handler; + const char *default_trigger; +}; + +#define NUM_LEDS_PER_BOARD 3 +struct sunfire_drvdata { + struct sunfire_led leds[NUM_LEDS_PER_BOARD]; +}; + +static int __devinit sunfire_led_generic_probe(struct platform_device *pdev, + struct led_type *types) +{ + struct sunfire_drvdata *p; + int i, err = -EINVAL; + + if (pdev->num_resources != 1) { + printk(KERN_ERR PFX "Wrong number of resources %d, should be 1\n", + pdev->num_resources); + goto out; + } + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR PFX "Could not allocate struct sunfire_drvdata\n"); + goto out; + } + + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) { + struct led_classdev *lp = &p->leds[i].led_cdev; + + p->leds[i].reg = (void __iomem *) pdev->resource[0].start; + lp->name = types[i].name; + lp->brightness = LED_FULL; + lp->brightness_set = types[i].handler; + lp->default_trigger = types[i].default_trigger; + + err = led_classdev_register(&pdev->dev, lp); + if (err) { + printk(KERN_ERR PFX "Could not register %s LED\n", + lp->name); + goto out_unregister_led_cdevs; + } + } + + dev_set_drvdata(&pdev->dev, p); + + err = 0; +out: + return err; + +out_unregister_led_cdevs: + for (i--; i >= 0; i--) + led_classdev_unregister(&p->leds[i].led_cdev); + goto out; +} + +static int __devexit sunfire_led_generic_remove(struct platform_device *pdev) +{ + struct sunfire_drvdata *p = dev_get_drvdata(&pdev->dev); + int i; + + for (i = 0; i < NUM_LEDS_PER_BOARD; i++) + led_classdev_unregister(&p->leds[i].led_cdev); + + kfree(p); + + return 0; +} + +static struct led_type clockboard_led_types[NUM_LEDS_PER_BOARD] = { + { + .name = "clockboard-left", + .handler = clockboard_left_set, + }, + { + .name = "clockboard-middle", + .handler = clockboard_middle_set, + }, + { + .name = "clockboard-right", + .handler = clockboard_right_set, + .default_trigger= "heartbeat", + }, +}; + +static int __devinit sunfire_clockboard_led_probe(struct platform_device *pdev) +{ + return sunfire_led_generic_probe(pdev, clockboard_led_types); +} + +static struct led_type fhc_led_types[NUM_LEDS_PER_BOARD] = { + { + .name = "fhc-left", + .handler = fhc_left_set, + }, + { + .name = "fhc-middle", + .handler = fhc_middle_set, + }, + { + .name = "fhc-right", + .handler = fhc_right_set, + .default_trigger= "heartbeat", + }, +}; + +static int __devinit sunfire_fhc_led_probe(struct platform_device *pdev) +{ + return sunfire_led_generic_probe(pdev, fhc_led_types); +} + +MODULE_ALIAS("platform:sunfire-clockboard-leds"); +MODULE_ALIAS("platform:sunfire-fhc-leds"); + +static struct platform_driver sunfire_clockboard_led_driver = { + .probe = sunfire_clockboard_led_probe, + .remove = __devexit_p(sunfire_led_generic_remove), + .driver = { + .name = "sunfire-clockboard-leds", + .owner = THIS_MODULE, + }, +}; + +static struct platform_driver sunfire_fhc_led_driver = { + .probe = sunfire_fhc_led_probe, + .remove = __devexit_p(sunfire_led_generic_remove), + .driver = { + .name = "sunfire-fhc-leds", + .owner = THIS_MODULE, + }, +}; + +static int __init sunfire_leds_init(void) +{ + int err = platform_driver_register(&sunfire_clockboard_led_driver); + + if (err) { + printk(KERN_ERR PFX "Could not register clock board LED driver\n"); + return err; + } + + err = platform_driver_register(&sunfire_fhc_led_driver); + if (err) { + printk(KERN_ERR PFX "Could not register FHC LED driver\n"); + platform_driver_unregister(&sunfire_clockboard_led_driver); + } + + return err; +} + +static void __exit sunfire_leds_exit(void) +{ + platform_driver_unregister(&sunfire_clockboard_led_driver); + platform_driver_unregister(&sunfire_fhc_led_driver); +} + +module_init(sunfire_leds_init); +module_exit(sunfire_leds_exit); diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index e9994c81df6..fc9497bdd32 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -2096,15 +2096,8 @@ static int cafe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int ret; - u16 classword; struct cafe_camera *cam; - /* - * Make sure we have a camera here - we'll get calls for - * the other cafe devices as well. - */ - pci_read_config_word(pdev, PCI_CLASS_DEVICE, &classword); - if (classword != PCI_CLASS_MULTIMEDIA_VIDEO) - return -ENODEV; + /* * Start putting together one of our big camera structures. */ @@ -2292,8 +2285,8 @@ static int cafe_pci_resume(struct pci_dev *pdev) static struct pci_device_id cafe_ids[] = { - { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ - { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, + PCI_DEVICE_ID_MARVELL_88ALP01_CCIC) }, { 0, } }; diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c0b41e8bcd9..f2eeb38efa6 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -3,13 +3,14 @@ # menuconfig MMC - tristate "MMC/SD card support" + tristate "MMC/SD/SDIO card support" depends on HAS_IOMEM help - MMC is the "multi-media card" bus protocol. + This selects MultiMediaCard, Secure Digital and Secure + Digital I/O support. - If you want MMC support, you should say Y here and also - to the specific driver for your MMC interface. + If you want MMC/SD/SDIO support, you should say Y here and + also to your specific host controller driver. config MMC_DEBUG bool "MMC debugging" diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig index dd0f398ee2f..3f2a912659a 100644 --- a/drivers/mmc/card/Kconfig +++ b/drivers/mmc/card/Kconfig @@ -2,7 +2,7 @@ # MMC/SD card drivers # -comment "MMC/SD Card Drivers" +comment "MMC/SD/SDIO Card Drivers" config MMC_BLOCK tristate "MMC block device driver" @@ -34,7 +34,6 @@ config MMC_BLOCK_BOUNCE config SDIO_UART tristate "SDIO UART/GPS class support" - depends on MMC help SDIO function driver for SDIO cards that implements the UART class, as well as the GPS class which appears like a UART. diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index efacee0404a..24c97d3d16b 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -58,7 +58,6 @@ struct mmc_blk_data { struct mmc_queue queue; unsigned int usage; - unsigned int block_bits; unsigned int read_only; }; @@ -216,8 +215,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; struct mmc_blk_request brq; - int ret = 1, data_size, i; - struct scatterlist *sg; + int ret = 1; mmc_claim_host(card->host); @@ -233,13 +231,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) if (!mmc_card_blockaddr(card)) brq.cmd.arg <<= 9; brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; - brq.data.blksz = 1 << md->block_bits; + brq.data.blksz = 512; brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.arg = 0; brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC; - brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); - if (brq.data.blocks > card->host->max_blk_count) - brq.data.blocks = card->host->max_blk_count; + brq.data.blocks = req->nr_sectors; if (brq.data.blocks > 1) { /* SPI multiblock writes terminate using a special @@ -271,24 +267,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_queue_bounce_pre(mq); - /* - * Adjust the sg list so it is the same size as the - * request. - */ - if (brq.data.blocks != - (req->nr_sectors >> (md->block_bits - 9))) { - data_size = brq.data.blocks * brq.data.blksz; - for_each_sg(brq.data.sg, sg, brq.data.sg_len, i) { - data_size -= sg->length; - if (data_size <= 0) { - sg->length += data_size; - i++; - break; - } - } - brq.data.sg_len = i; - } - mmc_wait_for_req(card->host, &brq.mrq); mmc_queue_bounce_post(mq); @@ -373,16 +351,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) if (rq_data_dir(req) != READ) { if (mmc_card_sd(card)) { u32 blocks; - unsigned int bytes; blocks = mmc_sd_num_wr_blocks(card); if (blocks != (u32)-1) { - if (card->csd.write_partial) - bytes = blocks << md->block_bits; - else - bytes = blocks << 9; spin_lock_irq(&md->lock); - ret = __blk_end_request(req, 0, bytes); + ret = __blk_end_request(req, 0, blocks << 9); spin_unlock_irq(&md->lock); } } else { @@ -432,13 +405,6 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) */ md->read_only = mmc_blk_readonly(card); - /* - * Both SD and MMC specifications state (although a bit - * unclearly in the MMC case) that a block size of 512 - * bytes must always be supported by the card. - */ - md->block_bits = 9; - md->disk = alloc_disk(1 << MMC_SHIFT); if (md->disk == NULL) { ret = -ENOMEM; @@ -476,7 +442,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) sprintf(md->disk->disk_name, "mmcblk%d", devidx); - blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); + blk_queue_hardsect_size(md->queue.queue, 512); if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { /* @@ -514,7 +480,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) mmc_claim_host(card->host); cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = 1 << md->block_bits; + cmd.arg = 512; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, 5); mmc_release_host(card->host); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 3dee97e7d16..406989e992b 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -31,7 +31,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) /* * We only like normal block requests. */ - if (!blk_fs_request(req) && !blk_pc_request(req)) { + if (!blk_fs_request(req)) { blk_dump_rq_flags(req, "MMC bad request"); return BLKPREP_KILL; } @@ -131,6 +131,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock mq->req = NULL; blk_queue_prep_rq(mq->queue, mmc_prep_request); + blk_queue_ordered(mq->queue, QUEUE_ORDERED_DRAIN, NULL); #ifdef CONFIG_MMC_BLOCK_BOUNCE if (host->max_hw_segs == 1) { @@ -142,12 +143,19 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock bouncesz = host->max_req_size; if (bouncesz > host->max_seg_size) bouncesz = host->max_seg_size; + if (bouncesz > (host->max_blk_count * 512)) + bouncesz = host->max_blk_count * 512; + + if (bouncesz > 512) { + mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); + if (!mq->bounce_buf) { + printk(KERN_WARNING "%s: unable to " + "allocate bounce buffer\n", + mmc_card_name(card)); + } + } - mq->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); - if (!mq->bounce_buf) { - printk(KERN_WARNING "%s: unable to allocate " - "bounce buffer\n", mmc_card_name(card)); - } else { + if (mq->bounce_buf) { blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY); blk_queue_max_sectors(mq->queue, bouncesz / 512); blk_queue_max_phys_segments(mq->queue, bouncesz / 512); @@ -175,7 +183,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock if (!mq->bounce_buf) { blk_queue_bounce_limit(mq->queue, limit); - blk_queue_max_sectors(mq->queue, host->max_req_size / 512); + blk_queue_max_sectors(mq->queue, + min(host->max_blk_count, host->max_req_size / 512)); blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); blk_queue_max_segment_size(mq->queue, host->max_seg_size); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 64b05c6270f..9c50e6f1c23 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -248,8 +248,12 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, sg_init_one(&sg, data_buf, len); - if (card) - mmc_set_data_timeout(&data, card); + /* + * The spec states that CSR and CID accesses have a timeout + * of 64 clock cycles. + */ + data.timeout_ns = 0; + data.timeout_clks = 64; mmc_wait_for_req(host, &mrq); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 4eab79e09cc..fb99ccff908 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -165,6 +165,36 @@ static int sdio_enable_wide(struct mmc_card *card) } /* + * Test if the card supports high-speed mode and, if so, switch to it. + */ +static int sdio_enable_hs(struct mmc_card *card) +{ + int ret; + u8 speed; + + if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) + return 0; + + if (!card->cccr.high_speed) + return 0; + + ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed); + if (ret) + return ret; + + speed |= SDIO_SPEED_EHS; + + ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL); + if (ret) + return ret; + + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); + + return 0; +} + +/* * Host is being removed. Free up the current card. */ static void mmc_sdio_remove(struct mmc_host *host) @@ -333,10 +363,26 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr) goto remove; /* - * No support for high-speed yet, so just set - * the card's maximum speed. + * Switch to high-speed (if supported). */ - mmc_set_clock(host, card->cis.max_dtr); + err = sdio_enable_hs(card); + if (err) + goto remove; + + /* + * Change to the card's maximum speed. + */ + if (mmc_card_highspeed(card)) { + /* + * The SDIO specification doesn't mention how + * the CIS transfer speed register relates to + * high-speed, but it seems that 50 MHz is + * mandatory. + */ + mmc_set_clock(host, 50000000); + } else { + mmc_set_clock(host, card->cis.max_dtr); + } /* * Switch to wider bus (if supported). diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index c292e124107..bb192f90e8e 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -5,6 +5,8 @@ * Created: June 18, 2007 * Copyright: MontaVista Software Inc. * + * Copyright 2008 Pierre Ossman + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at @@ -107,11 +109,14 @@ static int sdio_irq_thread(void *_host) /* * Give other threads a chance to run in the presence of - * errors. FIXME: determine if due to card removal and - * possibly exit this thread if so. + * errors. */ - if (ret < 0) - ssleep(1); + if (ret < 0) { + set_current_state(TASK_INTERRUPTIBLE); + if (!kthread_should_stop()) + schedule_timeout(HZ); + set_current_state(TASK_RUNNING); + } /* * Adaptive polling frequency based on the assumption @@ -154,7 +159,8 @@ static int sdio_card_irq_get(struct mmc_card *card) if (!host->sdio_irqs++) { atomic_set(&host->sdio_irq_thread_abort, 0); host->sdio_irq_thread = - kthread_run(sdio_irq_thread, host, "ksdiorqd"); + kthread_run(sdio_irq_thread, host, "ksdioirqd/%s", + mmc_hostname(host)); if (IS_ERR(host->sdio_irq_thread)) { int err = PTR_ERR(host->sdio_irq_thread); host->sdio_irqs--; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ea8d7a3490d..dfa585f7fea 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -2,7 +2,7 @@ # MMC/SD host controller drivers # -comment "MMC/SD Host Controller Drivers" +comment "MMC/SD/SDIO Host Controller Drivers" config MMC_ARMMMCI tristate "ARM AMBA Multimedia Card Interface support" @@ -114,6 +114,17 @@ config MMC_ATMELMCI If unsure, say N. +config MMC_ATMELMCI_DMA + bool "Atmel MCI DMA support (EXPERIMENTAL)" + depends on MMC_ATMELMCI && DMA_ENGINE && EXPERIMENTAL + help + Say Y here to have the Atmel MCI driver use a DMA engine to + do data transfers and thus increase the throughput and + reduce the CPU utilization. Note that this is highly + experimental and may cause the driver to lock up. + + If unsure, say N. + config MMC_IMX tristate "Motorola i.MX Multimedia Card Interface support" depends on ARCH_IMX @@ -141,21 +152,22 @@ config MMC_TIFM_SD module will be called tifm_sd. config MMC_SPI - tristate "MMC/SD over SPI" - depends on MMC && SPI_MASTER && !HIGHMEM && HAS_DMA + tristate "MMC/SD/SDIO over SPI" + depends on SPI_MASTER && !HIGHMEM && HAS_DMA select CRC7 select CRC_ITU_T help - Some systems accss MMC/SD cards using a SPI controller instead of - using a "native" MMC/SD controller. This has a disadvantage of - being relatively high overhead, but a compensating advantage of - working on many systems without dedicated MMC/SD controllers. + Some systems accss MMC/SD/SDIO cards using a SPI controller + instead of using a "native" MMC/SD/SDIO controller. This has a + disadvantage of being relatively high overhead, but a compensating + advantage of working on many systems without dedicated MMC/SD/SDIO + controllers. If unsure, or if your system has no SPI master driver, say N. config MMC_S3C tristate "Samsung S3C SD/MMC Card Interface support" - depends on ARCH_S3C2410 && MMC + depends on ARCH_S3C2410 help This selects a driver for the MCI interface found in Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. @@ -166,7 +178,7 @@ config MMC_S3C config MMC_SDRICOH_CS tristate "MMC/SD driver for Ricoh Bay1Controllers (EXPERIMENTAL)" - depends on EXPERIMENTAL && MMC && PCI && PCMCIA + depends on EXPERIMENTAL && PCI && PCMCIA help Say Y here if your Notebook reports a Ricoh Bay1Controller PCMCIA card whenever you insert a MMC or SD card into the card slot. diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h index 26bd80e6503..b58364ed6bb 100644 --- a/drivers/mmc/host/atmel-mci-regs.h +++ b/drivers/mmc/host/atmel-mci-regs.h @@ -25,8 +25,10 @@ #define MCI_SDCR 0x000c /* SD Card / SDIO */ # define MCI_SDCSEL_SLOT_A ( 0 << 0) /* Select SD slot A */ # define MCI_SDCSEL_SLOT_B ( 1 << 0) /* Select SD slot A */ -# define MCI_SDCBUS_1BIT ( 0 << 7) /* 1-bit data bus */ -# define MCI_SDCBUS_4BIT ( 1 << 7) /* 4-bit data bus */ +# define MCI_SDCSEL_MASK ( 3 << 0) +# define MCI_SDCBUS_1BIT ( 0 << 6) /* 1-bit data bus */ +# define MCI_SDCBUS_4BIT ( 2 << 6) /* 4-bit data bus */ +# define MCI_SDCBUS_MASK ( 3 << 6) #define MCI_ARGR 0x0010 /* Command Argument */ #define MCI_CMDR 0x0014 /* Command */ # define MCI_CMDR_CMDNB(x) ((x) << 0) /* Command Opcode */ diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 00008967ef7..7a3f2436b01 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -11,6 +11,8 @@ #include <linux/clk.h> #include <linux/debugfs.h> #include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/gpio.h> #include <linux/init.h> @@ -33,64 +35,178 @@ #include "atmel-mci-regs.h" #define ATMCI_DATA_ERROR_FLAGS (MCI_DCRCE | MCI_DTOE | MCI_OVRE | MCI_UNRE) +#define ATMCI_DMA_THRESHOLD 16 enum { EVENT_CMD_COMPLETE = 0, - EVENT_DATA_ERROR, - EVENT_DATA_COMPLETE, - EVENT_STOP_SENT, - EVENT_STOP_COMPLETE, EVENT_XFER_COMPLETE, + EVENT_DATA_COMPLETE, + EVENT_DATA_ERROR, +}; + +enum atmel_mci_state { + STATE_IDLE = 0, + STATE_SENDING_CMD, + STATE_SENDING_DATA, + STATE_DATA_BUSY, + STATE_SENDING_STOP, + STATE_DATA_ERROR, +}; + +struct atmel_mci_dma { +#ifdef CONFIG_MMC_ATMELMCI_DMA + struct dma_client client; + struct dma_chan *chan; + struct dma_async_tx_descriptor *data_desc; +#endif }; +/** + * struct atmel_mci - MMC controller state shared between all slots + * @lock: Spinlock protecting the queue and associated data. + * @regs: Pointer to MMIO registers. + * @sg: Scatterlist entry currently being processed by PIO code, if any. + * @pio_offset: Offset into the current scatterlist entry. + * @cur_slot: The slot which is currently using the controller. + * @mrq: The request currently being processed on @cur_slot, + * or NULL if the controller is idle. + * @cmd: The command currently being sent to the card, or NULL. + * @data: The data currently being transferred, or NULL if no data + * transfer is in progress. + * @dma: DMA client state. + * @data_chan: DMA channel being used for the current data transfer. + * @cmd_status: Snapshot of SR taken upon completion of the current + * command. Only valid when EVENT_CMD_COMPLETE is pending. + * @data_status: Snapshot of SR taken upon completion of the current + * data transfer. Only valid when EVENT_DATA_COMPLETE or + * EVENT_DATA_ERROR is pending. + * @stop_cmdr: Value to be loaded into CMDR when the stop command is + * to be sent. + * @tasklet: Tasklet running the request state machine. + * @pending_events: Bitmask of events flagged by the interrupt handler + * to be processed by the tasklet. + * @completed_events: Bitmask of events which the state machine has + * processed. + * @state: Tasklet state. + * @queue: List of slots waiting for access to the controller. + * @need_clock_update: Update the clock rate before the next request. + * @need_reset: Reset controller before next request. + * @mode_reg: Value of the MR register. + * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus + * rate and timeout calculations. + * @mapbase: Physical address of the MMIO registers. + * @mck: The peripheral bus clock hooked up to the MMC controller. + * @pdev: Platform device associated with the MMC controller. + * @slot: Slots sharing this MMC controller. + * + * Locking + * ======= + * + * @lock is a softirq-safe spinlock protecting @queue as well as + * @cur_slot, @mrq and @state. These must always be updated + * at the same time while holding @lock. + * + * @lock also protects mode_reg and need_clock_update since these are + * used to synchronize mode register updates with the queue + * processing. + * + * The @mrq field of struct atmel_mci_slot is also protected by @lock, + * and must always be written at the same time as the slot is added to + * @queue. + * + * @pending_events and @completed_events are accessed using atomic bit + * operations, so they don't need any locking. + * + * None of the fields touched by the interrupt handler need any + * locking. However, ordering is important: Before EVENT_DATA_ERROR or + * EVENT_DATA_COMPLETE is set in @pending_events, all data-related + * interrupts must be disabled and @data_status updated with a + * snapshot of SR. Similarly, before EVENT_CMD_COMPLETE is set, the + * CMDRDY interupt must be disabled and @cmd_status updated with a + * snapshot of SR, and before EVENT_XFER_COMPLETE can be set, the + * bytes_xfered field of @data must be written. This is ensured by + * using barriers. + */ struct atmel_mci { - struct mmc_host *mmc; + spinlock_t lock; void __iomem *regs; struct scatterlist *sg; unsigned int pio_offset; + struct atmel_mci_slot *cur_slot; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; + struct atmel_mci_dma dma; + struct dma_chan *data_chan; + u32 cmd_status; u32 data_status; - u32 stop_status; u32 stop_cmdr; - u32 mode_reg; - u32 sdc_reg; - struct tasklet_struct tasklet; unsigned long pending_events; unsigned long completed_events; + enum atmel_mci_state state; + struct list_head queue; - int present; - int detect_pin; - int wp_pin; - - /* For detect pin debouncing */ - struct timer_list detect_timer; - + bool need_clock_update; + bool need_reset; + u32 mode_reg; unsigned long bus_hz; unsigned long mapbase; struct clk *mck; struct platform_device *pdev; + + struct atmel_mci_slot *slot[ATMEL_MCI_MAX_NR_SLOTS]; +}; + +/** + * struct atmel_mci_slot - MMC slot state + * @mmc: The mmc_host representing this slot. + * @host: The MMC controller this slot is using. + * @sdc_reg: Value of SDCR to be written before using this slot. + * @mrq: mmc_request currently being processed or waiting to be + * processed, or NULL when the slot is idle. + * @queue_node: List node for placing this node in the @queue list of + * &struct atmel_mci. + * @clock: Clock rate configured by set_ios(). Protected by host->lock. + * @flags: Random state bits associated with the slot. + * @detect_pin: GPIO pin used for card detection, or negative if not + * available. + * @wp_pin: GPIO pin used for card write protect sending, or negative + * if not available. + * @detect_timer: Timer used for debouncing @detect_pin interrupts. + */ +struct atmel_mci_slot { + struct mmc_host *mmc; + struct atmel_mci *host; + + u32 sdc_reg; + + struct mmc_request *mrq; + struct list_head queue_node; + + unsigned int clock; + unsigned long flags; +#define ATMCI_CARD_PRESENT 0 +#define ATMCI_CARD_NEED_INIT 1 +#define ATMCI_SHUTDOWN 2 + + int detect_pin; + int wp_pin; + + struct timer_list detect_timer; }; -#define atmci_is_completed(host, event) \ - test_bit(event, &host->completed_events) #define atmci_test_and_clear_pending(host, event) \ test_and_clear_bit(event, &host->pending_events) -#define atmci_test_and_set_completed(host, event) \ - test_and_set_bit(event, &host->completed_events) #define atmci_set_completed(host, event) \ set_bit(event, &host->completed_events) #define atmci_set_pending(host, event) \ set_bit(event, &host->pending_events) -#define atmci_clear_pending(host, event) \ - clear_bit(event, &host->pending_events) /* * The debugfs stuff below is mostly optimized away when @@ -98,14 +214,15 @@ struct atmel_mci { */ static int atmci_req_show(struct seq_file *s, void *v) { - struct atmel_mci *host = s->private; - struct mmc_request *mrq = host->mrq; + struct atmel_mci_slot *slot = s->private; + struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_command *stop; struct mmc_data *data; /* Make sure we get a consistent snapshot */ - spin_lock_irq(&host->mmc->lock); + spin_lock_bh(&slot->host->lock); + mrq = slot->mrq; if (mrq) { cmd = mrq->cmd; @@ -130,7 +247,7 @@ static int atmci_req_show(struct seq_file *s, void *v) stop->resp[2], stop->error); } - spin_unlock_irq(&host->mmc->lock); + spin_unlock_bh(&slot->host->lock); return 0; } @@ -193,12 +310,16 @@ static int atmci_regs_show(struct seq_file *s, void *v) if (!buf) return -ENOMEM; - /* Grab a more or less consistent snapshot */ - spin_lock_irq(&host->mmc->lock); + /* + * Grab a more or less consistent snapshot. Note that we're + * not disabling interrupts, so IMR and SR may not be + * consistent. + */ + spin_lock_bh(&host->lock); clk_enable(host->mck); memcpy_fromio(buf, host->regs, MCI_REGS_SIZE); clk_disable(host->mck); - spin_unlock_irq(&host->mmc->lock); + spin_unlock_bh(&host->lock); seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n", buf[MCI_MR / 4], @@ -236,13 +357,13 @@ static const struct file_operations atmci_regs_fops = { .release = single_release, }; -static void atmci_init_debugfs(struct atmel_mci *host) +static void atmci_init_debugfs(struct atmel_mci_slot *slot) { - struct mmc_host *mmc; - struct dentry *root; - struct dentry *node; + struct mmc_host *mmc = slot->mmc; + struct atmel_mci *host = slot->host; + struct dentry *root; + struct dentry *node; - mmc = host->mmc; root = mmc->debugfs_root; if (!root) return; @@ -254,7 +375,11 @@ static void atmci_init_debugfs(struct atmel_mci *host) if (!node) goto err; - node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops); + node = debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops); + if (!node) + goto err; + + node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state); if (!node) goto err; @@ -271,25 +396,7 @@ static void atmci_init_debugfs(struct atmel_mci *host) return; err: - dev_err(&host->pdev->dev, - "failed to initialize debugfs for controller\n"); -} - -static void atmci_enable(struct atmel_mci *host) -{ - clk_enable(host->mck); - mci_writel(host, CR, MCI_CR_MCIEN); - mci_writel(host, MR, host->mode_reg); - mci_writel(host, SDCR, host->sdc_reg); -} - -static void atmci_disable(struct atmel_mci *host) -{ - mci_writel(host, CR, MCI_CR_SWRST); - - /* Stall until write is complete, then disable the bus clock */ - mci_readl(host, SR); - clk_disable(host->mck); + dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n"); } static inline unsigned int ns_to_clocks(struct atmel_mci *host, @@ -299,7 +406,7 @@ static inline unsigned int ns_to_clocks(struct atmel_mci *host, } static void atmci_set_timeout(struct atmel_mci *host, - struct mmc_data *data) + struct atmel_mci_slot *slot, struct mmc_data *data) { static unsigned dtomul_to_shift[] = { 0, 4, 7, 8, 10, 12, 16, 20 @@ -322,7 +429,7 @@ static void atmci_set_timeout(struct atmel_mci *host, dtocyc = 15; } - dev_vdbg(&host->mmc->class_dev, "setting timeout to %u cycles\n", + dev_vdbg(&slot->mmc->class_dev, "setting timeout to %u cycles\n", dtocyc << dtomul_to_shift[dtomul]); mci_writel(host, DTOR, (MCI_DTOMUL(dtomul) | MCI_DTOCYC(dtocyc))); } @@ -375,15 +482,12 @@ static u32 atmci_prepare_command(struct mmc_host *mmc, } static void atmci_start_command(struct atmel_mci *host, - struct mmc_command *cmd, - u32 cmd_flags) + struct mmc_command *cmd, u32 cmd_flags) { - /* Must read host->cmd after testing event flags */ - smp_rmb(); WARN_ON(host->cmd); host->cmd = cmd; - dev_vdbg(&host->mmc->class_dev, + dev_vdbg(&host->pdev->dev, "start command: ARGR=0x%08x CMDR=0x%08x\n", cmd->arg, cmd_flags); @@ -391,34 +495,157 @@ static void atmci_start_command(struct atmel_mci *host, mci_writel(host, CMDR, cmd_flags); } -static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data) +static void send_stop_cmd(struct atmel_mci *host, struct mmc_data *data) { - struct atmel_mci *host = mmc_priv(mmc); - atmci_start_command(host, data->stop, host->stop_cmdr); mci_writel(host, IER, MCI_CMDRDY); } -static void atmci_request_end(struct mmc_host *mmc, struct mmc_request *mrq) +#ifdef CONFIG_MMC_ATMELMCI_DMA +static void atmci_dma_cleanup(struct atmel_mci *host) { - struct atmel_mci *host = mmc_priv(mmc); + struct mmc_data *data = host->data; - WARN_ON(host->cmd || host->data); - host->mrq = NULL; + dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, + ((data->flags & MMC_DATA_WRITE) + ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); +} + +static void atmci_stop_dma(struct atmel_mci *host) +{ + struct dma_chan *chan = host->data_chan; + + if (chan) { + chan->device->device_terminate_all(chan); + atmci_dma_cleanup(host); + } else { + /* Data transfer was stopped by the interrupt handler */ + atmci_set_pending(host, EVENT_XFER_COMPLETE); + mci_writel(host, IER, MCI_NOTBUSY); + } +} + +/* This function is called by the DMA driver from tasklet context. */ +static void atmci_dma_complete(void *arg) +{ + struct atmel_mci *host = arg; + struct mmc_data *data = host->data; + + dev_vdbg(&host->pdev->dev, "DMA complete\n"); + + atmci_dma_cleanup(host); + + /* + * If the card was removed, data will be NULL. No point trying + * to send the stop command or waiting for NBUSY in this case. + */ + if (data) { + atmci_set_pending(host, EVENT_XFER_COMPLETE); + tasklet_schedule(&host->tasklet); + + /* + * Regardless of what the documentation says, we have + * to wait for NOTBUSY even after block read + * operations. + * + * When the DMA transfer is complete, the controller + * may still be reading the CRC from the card, i.e. + * the data transfer is still in progress and we + * haven't seen all the potential error bits yet. + * + * The interrupt handler will schedule a different + * tasklet to finish things up when the data transfer + * is completely done. + * + * We may not complete the mmc request here anyway + * because the mmc layer may call back and cause us to + * violate the "don't submit new operations from the + * completion callback" rule of the dma engine + * framework. + */ + mci_writel(host, IER, MCI_NOTBUSY); + } +} + +static int +atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) +{ + struct dma_chan *chan; + struct dma_async_tx_descriptor *desc; + struct scatterlist *sg; + unsigned int i; + enum dma_data_direction direction; + + /* + * We don't do DMA on "complex" transfers, i.e. with + * non-word-aligned buffers or lengths. Also, we don't bother + * with all the DMA setup overhead for short transfers. + */ + if (data->blocks * data->blksz < ATMCI_DMA_THRESHOLD) + return -EINVAL; + if (data->blksz & 3) + return -EINVAL; + + for_each_sg(data->sg, sg, data->sg_len, i) { + if (sg->offset & 3 || sg->length & 3) + return -EINVAL; + } + + /* If we don't have a channel, we can't do DMA */ + chan = host->dma.chan; + if (chan) { + dma_chan_get(chan); + host->data_chan = chan; + } + + if (!chan) + return -ENODEV; + + if (data->flags & MMC_DATA_READ) + direction = DMA_FROM_DEVICE; + else + direction = DMA_TO_DEVICE; + + desc = chan->device->device_prep_slave_sg(chan, + data->sg, data->sg_len, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + return -ENOMEM; - atmci_disable(host); + host->dma.data_desc = desc; + desc->callback = atmci_dma_complete; + desc->callback_param = host; + desc->tx_submit(desc); - mmc_request_done(mmc, mrq); + /* Go! */ + chan->device->device_issue_pending(chan); + + return 0; +} + +#else /* CONFIG_MMC_ATMELMCI_DMA */ + +static int atmci_submit_data_dma(struct atmel_mci *host, struct mmc_data *data) +{ + return -ENOSYS; } +static void atmci_stop_dma(struct atmel_mci *host) +{ + /* Data transfer was stopped by the interrupt handler */ + atmci_set_pending(host, EVENT_XFER_COMPLETE); + mci_writel(host, IER, MCI_NOTBUSY); +} + +#endif /* CONFIG_MMC_ATMELMCI_DMA */ + /* * Returns a mask of interrupt flags to be enabled after the whole * request has been prepared. */ -static u32 atmci_submit_data(struct mmc_host *mmc, struct mmc_data *data) +static u32 atmci_submit_data(struct atmel_mci *host, struct mmc_data *data) { - struct atmel_mci *host = mmc_priv(mmc); - u32 iflags; + u32 iflags; data->error = -EINPROGRESS; @@ -426,77 +653,89 @@ static u32 atmci_submit_data(struct mmc_host *mmc, struct mmc_data *data) host->sg = NULL; host->data = data; - dev_vdbg(&mmc->class_dev, "BLKR=0x%08x\n", - MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); - iflags = ATMCI_DATA_ERROR_FLAGS; - host->sg = data->sg; - host->pio_offset = 0; - if (data->flags & MMC_DATA_READ) - iflags |= MCI_RXRDY; - else - iflags |= MCI_TXRDY; + if (atmci_submit_data_dma(host, data)) { + host->data_chan = NULL; + + /* + * Errata: MMC data write operation with less than 12 + * bytes is impossible. + * + * Errata: MCI Transmit Data Register (TDR) FIFO + * corruption when length is not multiple of 4. + */ + if (data->blocks * data->blksz < 12 + || (data->blocks * data->blksz) & 3) + host->need_reset = true; + + host->sg = data->sg; + host->pio_offset = 0; + if (data->flags & MMC_DATA_READ) + iflags |= MCI_RXRDY; + else + iflags |= MCI_TXRDY; + } return iflags; } -static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +static void atmci_start_request(struct atmel_mci *host, + struct atmel_mci_slot *slot) { - struct atmel_mci *host = mmc_priv(mmc); - struct mmc_data *data; + struct mmc_request *mrq; struct mmc_command *cmd; + struct mmc_data *data; u32 iflags; - u32 cmdflags = 0; - - iflags = mci_readl(host, IMR); - if (iflags) - dev_warn(&mmc->class_dev, "WARNING: IMR=0x%08x\n", - mci_readl(host, IMR)); - - WARN_ON(host->mrq != NULL); - - /* - * We may "know" the card is gone even though there's still an - * electrical connection. If so, we really need to communicate - * this to the MMC core since there won't be any more - * interrupts as the card is completely removed. Otherwise, - * the MMC core might believe the card is still there even - * though the card was just removed very slowly. - */ - if (!host->present) { - mrq->cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, mrq); - return; - } + u32 cmdflags; + mrq = slot->mrq; + host->cur_slot = slot; host->mrq = mrq; + host->pending_events = 0; host->completed_events = 0; + host->data_status = 0; - atmci_enable(host); + if (host->need_reset) { + mci_writel(host, CR, MCI_CR_SWRST); + mci_writel(host, CR, MCI_CR_MCIEN); + mci_writel(host, MR, host->mode_reg); + host->need_reset = false; + } + mci_writel(host, SDCR, slot->sdc_reg); - /* We don't support multiple blocks of weird lengths. */ + iflags = mci_readl(host, IMR); + if (iflags) + dev_warn(&slot->mmc->class_dev, "WARNING: IMR=0x%08x\n", + iflags); + + if (unlikely(test_and_clear_bit(ATMCI_CARD_NEED_INIT, &slot->flags))) { + /* Send init sequence (74 clock cycles) */ + mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT); + while (!(mci_readl(host, SR) & MCI_CMDRDY)) + cpu_relax(); + } data = mrq->data; if (data) { - if (data->blocks > 1 && data->blksz & 3) - goto fail; - atmci_set_timeout(host, data); + atmci_set_timeout(host, slot, data); /* Must set block count/size before sending command */ mci_writel(host, BLKR, MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); + dev_vdbg(&slot->mmc->class_dev, "BLKR=0x%08x\n", + MCI_BCNT(data->blocks) | MCI_BLKLEN(data->blksz)); } iflags = MCI_CMDRDY; cmd = mrq->cmd; - cmdflags = atmci_prepare_command(mmc, cmd); + cmdflags = atmci_prepare_command(slot->mmc, cmd); atmci_start_command(host, cmd, cmdflags); if (data) - iflags |= atmci_submit_data(mmc, data); + iflags |= atmci_submit_data(host, data); if (mrq->stop) { - host->stop_cmdr = atmci_prepare_command(mmc, mrq->stop); + host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop); host->stop_cmdr |= MCI_CMDR_STOP_XFER; if (!(data->flags & MMC_DATA_WRITE)) host->stop_cmdr |= MCI_CMDR_TRDIR_READ; @@ -513,59 +752,156 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) * prepared yet.) */ mci_writel(host, IER, iflags); +} - return; +static void atmci_queue_request(struct atmel_mci *host, + struct atmel_mci_slot *slot, struct mmc_request *mrq) +{ + dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n", + host->state); + + spin_lock_bh(&host->lock); + slot->mrq = mrq; + if (host->state == STATE_IDLE) { + host->state = STATE_SENDING_CMD; + atmci_start_request(host, slot); + } else { + list_add_tail(&slot->queue_node, &host->queue); + } + spin_unlock_bh(&host->lock); +} -fail: - atmci_disable(host); - host->mrq = NULL; - mrq->cmd->error = -EINVAL; - mmc_request_done(mmc, mrq); +static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct atmel_mci_slot *slot = mmc_priv(mmc); + struct atmel_mci *host = slot->host; + struct mmc_data *data; + + WARN_ON(slot->mrq); + + /* + * We may "know" the card is gone even though there's still an + * electrical connection. If so, we really need to communicate + * this to the MMC core since there won't be any more + * interrupts as the card is completely removed. Otherwise, + * the MMC core might believe the card is still there even + * though the card was just removed very slowly. + */ + if (!test_bit(ATMCI_CARD_PRESENT, &slot->flags)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + } + + /* We don't support multiple blocks of weird lengths. */ + data = mrq->data; + if (data && data->blocks > 1 && data->blksz & 3) { + mrq->cmd->error = -EINVAL; + mmc_request_done(mmc, mrq); + } + + atmci_queue_request(host, slot, mrq); } static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { - struct atmel_mci *host = mmc_priv(mmc); + struct atmel_mci_slot *slot = mmc_priv(mmc); + struct atmel_mci *host = slot->host; + unsigned int i; + + slot->sdc_reg &= ~MCI_SDCBUS_MASK; + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + slot->sdc_reg |= MCI_SDCBUS_1BIT; + break; + case MMC_BUS_WIDTH_4: + slot->sdc_reg = MCI_SDCBUS_4BIT; + break; + } if (ios->clock) { + unsigned int clock_min = ~0U; u32 clkdiv; - /* Set clock rate */ - clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * ios->clock) - 1; + spin_lock_bh(&host->lock); + if (!host->mode_reg) { + clk_enable(host->mck); + mci_writel(host, CR, MCI_CR_SWRST); + mci_writel(host, CR, MCI_CR_MCIEN); + } + + /* + * Use mirror of ios->clock to prevent race with mmc + * core ios update when finding the minimum. + */ + slot->clock = ios->clock; + for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) { + if (host->slot[i] && host->slot[i]->clock + && host->slot[i]->clock < clock_min) + clock_min = host->slot[i]->clock; + } + + /* Calculate clock divider */ + clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1; if (clkdiv > 255) { dev_warn(&mmc->class_dev, "clock %u too slow; using %lu\n", - ios->clock, host->bus_hz / (2 * 256)); + clock_min, host->bus_hz / (2 * 256)); clkdiv = 255; } + /* + * WRPROOF and RDPROOF prevent overruns/underruns by + * stopping the clock when the FIFO is full/empty. + * This state is not expected to last for long. + */ host->mode_reg = MCI_MR_CLKDIV(clkdiv) | MCI_MR_WRPROOF | MCI_MR_RDPROOF; - } - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - host->sdc_reg = 0; - break; - case MMC_BUS_WIDTH_4: - host->sdc_reg = MCI_SDCBUS_4BIT; - break; + if (list_empty(&host->queue)) + mci_writel(host, MR, host->mode_reg); + else + host->need_clock_update = true; + + spin_unlock_bh(&host->lock); + } else { + bool any_slot_active = false; + + spin_lock_bh(&host->lock); + slot->clock = 0; + for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) { + if (host->slot[i] && host->slot[i]->clock) { + any_slot_active = true; + break; + } + } + if (!any_slot_active) { + mci_writel(host, CR, MCI_CR_MCIDIS); + if (host->mode_reg) { + mci_readl(host, MR); + clk_disable(host->mck); + } + host->mode_reg = 0; + } + spin_unlock_bh(&host->lock); } switch (ios->power_mode) { - case MMC_POWER_ON: - /* Send init sequence (74 clock cycles) */ - atmci_enable(host); - mci_writel(host, CMDR, MCI_CMDR_SPCMD_INIT); - while (!(mci_readl(host, SR) & MCI_CMDRDY)) - cpu_relax(); - atmci_disable(host); + case MMC_POWER_UP: + set_bit(ATMCI_CARD_NEED_INIT, &slot->flags); break; default: /* * TODO: None of the currently available AVR32-based * boards allow MMC power to be turned off. Implement * power control when this can be tested properly. + * + * We also need to hook this into the clock management + * somehow so that newly inserted cards aren't + * subjected to a fast clock before we have a chance + * to figure out what the maximum rate is. Currently, + * there's no way to avoid this, and there never will + * be for boards that don't support power control. */ break; } @@ -573,31 +909,82 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int atmci_get_ro(struct mmc_host *mmc) { - int read_only = 0; - struct atmel_mci *host = mmc_priv(mmc); + int read_only = -ENOSYS; + struct atmel_mci_slot *slot = mmc_priv(mmc); - if (gpio_is_valid(host->wp_pin)) { - read_only = gpio_get_value(host->wp_pin); + if (gpio_is_valid(slot->wp_pin)) { + read_only = gpio_get_value(slot->wp_pin); dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write"); - } else { - dev_dbg(&mmc->class_dev, - "no pin for checking read-only switch." - " Assuming write-enable.\n"); } return read_only; } -static struct mmc_host_ops atmci_ops = { +static int atmci_get_cd(struct mmc_host *mmc) +{ + int present = -ENOSYS; + struct atmel_mci_slot *slot = mmc_priv(mmc); + + if (gpio_is_valid(slot->detect_pin)) { + present = !gpio_get_value(slot->detect_pin); + dev_dbg(&mmc->class_dev, "card is %spresent\n", + present ? "" : "not "); + } + + return present; +} + +static const struct mmc_host_ops atmci_ops = { .request = atmci_request, .set_ios = atmci_set_ios, .get_ro = atmci_get_ro, + .get_cd = atmci_get_cd, }; +/* Called with host->lock held */ +static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) + __releases(&host->lock) + __acquires(&host->lock) +{ + struct atmel_mci_slot *slot = NULL; + struct mmc_host *prev_mmc = host->cur_slot->mmc; + + WARN_ON(host->cmd || host->data); + + /* + * Update the MMC clock rate if necessary. This may be + * necessary if set_ios() is called when a different slot is + * busy transfering data. + */ + if (host->need_clock_update) + mci_writel(host, MR, host->mode_reg); + + host->cur_slot->mrq = NULL; + host->mrq = NULL; + if (!list_empty(&host->queue)) { + slot = list_entry(host->queue.next, + struct atmel_mci_slot, queue_node); + list_del(&slot->queue_node); + dev_vdbg(&host->pdev->dev, "list not empty: %s is next\n", + mmc_hostname(slot->mmc)); + host->state = STATE_SENDING_CMD; + atmci_start_request(host, slot); + } else { + dev_vdbg(&host->pdev->dev, "list empty\n"); + host->state = STATE_IDLE; + } + + spin_unlock(&host->lock); + mmc_request_done(prev_mmc, mrq); + spin_lock(&host->lock); +} + static void atmci_command_complete(struct atmel_mci *host, - struct mmc_command *cmd, u32 status) + struct mmc_command *cmd) { + u32 status = host->cmd_status; + /* Read the response from the card (up to 16 bytes) */ cmd->resp[0] = mci_readl(host, RSPR); cmd->resp[1] = mci_readl(host, RSPR); @@ -614,11 +1001,12 @@ static void atmci_command_complete(struct atmel_mci *host, cmd->error = 0; if (cmd->error) { - dev_dbg(&host->mmc->class_dev, + dev_dbg(&host->pdev->dev, "command error: status=0x%08x\n", status); if (cmd->data) { host->data = NULL; + atmci_stop_dma(host); mci_writel(host, IDR, MCI_NOTBUSY | MCI_TXRDY | MCI_RXRDY | ATMCI_DATA_ERROR_FLAGS); @@ -628,146 +1016,222 @@ static void atmci_command_complete(struct atmel_mci *host, static void atmci_detect_change(unsigned long data) { - struct atmel_mci *host = (struct atmel_mci *)data; - struct mmc_request *mrq = host->mrq; - int present; + struct atmel_mci_slot *slot = (struct atmel_mci_slot *)data; + bool present; + bool present_old; /* - * atmci_remove() sets detect_pin to -1 before freeing the - * interrupt. We must not re-enable the interrupt if it has - * been freed. + * atmci_cleanup_slot() sets the ATMCI_SHUTDOWN flag before + * freeing the interrupt. We must not re-enable the interrupt + * if it has been freed, and if we're shutting down, it + * doesn't really matter whether the card is present or not. */ smp_rmb(); - if (!gpio_is_valid(host->detect_pin)) + if (test_bit(ATMCI_SHUTDOWN, &slot->flags)) return; - enable_irq(gpio_to_irq(host->detect_pin)); - present = !gpio_get_value(host->detect_pin); + enable_irq(gpio_to_irq(slot->detect_pin)); + present = !gpio_get_value(slot->detect_pin); + present_old = test_bit(ATMCI_CARD_PRESENT, &slot->flags); + + dev_vdbg(&slot->mmc->class_dev, "detect change: %d (was %d)\n", + present, present_old); - dev_vdbg(&host->pdev->dev, "detect change: %d (was %d)\n", - present, host->present); + if (present != present_old) { + struct atmel_mci *host = slot->host; + struct mmc_request *mrq; - if (present != host->present) { - dev_dbg(&host->mmc->class_dev, "card %s\n", + dev_dbg(&slot->mmc->class_dev, "card %s\n", present ? "inserted" : "removed"); - host->present = present; - /* Reset controller if card is gone */ - if (!present) { - mci_writel(host, CR, MCI_CR_SWRST); - mci_writel(host, IDR, ~0UL); - mci_writel(host, CR, MCI_CR_MCIEN); - } + spin_lock(&host->lock); + + if (!present) + clear_bit(ATMCI_CARD_PRESENT, &slot->flags); + else + set_bit(ATMCI_CARD_PRESENT, &slot->flags); /* Clean up queue if present */ + mrq = slot->mrq; if (mrq) { - /* - * Reset controller to terminate any ongoing - * commands or data transfers. - */ - mci_writel(host, CR, MCI_CR_SWRST); + if (mrq == host->mrq) { + /* + * Reset controller to terminate any ongoing + * commands or data transfers. + */ + mci_writel(host, CR, MCI_CR_SWRST); + mci_writel(host, CR, MCI_CR_MCIEN); + mci_writel(host, MR, host->mode_reg); - if (!atmci_is_completed(host, EVENT_CMD_COMPLETE)) - mrq->cmd->error = -ENOMEDIUM; - - if (mrq->data && !atmci_is_completed(host, - EVENT_DATA_COMPLETE)) { host->data = NULL; - mrq->data->error = -ENOMEDIUM; + host->cmd = NULL; + + switch (host->state) { + case STATE_IDLE: + break; + case STATE_SENDING_CMD: + mrq->cmd->error = -ENOMEDIUM; + if (!mrq->data) + break; + /* fall through */ + case STATE_SENDING_DATA: + mrq->data->error = -ENOMEDIUM; + atmci_stop_dma(host); + break; + case STATE_DATA_BUSY: + case STATE_DATA_ERROR: + if (mrq->data->error == -EINPROGRESS) + mrq->data->error = -ENOMEDIUM; + if (!mrq->stop) + break; + /* fall through */ + case STATE_SENDING_STOP: + mrq->stop->error = -ENOMEDIUM; + break; + } + + atmci_request_end(host, mrq); + } else { + list_del(&slot->queue_node); + mrq->cmd->error = -ENOMEDIUM; + if (mrq->data) + mrq->data->error = -ENOMEDIUM; + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; + + spin_unlock(&host->lock); + mmc_request_done(slot->mmc, mrq); + spin_lock(&host->lock); } - if (mrq->stop && !atmci_is_completed(host, - EVENT_STOP_COMPLETE)) - mrq->stop->error = -ENOMEDIUM; - - host->cmd = NULL; - atmci_request_end(host->mmc, mrq); } + spin_unlock(&host->lock); - mmc_detect_change(host->mmc, 0); + mmc_detect_change(slot->mmc, 0); } } static void atmci_tasklet_func(unsigned long priv) { - struct mmc_host *mmc = (struct mmc_host *)priv; - struct atmel_mci *host = mmc_priv(mmc); + struct atmel_mci *host = (struct atmel_mci *)priv; struct mmc_request *mrq = host->mrq; struct mmc_data *data = host->data; + struct mmc_command *cmd = host->cmd; + enum atmel_mci_state state = host->state; + enum atmel_mci_state prev_state; + u32 status; + + spin_lock(&host->lock); - dev_vdbg(&mmc->class_dev, - "tasklet: pending/completed/mask %lx/%lx/%x\n", - host->pending_events, host->completed_events, + state = host->state; + + dev_vdbg(&host->pdev->dev, + "tasklet: state %u pending/completed/mask %lx/%lx/%x\n", + state, host->pending_events, host->completed_events, mci_readl(host, IMR)); - if (atmci_test_and_clear_pending(host, EVENT_CMD_COMPLETE)) { - /* - * host->cmd must be set to NULL before the interrupt - * handler sees EVENT_CMD_COMPLETE - */ - host->cmd = NULL; - smp_wmb(); - atmci_set_completed(host, EVENT_CMD_COMPLETE); - atmci_command_complete(host, mrq->cmd, host->cmd_status); - - if (!mrq->cmd->error && mrq->stop - && atmci_is_completed(host, EVENT_XFER_COMPLETE) - && !atmci_test_and_set_completed(host, - EVENT_STOP_SENT)) - send_stop_cmd(host->mmc, mrq->data); - } - if (atmci_test_and_clear_pending(host, EVENT_STOP_COMPLETE)) { - /* - * host->cmd must be set to NULL before the interrupt - * handler sees EVENT_STOP_COMPLETE - */ - host->cmd = NULL; - smp_wmb(); - atmci_set_completed(host, EVENT_STOP_COMPLETE); - atmci_command_complete(host, mrq->stop, host->stop_status); - } - if (atmci_test_and_clear_pending(host, EVENT_DATA_ERROR)) { - u32 status = host->data_status; + do { + prev_state = state; - dev_vdbg(&mmc->class_dev, "data error: status=%08x\n", status); + switch (state) { + case STATE_IDLE: + break; - atmci_set_completed(host, EVENT_DATA_ERROR); - atmci_set_completed(host, EVENT_DATA_COMPLETE); + case STATE_SENDING_CMD: + if (!atmci_test_and_clear_pending(host, + EVENT_CMD_COMPLETE)) + break; - if (status & MCI_DTOE) { - dev_dbg(&mmc->class_dev, - "data timeout error\n"); - data->error = -ETIMEDOUT; - } else if (status & MCI_DCRCE) { - dev_dbg(&mmc->class_dev, "data CRC error\n"); - data->error = -EILSEQ; - } else { - dev_dbg(&mmc->class_dev, - "data FIFO error (status=%08x)\n", - status); - data->error = -EIO; - } + host->cmd = NULL; + atmci_set_completed(host, EVENT_CMD_COMPLETE); + atmci_command_complete(host, mrq->cmd); + if (!mrq->data || cmd->error) { + atmci_request_end(host, host->mrq); + goto unlock; + } + + prev_state = state = STATE_SENDING_DATA; + /* fall through */ + + case STATE_SENDING_DATA: + if (atmci_test_and_clear_pending(host, + EVENT_DATA_ERROR)) { + atmci_stop_dma(host); + if (data->stop) + send_stop_cmd(host, data); + state = STATE_DATA_ERROR; + break; + } - if (host->present && data->stop - && atmci_is_completed(host, EVENT_CMD_COMPLETE) - && !atmci_test_and_set_completed( - host, EVENT_STOP_SENT)) - send_stop_cmd(host->mmc, data); + if (!atmci_test_and_clear_pending(host, + EVENT_XFER_COMPLETE)) + break; - host->data = NULL; - } - if (atmci_test_and_clear_pending(host, EVENT_DATA_COMPLETE)) { - atmci_set_completed(host, EVENT_DATA_COMPLETE); + atmci_set_completed(host, EVENT_XFER_COMPLETE); + prev_state = state = STATE_DATA_BUSY; + /* fall through */ + + case STATE_DATA_BUSY: + if (!atmci_test_and_clear_pending(host, + EVENT_DATA_COMPLETE)) + break; - if (!atmci_is_completed(host, EVENT_DATA_ERROR)) { - data->bytes_xfered = data->blocks * data->blksz; - data->error = 0; + host->data = NULL; + atmci_set_completed(host, EVENT_DATA_COMPLETE); + status = host->data_status; + if (unlikely(status & ATMCI_DATA_ERROR_FLAGS)) { + if (status & MCI_DTOE) { + dev_dbg(&host->pdev->dev, + "data timeout error\n"); + data->error = -ETIMEDOUT; + } else if (status & MCI_DCRCE) { + dev_dbg(&host->pdev->dev, + "data CRC error\n"); + data->error = -EILSEQ; + } else { + dev_dbg(&host->pdev->dev, + "data FIFO error (status=%08x)\n", + status); + data->error = -EIO; + } + } else { + data->bytes_xfered = data->blocks * data->blksz; + data->error = 0; + } + + if (!data->stop) { + atmci_request_end(host, host->mrq); + goto unlock; + } + + prev_state = state = STATE_SENDING_STOP; + if (!data->error) + send_stop_cmd(host, data); + /* fall through */ + + case STATE_SENDING_STOP: + if (!atmci_test_and_clear_pending(host, + EVENT_CMD_COMPLETE)) + break; + + host->cmd = NULL; + atmci_command_complete(host, mrq->stop); + atmci_request_end(host, host->mrq); + goto unlock; + + case STATE_DATA_ERROR: + if (!atmci_test_and_clear_pending(host, + EVENT_XFER_COMPLETE)) + break; + + state = STATE_DATA_BUSY; + break; } + } while (state != prev_state); - host->data = NULL; - } + host->state = state; - if (host->mrq && !host->cmd && !host->data) - atmci_request_end(mmc, host->mrq); +unlock: + spin_unlock(&host->lock); } static void atmci_read_data_pio(struct atmel_mci *host) @@ -789,6 +1253,7 @@ static void atmci_read_data_pio(struct atmel_mci *host) nbytes += 4; if (offset == sg->length) { + flush_dcache_page(sg_page(sg)); host->sg = sg = sg_next(sg); if (!sg) goto done; @@ -817,9 +1282,11 @@ static void atmci_read_data_pio(struct atmel_mci *host) mci_writel(host, IDR, (MCI_NOTBUSY | MCI_RXRDY | ATMCI_DATA_ERROR_FLAGS)); host->data_status = status; + data->bytes_xfered += nbytes; + smp_wmb(); atmci_set_pending(host, EVENT_DATA_ERROR); tasklet_schedule(&host->tasklet); - break; + return; } } while (status & MCI_RXRDY); @@ -832,10 +1299,8 @@ done: mci_writel(host, IDR, MCI_RXRDY); mci_writel(host, IER, MCI_NOTBUSY); data->bytes_xfered += nbytes; - atmci_set_completed(host, EVENT_XFER_COMPLETE); - if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE) - && !atmci_test_and_set_completed(host, EVENT_STOP_SENT)) - send_stop_cmd(host->mmc, data); + smp_wmb(); + atmci_set_pending(host, EVENT_XFER_COMPLETE); } static void atmci_write_data_pio(struct atmel_mci *host) @@ -888,9 +1353,11 @@ static void atmci_write_data_pio(struct atmel_mci *host) mci_writel(host, IDR, (MCI_NOTBUSY | MCI_TXRDY | ATMCI_DATA_ERROR_FLAGS)); host->data_status = status; + data->bytes_xfered += nbytes; + smp_wmb(); atmci_set_pending(host, EVENT_DATA_ERROR); tasklet_schedule(&host->tasklet); - break; + return; } } while (status & MCI_TXRDY); @@ -903,38 +1370,26 @@ done: mci_writel(host, IDR, MCI_TXRDY); mci_writel(host, IER, MCI_NOTBUSY); data->bytes_xfered += nbytes; - atmci_set_completed(host, EVENT_XFER_COMPLETE); - if (data->stop && atmci_is_completed(host, EVENT_CMD_COMPLETE) - && !atmci_test_and_set_completed(host, EVENT_STOP_SENT)) - send_stop_cmd(host->mmc, data); + smp_wmb(); + atmci_set_pending(host, EVENT_XFER_COMPLETE); } -static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status) +static void atmci_cmd_interrupt(struct atmel_mci *host, u32 status) { - struct atmel_mci *host = mmc_priv(mmc); - mci_writel(host, IDR, MCI_CMDRDY); - if (atmci_is_completed(host, EVENT_STOP_SENT)) { - host->stop_status = status; - atmci_set_pending(host, EVENT_STOP_COMPLETE); - } else { - host->cmd_status = status; - atmci_set_pending(host, EVENT_CMD_COMPLETE); - } - + host->cmd_status = status; + smp_wmb(); + atmci_set_pending(host, EVENT_CMD_COMPLETE); tasklet_schedule(&host->tasklet); } static irqreturn_t atmci_interrupt(int irq, void *dev_id) { - struct mmc_host *mmc = dev_id; - struct atmel_mci *host = mmc_priv(mmc); + struct atmel_mci *host = dev_id; u32 status, mask, pending; unsigned int pass_count = 0; - spin_lock(&mmc->lock); - do { status = mci_readl(host, SR); mask = mci_readl(host, IMR); @@ -946,13 +1401,18 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) mci_writel(host, IDR, ATMCI_DATA_ERROR_FLAGS | MCI_RXRDY | MCI_TXRDY); pending &= mci_readl(host, IMR); + host->data_status = status; + smp_wmb(); atmci_set_pending(host, EVENT_DATA_ERROR); tasklet_schedule(&host->tasklet); } if (pending & MCI_NOTBUSY) { - mci_writel(host, IDR, (MCI_NOTBUSY - | ATMCI_DATA_ERROR_FLAGS)); + mci_writel(host, IDR, + ATMCI_DATA_ERROR_FLAGS | MCI_NOTBUSY); + if (!host->data_status) + host->data_status = status; + smp_wmb(); atmci_set_pending(host, EVENT_DATA_COMPLETE); tasklet_schedule(&host->tasklet); } @@ -962,18 +1422,15 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) atmci_write_data_pio(host); if (pending & MCI_CMDRDY) - atmci_cmd_interrupt(mmc, status); + atmci_cmd_interrupt(host, status); } while (pass_count++ < 5); - spin_unlock(&mmc->lock); - return pass_count ? IRQ_HANDLED : IRQ_NONE; } static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) { - struct mmc_host *mmc = dev_id; - struct atmel_mci *host = mmc_priv(mmc); + struct atmel_mci_slot *slot = dev_id; /* * Disable interrupts until the pin has stabilized and check @@ -981,19 +1438,176 @@ static irqreturn_t atmci_detect_interrupt(int irq, void *dev_id) * middle of the timer routine when this interrupt triggers. */ disable_irq_nosync(irq); - mod_timer(&host->detect_timer, jiffies + msecs_to_jiffies(20)); + mod_timer(&slot->detect_timer, jiffies + msecs_to_jiffies(20)); return IRQ_HANDLED; } +#ifdef CONFIG_MMC_ATMELMCI_DMA + +static inline struct atmel_mci * +dma_client_to_atmel_mci(struct dma_client *client) +{ + return container_of(client, struct atmel_mci, dma.client); +} + +static enum dma_state_client atmci_dma_event(struct dma_client *client, + struct dma_chan *chan, enum dma_state state) +{ + struct atmel_mci *host; + enum dma_state_client ret = DMA_NAK; + + host = dma_client_to_atmel_mci(client); + + switch (state) { + case DMA_RESOURCE_AVAILABLE: + spin_lock_bh(&host->lock); + if (!host->dma.chan) { + host->dma.chan = chan; + ret = DMA_ACK; + } + spin_unlock_bh(&host->lock); + + if (ret == DMA_ACK) + dev_info(&host->pdev->dev, + "Using %s for DMA transfers\n", + chan->dev.bus_id); + break; + + case DMA_RESOURCE_REMOVED: + spin_lock_bh(&host->lock); + if (host->dma.chan == chan) { + host->dma.chan = NULL; + ret = DMA_ACK; + } + spin_unlock_bh(&host->lock); + + if (ret == DMA_ACK) + dev_info(&host->pdev->dev, + "Lost %s, falling back to PIO\n", + chan->dev.bus_id); + break; + + default: + break; + } + + + return ret; +} +#endif /* CONFIG_MMC_ATMELMCI_DMA */ + +static int __init atmci_init_slot(struct atmel_mci *host, + struct mci_slot_pdata *slot_data, unsigned int id, + u32 sdc_reg) +{ + struct mmc_host *mmc; + struct atmel_mci_slot *slot; + + mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), &host->pdev->dev); + if (!mmc) + return -ENOMEM; + + slot = mmc_priv(mmc); + slot->mmc = mmc; + slot->host = host; + slot->detect_pin = slot_data->detect_pin; + slot->wp_pin = slot_data->wp_pin; + slot->sdc_reg = sdc_reg; + + mmc->ops = &atmci_ops; + mmc->f_min = DIV_ROUND_UP(host->bus_hz, 512); + mmc->f_max = host->bus_hz / 2; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + if (slot_data->bus_width >= 4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + mmc->max_hw_segs = 64; + mmc->max_phys_segs = 64; + mmc->max_req_size = 32768 * 512; + mmc->max_blk_size = 32768; + mmc->max_blk_count = 512; + + /* Assume card is present initially */ + set_bit(ATMCI_CARD_PRESENT, &slot->flags); + if (gpio_is_valid(slot->detect_pin)) { + if (gpio_request(slot->detect_pin, "mmc_detect")) { + dev_dbg(&mmc->class_dev, "no detect pin available\n"); + slot->detect_pin = -EBUSY; + } else if (gpio_get_value(slot->detect_pin)) { + clear_bit(ATMCI_CARD_PRESENT, &slot->flags); + } + } + + if (!gpio_is_valid(slot->detect_pin)) + mmc->caps |= MMC_CAP_NEEDS_POLL; + + if (gpio_is_valid(slot->wp_pin)) { + if (gpio_request(slot->wp_pin, "mmc_wp")) { + dev_dbg(&mmc->class_dev, "no WP pin available\n"); + slot->wp_pin = -EBUSY; + } + } + + host->slot[id] = slot; + mmc_add_host(mmc); + + if (gpio_is_valid(slot->detect_pin)) { + int ret; + + setup_timer(&slot->detect_timer, atmci_detect_change, + (unsigned long)slot); + + ret = request_irq(gpio_to_irq(slot->detect_pin), + atmci_detect_interrupt, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "mmc-detect", slot); + if (ret) { + dev_dbg(&mmc->class_dev, + "could not request IRQ %d for detect pin\n", + gpio_to_irq(slot->detect_pin)); + gpio_free(slot->detect_pin); + slot->detect_pin = -EBUSY; + } + } + + atmci_init_debugfs(slot); + + return 0; +} + +static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, + unsigned int id) +{ + /* Debugfs stuff is cleaned up by mmc core */ + + set_bit(ATMCI_SHUTDOWN, &slot->flags); + smp_wmb(); + + mmc_remove_host(slot->mmc); + + if (gpio_is_valid(slot->detect_pin)) { + int pin = slot->detect_pin; + + free_irq(gpio_to_irq(pin), slot); + del_timer_sync(&slot->detect_timer); + gpio_free(pin); + } + if (gpio_is_valid(slot->wp_pin)) + gpio_free(slot->wp_pin); + + slot->host->slot[id] = NULL; + mmc_free_host(slot->mmc); +} + static int __init atmci_probe(struct platform_device *pdev) { struct mci_platform_data *pdata; - struct atmel_mci *host; - struct mmc_host *mmc; - struct resource *regs; - int irq; - int ret; + struct atmel_mci *host; + struct resource *regs; + unsigned int nr_slots; + int irq; + int ret; regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!regs) @@ -1005,15 +1619,13 @@ static int __init atmci_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct atmel_mci), &pdev->dev); - if (!mmc) + host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL); + if (!host) return -ENOMEM; - host = mmc_priv(mmc); host->pdev = pdev; - host->mmc = mmc; - host->detect_pin = pdata->detect_pin; - host->wp_pin = pdata->wp_pin; + spin_lock_init(&host->lock); + INIT_LIST_HEAD(&host->queue); host->mck = clk_get(&pdev->dev, "mci_clk"); if (IS_ERR(host->mck)) { @@ -1033,122 +1645,102 @@ static int __init atmci_probe(struct platform_device *pdev) host->mapbase = regs->start; - mmc->ops = &atmci_ops; - mmc->f_min = (host->bus_hz + 511) / 512; - mmc->f_max = host->bus_hz / 2; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps |= MMC_CAP_4_BIT_DATA; - - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; - mmc->max_req_size = 32768 * 512; - mmc->max_blk_size = 32768; - mmc->max_blk_count = 512; - - tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)mmc); + tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host); - ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, mmc); + ret = request_irq(irq, atmci_interrupt, 0, pdev->dev.bus_id, host); if (ret) goto err_request_irq; - /* Assume card is present if we don't have a detect pin */ - host->present = 1; - if (gpio_is_valid(host->detect_pin)) { - if (gpio_request(host->detect_pin, "mmc_detect")) { - dev_dbg(&mmc->class_dev, "no detect pin available\n"); - host->detect_pin = -1; - } else { - host->present = !gpio_get_value(host->detect_pin); - } - } +#ifdef CONFIG_MMC_ATMELMCI_DMA + if (pdata->dma_slave) { + struct dma_slave *slave = pdata->dma_slave; - if (!gpio_is_valid(host->detect_pin)) - mmc->caps |= MMC_CAP_NEEDS_POLL; + slave->tx_reg = regs->start + MCI_TDR; + slave->rx_reg = regs->start + MCI_RDR; - if (gpio_is_valid(host->wp_pin)) { - if (gpio_request(host->wp_pin, "mmc_wp")) { - dev_dbg(&mmc->class_dev, "no WP pin available\n"); - host->wp_pin = -1; - } + /* Try to grab a DMA channel */ + host->dma.client.event_callback = atmci_dma_event; + dma_cap_set(DMA_SLAVE, host->dma.client.cap_mask); + host->dma.client.slave = slave; + + dma_async_client_register(&host->dma.client); + dma_async_client_chan_request(&host->dma.client); + } else { + dev_notice(&pdev->dev, "DMA not available, using PIO\n"); } +#endif /* CONFIG_MMC_ATMELMCI_DMA */ platform_set_drvdata(pdev, host); - mmc_add_host(mmc); - - if (gpio_is_valid(host->detect_pin)) { - setup_timer(&host->detect_timer, atmci_detect_change, - (unsigned long)host); - - ret = request_irq(gpio_to_irq(host->detect_pin), - atmci_detect_interrupt, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, - "mmc-detect", mmc); - if (ret) { - dev_dbg(&mmc->class_dev, - "could not request IRQ %d for detect pin\n", - gpio_to_irq(host->detect_pin)); - gpio_free(host->detect_pin); - host->detect_pin = -1; - } + /* We need at least one slot to succeed */ + nr_slots = 0; + ret = -ENODEV; + if (pdata->slot[0].bus_width) { + ret = atmci_init_slot(host, &pdata->slot[0], + MCI_SDCSEL_SLOT_A, 0); + if (!ret) + nr_slots++; + } + if (pdata->slot[1].bus_width) { + ret = atmci_init_slot(host, &pdata->slot[1], + MCI_SDCSEL_SLOT_B, 1); + if (!ret) + nr_slots++; } - dev_info(&mmc->class_dev, - "Atmel MCI controller at 0x%08lx irq %d\n", - host->mapbase, irq); + if (!nr_slots) + goto err_init_slot; - atmci_init_debugfs(host); + dev_info(&pdev->dev, + "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", + host->mapbase, irq, nr_slots); return 0; +err_init_slot: +#ifdef CONFIG_MMC_ATMELMCI_DMA + if (pdata->dma_slave) + dma_async_client_unregister(&host->dma.client); +#endif + free_irq(irq, host); err_request_irq: iounmap(host->regs); err_ioremap: clk_put(host->mck); err_clk_get: - mmc_free_host(mmc); + kfree(host); return ret; } static int __exit atmci_remove(struct platform_device *pdev) { - struct atmel_mci *host = platform_get_drvdata(pdev); + struct atmel_mci *host = platform_get_drvdata(pdev); + unsigned int i; platform_set_drvdata(pdev, NULL); - if (host) { - /* Debugfs stuff is cleaned up by mmc core */ - - if (gpio_is_valid(host->detect_pin)) { - int pin = host->detect_pin; - - /* Make sure the timer doesn't enable the interrupt */ - host->detect_pin = -1; - smp_wmb(); - - free_irq(gpio_to_irq(pin), host->mmc); - del_timer_sync(&host->detect_timer); - gpio_free(pin); - } - - mmc_remove_host(host->mmc); + for (i = 0; i < ATMEL_MCI_MAX_NR_SLOTS; i++) { + if (host->slot[i]) + atmci_cleanup_slot(host->slot[i], i); + } - clk_enable(host->mck); - mci_writel(host, IDR, ~0UL); - mci_writel(host, CR, MCI_CR_MCIDIS); - mci_readl(host, SR); - clk_disable(host->mck); + clk_enable(host->mck); + mci_writel(host, IDR, ~0UL); + mci_writel(host, CR, MCI_CR_MCIDIS); + mci_readl(host, SR); + clk_disable(host->mck); - if (gpio_is_valid(host->wp_pin)) - gpio_free(host->wp_pin); +#ifdef CONFIG_MMC_ATMELMCI_DMA + if (host->dma.client.slave) + dma_async_client_unregister(&host->dma.client); +#endif - free_irq(platform_get_irq(pdev, 0), host->mmc); - iounmap(host->regs); + free_irq(platform_get_irq(pdev, 0), host); + iounmap(host->regs); - clk_put(host->mck); + clk_put(host->mck); + kfree(host); - mmc_free_host(host->mmc); - } return 0; } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 7503b81374e..07faf5412a1 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -95,8 +95,6 @@ * reads which takes nowhere near that long. Older cards may be able to use * shorter timeouts ... but why bother? */ -#define readblock_timeout ktime_set(0, 100 * 1000 * 1000) -#define writeblock_timeout ktime_set(0, 250 * 1000 * 1000) #define r1b_timeout ktime_set(3, 0) @@ -220,9 +218,9 @@ mmc_spi_wait_unbusy(struct mmc_spi_host *host, ktime_t timeout) return mmc_spi_skip(host, timeout, sizeof(host->data->status), 0); } -static int mmc_spi_readtoken(struct mmc_spi_host *host) +static int mmc_spi_readtoken(struct mmc_spi_host *host, ktime_t timeout) { - return mmc_spi_skip(host, readblock_timeout, 1, 0xff); + return mmc_spi_skip(host, timeout, 1, 0xff); } @@ -605,7 +603,8 @@ mmc_spi_setup_data_message( * Return negative errno, else success. */ static int -mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t) +mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, + ktime_t timeout) { struct spi_device *spi = host->spi; int status, i; @@ -673,7 +672,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t) if (scratch->status[i] != 0) return 0; } - return mmc_spi_wait_unbusy(host, writeblock_timeout); + return mmc_spi_wait_unbusy(host, timeout); } /* @@ -693,7 +692,8 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t) * STOP_TRANSMISSION command. */ static int -mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t) +mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t, + ktime_t timeout) { struct spi_device *spi = host->spi; int status; @@ -707,7 +707,7 @@ mmc_spi_readblock(struct mmc_spi_host *host, struct spi_transfer *t) return status; status = scratch->status[0]; if (status == 0xff || status == 0) - status = mmc_spi_readtoken(host); + status = mmc_spi_readtoken(host, timeout); if (status == SPI_TOKEN_SINGLE) { if (host->dma_dev) { @@ -778,6 +778,8 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, struct scatterlist *sg; unsigned n_sg; int multiple = (data->blocks > 1); + u32 clock_rate; + ktime_t timeout; if (data->flags & MMC_DATA_READ) direction = DMA_FROM_DEVICE; @@ -786,6 +788,14 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, mmc_spi_setup_data_message(host, multiple, direction); t = &host->t; + if (t->speed_hz) + clock_rate = t->speed_hz; + else + clock_rate = spi->max_speed_hz; + + timeout = ktime_add_ns(ktime_set(0, 0), data->timeout_ns + + data->timeout_clks * 1000000 / clock_rate); + /* Handle scatterlist segments one at a time, with synch for * each 512-byte block */ @@ -832,9 +842,9 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, t->len); if (direction == DMA_TO_DEVICE) - status = mmc_spi_writeblock(host, t); + status = mmc_spi_writeblock(host, t, timeout); else - status = mmc_spi_readblock(host, t); + status = mmc_spi_readblock(host, t, timeout); if (status < 0) break; @@ -917,7 +927,7 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd, if (scratch->status[tmp] != 0) return; } - tmp = mmc_spi_wait_unbusy(host, writeblock_timeout); + tmp = mmc_spi_wait_unbusy(host, timeout); if (tmp < 0 && !data->error) data->error = tmp; } diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index fcb14c2346c..9bd7026b002 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -144,7 +144,8 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE | SDHCI_QUIRK_RESET_AFTER_REQUEST | - SDHCI_QUIRK_BROKEN_SMALL_PIO; + SDHCI_QUIRK_BROKEN_SMALL_PIO | + SDHCI_QUIRK_FORCE_HIGHSPEED; } /* @@ -326,7 +327,7 @@ static const struct pci_device_id pci_ids[] __devinitdata = { { .vendor = PCI_VENDOR_ID_MARVELL, - .device = PCI_DEVICE_ID_MARVELL_CAFE_SD, + .device = PCI_DEVICE_ID_MARVELL_88ALP01_SD, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, .driver_data = (kernel_ulong_t)&sdhci_cafe, diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e3a8133560a..30f64b1f235 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -177,7 +177,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host) { unsigned long flags; size_t blksize, len, chunk; - u32 scratch; + u32 uninitialized_var(scratch); u8 *buf; DBG("PIO reading\n"); @@ -1154,7 +1154,7 @@ static void sdhci_tasklet_card(unsigned long param) spin_unlock_irqrestore(&host->lock, flags); - mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + mmc_detect_change(host->mmc, msecs_to_jiffies(200)); } static void sdhci_tasklet_finish(unsigned long param) @@ -1266,9 +1266,31 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) SDHCI_INT_INDEX)) host->cmd->error = -EILSEQ; - if (host->cmd->error) + if (host->cmd->error) { tasklet_schedule(&host->finish_tasklet); - else if (intmask & SDHCI_INT_RESPONSE) + return; + } + + /* + * The host can send and interrupt when the busy state has + * ended, allowing us to wait without wasting CPU cycles. + * Unfortunately this is overloaded on the "data complete" + * interrupt, so we need to take some care when handling + * it. + * + * Note: The 1.0 specification is a bit ambiguous about this + * feature so there might be some problems with older + * controllers. + */ + if (host->cmd->flags & MMC_RSP_BUSY) { + if (host->cmd->data) + DBG("Cannot wait for busy signal when also " + "doing a data transfer"); + else + return; + } + + if (intmask & SDHCI_INT_RESPONSE) sdhci_finish_command(host); } @@ -1278,11 +1300,16 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (!host->data) { /* - * A data end interrupt is sent together with the response - * for the stop command. + * The "data complete" interrupt is also used to + * indicate that a busy state has ended. See comment + * above in sdhci_cmd_irq(). */ - if (intmask & SDHCI_INT_DATA_END) - return; + if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { + if (intmask & SDHCI_INT_DATA_END) { + sdhci_finish_command(host); + return; + } + } printk(KERN_ERR "%s: Got data interrupt 0x%08x even " "though no data operation was in progress.\n", @@ -1604,7 +1631,8 @@ int sdhci_add_host(struct sdhci_host *host) mmc->f_max = host->max_clk; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; - if (caps & SDHCI_CAN_DO_HISPD) + if ((caps & SDHCI_CAN_DO_HISPD) || + (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED)) mmc->caps |= MMC_CAP_SD_HIGHSPEED; mmc->ocr_avail = 0; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 197d4a05f4a..31f4b1528e7 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -208,6 +208,8 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<12) /* Controller has an issue with buffer bits for small transfers */ #define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) +/* Controller supports high speed but doesn't have the caps bit set */ +#define SDHCI_QUIRK_FORCE_HIGHSPEED (1<<14) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 0d7c88396c8..fd7a1017399 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -1,13 +1,10 @@ -/* - * - * sun_uflash - Driver implementation for user-programmable flash - * present on many Sun Microsystems SME boardsets. +/* sun_uflash.c - Driver for user-programmable flash on + * Sun Microsystems SME boardsets. * * This driver does NOT provide access to the OBP-flash for * safety reasons-- use <linux>/drivers/sbus/char/flash.c instead. * * Copyright (c) 2001 Eric Brower (ebrower@usa.net) - * */ #include <linux/kernel.h> @@ -16,8 +13,8 @@ #include <linux/errno.h> #include <linux/init.h> #include <linux/ioport.h> -#include <asm/ebus.h> -#include <asm/oplib.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/prom.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -26,67 +23,65 @@ #include <linux/mtd/map.h> #define UFLASH_OBPNAME "flashprom" -#define UFLASH_DEVNAME "userflash" +#define DRIVER_NAME "sun_uflash" +#define PFX DRIVER_NAME ": " #define UFLASH_WINDOW_SIZE 0x200000 #define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */ MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets"); -MODULE_SUPPORTED_DEVICE("userflash"); +MODULE_SUPPORTED_DEVICE(DRIVER_NAME); MODULE_LICENSE("GPL"); -MODULE_VERSION("2.0"); +MODULE_VERSION("2.1"); -static LIST_HEAD(device_list); struct uflash_dev { const char *name; /* device name */ struct map_info map; /* mtd map info */ struct mtd_info *mtd; /* mtd info */ }; - struct map_info uflash_map_templ = { .name = "SUNW,???-????", .size = UFLASH_WINDOW_SIZE, .bankwidth = UFLASH_BUSWIDTH, }; -int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) +int uflash_devinit(struct of_device *op, struct device_node *dp) { struct uflash_dev *up; - struct resource *res; - res = &edev->resource[0]; - - if (edev->num_addrs != 1) { + if (op->resource[1].flags) { /* Non-CFI userflash device-- once I find one we * can work on supporting it. */ - printk("%s: unsupported device at 0x%llx (%d regs): " \ - "email ebrower@usa.net\n", - dp->full_name, (unsigned long long)res->start, - edev->num_addrs); + printk(KERN_ERR PFX "Unsupported device at %s, 0x%llx\n", + dp->full_name, (unsigned long long)op->resource[0].start); return -ENODEV; } up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL); - if (!up) + if (!up) { + printk(KERN_ERR PFX "Cannot allocate struct uflash_dev\n"); return -ENOMEM; + } /* copy defaults and tweak parameters */ memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ)); - up->map.size = (res->end - res->start) + 1UL; + + up->map.size = resource_size(&op->resource[0]); up->name = of_get_property(dp, "model", NULL); if (up->name && 0 < strlen(up->name)) up->map.name = (char *)up->name; - up->map.phys = res->start; + up->map.phys = op->resource[0].start; - up->map.virt = ioremap_nocache(res->start, up->map.size); + up->map.virt = of_ioremap(&op->resource[0], 0, up->map.size, + DRIVER_NAME); if (!up->map.virt) { - printk("%s: Failed to map device.\n", dp->full_name); + printk(KERN_ERR PFX "Failed to map device.\n"); kfree(up); return -EINVAL; @@ -97,7 +92,7 @@ int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) /* MTD registration */ up->mtd = do_map_probe("cfi_probe", &up->map); if (!up->mtd) { - iounmap(up->map.virt); + of_iounmap(&op->resource[0], up->map.virt, up->map.size); kfree(up); return -ENXIO; @@ -107,32 +102,34 @@ int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) add_mtd_device(up->mtd); - dev_set_drvdata(&edev->ofdev.dev, up); + dev_set_drvdata(&op->dev, up); return 0; } -static int __devinit uflash_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit uflash_probe(struct of_device *op, const struct of_device_id *match) { - struct linux_ebus_device *edev = to_ebus_device(&dev->dev); - struct device_node *dp = dev->node; + struct device_node *dp = op->node; - if (of_find_property(dp, "user", NULL)) + /* Flashprom must have the "user" property in order to + * be used by this driver. + */ + if (!of_find_property(dp, "user", NULL)) return -ENODEV; - return uflash_devinit(edev, dp); + return uflash_devinit(op, dp); } -static int __devexit uflash_remove(struct of_device *dev) +static int __devexit uflash_remove(struct of_device *op) { - struct uflash_dev *up = dev_get_drvdata(&dev->dev); + struct uflash_dev *up = dev_get_drvdata(&op->dev); if (up->mtd) { del_mtd_device(up->mtd); map_destroy(up->mtd); } if (up->map.virt) { - iounmap(up->map.virt); + of_iounmap(&op->resource[0], up->map.virt, up->map.size); up->map.virt = NULL; } @@ -141,7 +138,7 @@ static int __devexit uflash_remove(struct of_device *dev) return 0; } -static struct of_device_id uflash_match[] = { +static const struct of_device_id uflash_match[] = { { .name = UFLASH_OBPNAME, }, @@ -151,7 +148,7 @@ static struct of_device_id uflash_match[] = { MODULE_DEVICE_TABLE(of, uflash_match); static struct of_platform_driver uflash_driver = { - .name = UFLASH_DEVNAME, + .name = DRIVER_NAME, .match_table = uflash_match, .probe = uflash_probe, .remove = __devexit_p(uflash_remove), @@ -159,7 +156,7 @@ static struct of_platform_driver uflash_driver = { static int __init uflash_init(void) { - return of_register_driver(&uflash_driver, &ebus_bus_type); + return of_register_driver(&uflash_driver, &of_bus_type); } static void __exit uflash_exit(void) diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 95345d05157..b8064bf3aee 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -1,6 +1,9 @@ /* * Driver for One Laptop Per Child ‘CAFÉ’ controller, aka Marvell 88ALP01 * + * The data sheet for this device can be found at: + * http://www.marvell.com/products/pcconn/88ALP01.jsp + * * Copyright © 2006 Red Hat, Inc. * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> */ @@ -842,7 +845,8 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) } static struct pci_device_id cafe_nand_tbl[] = { - { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, + PCI_ANY_ID, PCI_ANY_ID }, { } }; diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 7b92201a7b5..019b13c08ae 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -94,8 +94,8 @@ static irqreturn_t au1000_interrupt(int, void *); static void au1000_tx_timeout(struct net_device *); static void set_rx_mode(struct net_device *); static int au1000_ioctl(struct net_device *, struct ifreq *, int); -static int mdio_read(struct net_device *, int, int); -static void mdio_write(struct net_device *, int, int, u16); +static int au1000_mdio_read(struct net_device *, int, int); +static void au1000_mdio_write(struct net_device *, int, int, u16); static void au1000_adjust_link(struct net_device *); static void enable_mac(struct net_device *, int); @@ -191,7 +191,7 @@ struct au1000_private *au_macs[NUM_ETH_INTERFACES]; /* * MII operations */ -static int mdio_read(struct net_device *dev, int phy_addr, int reg) +static int au1000_mdio_read(struct net_device *dev, int phy_addr, int reg) { struct au1000_private *aup = (struct au1000_private *) dev->priv; volatile u32 *const mii_control_reg = &aup->mac->mii_control; @@ -225,7 +225,8 @@ static int mdio_read(struct net_device *dev, int phy_addr, int reg) return (int)*mii_data_reg; } -static void mdio_write(struct net_device *dev, int phy_addr, int reg, u16 value) +static void au1000_mdio_write(struct net_device *dev, int phy_addr, + int reg, u16 value) { struct au1000_private *aup = (struct au1000_private *) dev->priv; volatile u32 *const mii_control_reg = &aup->mac->mii_control; @@ -249,7 +250,7 @@ static void mdio_write(struct net_device *dev, int phy_addr, int reg, u16 value) *mii_control_reg = mii_control; } -static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) +static int au1000_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) { /* WARNING: bus->phy_map[phy_addr].attached_dev == dev does * _NOT_ hold (e.g. when PHY is accessed through other MAC's MII bus) */ @@ -257,21 +258,21 @@ static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) enable_mac(dev, 0); /* make sure the MAC associated with this * mii_bus is enabled */ - return mdio_read(dev, phy_addr, regnum); + return au1000_mdio_read(dev, phy_addr, regnum); } -static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, - u16 value) +static int au1000_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, + u16 value) { struct net_device *const dev = bus->priv; enable_mac(dev, 0); /* make sure the MAC associated with this * mii_bus is enabled */ - mdio_write(dev, phy_addr, regnum, value); + au1000_mdio_write(dev, phy_addr, regnum, value); return 0; } -static int mdiobus_reset(struct mii_bus *bus) +static int au1000_mdiobus_reset(struct mii_bus *bus) { struct net_device *const dev = bus->priv; @@ -703,9 +704,9 @@ static struct net_device * au1000_probe(int port_num) goto err_out; aup->mii_bus->priv = dev; - aup->mii_bus->read = mdiobus_read; - aup->mii_bus->write = mdiobus_write; - aup->mii_bus->reset = mdiobus_reset; + aup->mii_bus->read = au1000_mdiobus_read; + aup->mii_bus->write = au1000_mdiobus_write; + aup->mii_bus->reset = au1000_mdiobus_reset; aup->mii_bus->name = "au1000_eth_mii"; snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%x", aup->mac_id); aup->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index a0d41c5d97d..b458d607a9c 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -253,7 +253,7 @@ init_error: * MII operations */ /* Wait until the previous MDC/MDIO transaction has completed */ -static void mdio_poll(void) +static void bfin_mdio_poll(void) { int timeout_cnt = MAX_TIMEOUT_CNT; @@ -269,25 +269,25 @@ static void mdio_poll(void) } /* Read an off-chip register in a PHY through the MDC/MDIO port */ -static int mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) +static int bfin_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) { - mdio_poll(); + bfin_mdio_poll(); /* read mode */ bfin_write_EMAC_STAADD(SET_PHYAD((u16) phy_addr) | SET_REGAD((u16) regnum) | STABUSY); - mdio_poll(); + bfin_mdio_poll(); return (int) bfin_read_EMAC_STADAT(); } /* Write an off-chip register in a PHY through the MDC/MDIO port */ -static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, - u16 value) +static int bfin_mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, + u16 value) { - mdio_poll(); + bfin_mdio_poll(); bfin_write_EMAC_STADAT((u32) value); @@ -297,12 +297,12 @@ static int mdiobus_write(struct mii_bus *bus, int phy_addr, int regnum, STAOP | STABUSY); - mdio_poll(); + bfin_mdio_poll(); return 0; } -static int mdiobus_reset(struct mii_bus *bus) +static int bfin_mdiobus_reset(struct mii_bus *bus) { return 0; } @@ -818,7 +818,7 @@ static void bfin_mac_enable(void) bfin_write_DMA1_CONFIG(rx_list_head->desc_a.config); /* Wait MII done */ - mdio_poll(); + bfin_mdio_poll(); /* We enable only RX here */ /* ASTP : Enable Automatic Pad Stripping @@ -1063,9 +1063,9 @@ static int __devinit bfin_mac_probe(struct platform_device *pdev) goto out_err_mdiobus_alloc; lp->mii_bus->priv = ndev; - lp->mii_bus->read = mdiobus_read; - lp->mii_bus->write = mdiobus_write; - lp->mii_bus->reset = mdiobus_reset; + lp->mii_bus->read = bfin_mdiobus_read; + lp->mii_bus->write = bfin_mdiobus_write; + lp->mii_bus->reset = bfin_mdiobus_reset; lp->mii_bus->name = "bfin_mac_mdio"; snprintf(lp->mii_bus->id, MII_BUS_ID_SIZE, "0"); lp->mii_bus->irq = kmalloc(sizeof(int)*PHY_MAX_ADDR, GFP_KERNEL); diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 3bafaede791..fac82152e4c 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -1047,6 +1047,11 @@ static int __devinit e1000_probe(struct pci_dev *pdev, netdev->features |= NETIF_F_LLTX; + netdev->vlan_features |= NETIF_F_TSO; + netdev->vlan_features |= NETIF_F_TSO6; + netdev->vlan_features |= NETIF_F_HW_CSUM; + netdev->vlan_features |= NETIF_F_SG; + adapter->en_mng_pt = e1000_enable_mng_pass_thru(hw); /* initialize eeprom parameters */ diff --git a/drivers/net/jme.c b/drivers/net/jme.c index 5f9a1313fa3..156f159aafb 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -21,7 +21,6 @@ * */ -#include <linux/version.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/pci.h> diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index 06e682334c7..3ad7589d6a1 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -1,6 +1,6 @@ /* myri_sbus.c: MyriCOM MyriNET SBUS card driver. * - * Copyright (C) 1996, 1999, 2006 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 1999, 2006, 2008 David S. Miller (davem@davemloft.net) */ static char version[] = @@ -22,6 +22,9 @@ static char version[] = #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <net/dst.h> #include <net/arp.h> @@ -33,7 +36,6 @@ static char version[] = #include <asm/dma.h> #include <asm/byteorder.h> #include <asm/idprom.h> -#include <asm/sbus.h> #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/auxio.h> @@ -243,7 +245,8 @@ static void myri_clean_rings(struct myri_eth *mp) u32 dma_addr; dma_addr = sbus_readl(&rxd->myri_scatters[0].addr); - sbus_unmap_single(mp->myri_sdev, dma_addr, RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE); + dma_unmap_single(&mp->myri_op->dev, dma_addr, + RX_ALLOC_SIZE, DMA_FROM_DEVICE); dev_kfree_skb(mp->rx_skbs[i]); mp->rx_skbs[i] = NULL; } @@ -259,7 +262,9 @@ static void myri_clean_rings(struct myri_eth *mp) u32 dma_addr; dma_addr = sbus_readl(&txd->myri_gathers[0].addr); - sbus_unmap_single(mp->myri_sdev, dma_addr, (skb->len + 3) & ~3, SBUS_DMA_TODEVICE); + dma_unmap_single(&mp->myri_op->dev, dma_addr, + (skb->len + 3) & ~3, + DMA_TO_DEVICE); dev_kfree_skb(mp->tx_skbs[i]); mp->tx_skbs[i] = NULL; } @@ -288,7 +293,9 @@ static void myri_init_rings(struct myri_eth *mp, int from_irq) skb->dev = dev; skb_put(skb, RX_ALLOC_SIZE); - dma_addr = sbus_map_single(mp->myri_sdev, skb->data, RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE); + dma_addr = dma_map_single(&mp->myri_op->dev, + skb->data, RX_ALLOC_SIZE, + DMA_FROM_DEVICE); sbus_writel(dma_addr, &rxd[i].myri_scatters[0].addr); sbus_writel(RX_ALLOC_SIZE, &rxd[i].myri_scatters[0].len); sbus_writel(i, &rxd[i].ctx); @@ -344,7 +351,8 @@ static void myri_tx(struct myri_eth *mp, struct net_device *dev) DTX(("SKB[%d] ", entry)); dma_addr = sbus_readl(&sq->myri_txd[entry].myri_gathers[0].addr); - sbus_unmap_single(mp->myri_sdev, dma_addr, skb->len, SBUS_DMA_TODEVICE); + dma_unmap_single(&mp->myri_op->dev, dma_addr, + skb->len, DMA_TO_DEVICE); dev_kfree_skb(skb); mp->tx_skbs[entry] = NULL; dev->stats.tx_packets++; @@ -423,9 +431,9 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev) /* Check for errors. */ DRX(("rxd[%d]: %p len[%d] csum[%08x] ", entry, rxd, len, csum)); - sbus_dma_sync_single_for_cpu(mp->myri_sdev, - sbus_readl(&rxd->myri_scatters[0].addr), - RX_ALLOC_SIZE, SBUS_DMA_FROMDEVICE); + dma_sync_single_for_cpu(&mp->myri_op->dev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, DMA_FROM_DEVICE); if (len < (ETH_HLEN + MYRI_PAD_LEN) || (skb->data[0] != MYRI_PAD_LEN)) { DRX(("ERROR[")); dev->stats.rx_errors++; @@ -442,10 +450,10 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev) drops++; DRX(("DROP ")); dev->stats.rx_dropped++; - sbus_dma_sync_single_for_device(mp->myri_sdev, - sbus_readl(&rxd->myri_scatters[0].addr), - RX_ALLOC_SIZE, - SBUS_DMA_FROMDEVICE); + dma_sync_single_for_device(&mp->myri_op->dev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, + DMA_FROM_DEVICE); sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len); sbus_writel(index, &rxd->ctx); sbus_writel(1, &rxd->num_sg); @@ -464,17 +472,17 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev) DRX(("skb_alloc(FAILED) ")); goto drop_it; } - sbus_unmap_single(mp->myri_sdev, - sbus_readl(&rxd->myri_scatters[0].addr), - RX_ALLOC_SIZE, - SBUS_DMA_FROMDEVICE); + dma_unmap_single(&mp->myri_op->dev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, + DMA_FROM_DEVICE); mp->rx_skbs[index] = new_skb; new_skb->dev = dev; skb_put(new_skb, RX_ALLOC_SIZE); - dma_addr = sbus_map_single(mp->myri_sdev, - new_skb->data, - RX_ALLOC_SIZE, - SBUS_DMA_FROMDEVICE); + dma_addr = dma_map_single(&mp->myri_op->dev, + new_skb->data, + RX_ALLOC_SIZE, + DMA_FROM_DEVICE); sbus_writel(dma_addr, &rxd->myri_scatters[0].addr); sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len); sbus_writel(index, &rxd->ctx); @@ -500,10 +508,10 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev) /* Reuse original ring buffer. */ DRX(("reuse ")); - sbus_dma_sync_single_for_device(mp->myri_sdev, - sbus_readl(&rxd->myri_scatters[0].addr), - RX_ALLOC_SIZE, - SBUS_DMA_FROMDEVICE); + dma_sync_single_for_device(&mp->myri_op->dev, + sbus_readl(&rxd->myri_scatters[0].addr), + RX_ALLOC_SIZE, + DMA_FROM_DEVICE); sbus_writel(RX_ALLOC_SIZE, &rxd->myri_scatters[0].len); sbus_writel(index, &rxd->ctx); sbus_writel(1, &rxd->num_sg); @@ -652,7 +660,8 @@ static int myri_start_xmit(struct sk_buff *skb, struct net_device *dev) sbus_writew((skb->data[4] << 8) | skb->data[5], &txd->addr[3]); } - dma_addr = sbus_map_single(mp->myri_sdev, skb->data, len, SBUS_DMA_TODEVICE); + dma_addr = dma_map_single(&mp->myri_op->dev, skb->data, + len, DMA_TO_DEVICE); sbus_writel(dma_addr, &txd->myri_gathers[0].addr); sbus_writel(len, &txd->myri_gathers[0].len); sbus_writel(1, &txd->num_sg); @@ -891,30 +900,30 @@ static const struct header_ops myri_header_ops = { .cache_update = myri_header_cache_update, }; -static int __devinit myri_ether_init(struct sbus_dev *sdev) +static int __devinit myri_sbus_probe(struct of_device *op, const struct of_device_id *match) { - static int num; + struct device_node *dp = op->node; static unsigned version_printed; struct net_device *dev; - struct myri_eth *mp; - unsigned char prop_buf[32]; - int i; DECLARE_MAC_BUF(mac); + struct myri_eth *mp; + const void *prop; + static int num; + int i, len; - DET(("myri_ether_init(%p,%d):\n", sdev, num)); + DET(("myri_ether_init(%p,%d):\n", op, num)); dev = alloc_etherdev(sizeof(struct myri_eth)); - if (!dev) return -ENOMEM; if (version_printed++ == 0) printk(version); - SET_NETDEV_DEV(dev, &sdev->ofdev.dev); + SET_NETDEV_DEV(dev, &op->dev); - mp = (struct myri_eth *) dev->priv; + mp = netdev_priv(dev); spin_lock_init(&mp->irq_lock); - mp->myri_sdev = sdev; + mp->myri_op = op; /* Clean out skb arrays. */ for (i = 0; i < (RX_RING_SIZE + 1); i++) @@ -924,55 +933,44 @@ static int __devinit myri_ether_init(struct sbus_dev *sdev) mp->tx_skbs[i] = NULL; /* First check for EEPROM information. */ - i = prom_getproperty(sdev->prom_node, "myrinet-eeprom-info", - (char *)&mp->eeprom, sizeof(struct myri_eeprom)); - DET(("prom_getprop(myrinet-eeprom-info) returns %d\n", i)); - if (i == 0 || i == -1) { + prop = of_get_property(dp, "myrinet-eeprom-info", &len); + + if (prop) + memcpy(&mp->eeprom, prop, sizeof(struct myri_eeprom)); + if (!prop) { /* No eeprom property, must cook up the values ourselves. */ DET(("No EEPROM: ")); mp->eeprom.bus_type = BUS_TYPE_SBUS; - mp->eeprom.cpuvers = prom_getintdefault(sdev->prom_node,"cpu_version",0); - mp->eeprom.cval = prom_getintdefault(sdev->prom_node,"clock_value",0); - mp->eeprom.ramsz = prom_getintdefault(sdev->prom_node,"sram_size",0); - DET(("cpuvers[%d] cval[%d] ramsz[%d]\n", mp->eeprom.cpuvers, - mp->eeprom.cval, mp->eeprom.ramsz)); - if (mp->eeprom.cpuvers == 0) { - DET(("EEPROM: cpuvers was zero, setting to %04x\n",CPUVERS_2_3)); + mp->eeprom.cpuvers = + of_getintprop_default(dp, "cpu_version", 0); + mp->eeprom.cval = + of_getintprop_default(dp, "clock_value", 0); + mp->eeprom.ramsz = of_getintprop_default(dp, "sram_size", 0); + if (!mp->eeprom.cpuvers) mp->eeprom.cpuvers = CPUVERS_2_3; - } - if (mp->eeprom.cpuvers < CPUVERS_3_0) { - DET(("EEPROM: cpuvers < CPUVERS_3_0, clockval set to zero.\n")); + if (mp->eeprom.cpuvers < CPUVERS_3_0) mp->eeprom.cval = 0; - } - if (mp->eeprom.ramsz == 0) { - DET(("EEPROM: ramsz == 0, setting to 128k\n")); + if (!mp->eeprom.ramsz) mp->eeprom.ramsz = (128 * 1024); - } - i = prom_getproperty(sdev->prom_node, "myrinet-board-id", - &prop_buf[0], 10); - DET(("EEPROM: prom_getprop(myrinet-board-id) returns %d\n", i)); - if ((i != 0) && (i != -1)) - memcpy(&mp->eeprom.id[0], &prop_buf[0], 6); + + prop = of_get_property(dp, "myrinet-board-id", &len); + if (prop) + memcpy(&mp->eeprom.id[0], prop, 6); else set_boardid_from_idprom(mp, num); - i = prom_getproperty(sdev->prom_node, "fpga_version", - &mp->eeprom.fvers[0], 32); - DET(("EEPROM: prom_getprop(fpga_version) returns %d\n", i)); - if (i == 0 || i == -1) + + prop = of_get_property(dp, "fpga_version", &len); + if (prop) + memcpy(&mp->eeprom.fvers[0], prop, 32); + else memset(&mp->eeprom.fvers[0], 0, 32); if (mp->eeprom.cpuvers == CPUVERS_4_1) { - DET(("EEPROM: cpuvers CPUVERS_4_1, ")); - if (mp->eeprom.ramsz == (128 * 1024)) { - DET(("ramsize 128k, setting to 256k, ")); + if (mp->eeprom.ramsz == (128 * 1024)) mp->eeprom.ramsz = (256 * 1024); - } - if ((mp->eeprom.cval==0x40414041)||(mp->eeprom.cval==0x90449044)){ - DET(("changing cval from %08x to %08x ", - mp->eeprom.cval, 0x50e450e4)); + if ((mp->eeprom.cval == 0x40414041) || + (mp->eeprom.cval == 0x90449044)) mp->eeprom.cval = 0x50e450e4; - } - DET(("\n")); } } #ifdef DEBUG_DETECT @@ -991,8 +989,8 @@ static int __devinit myri_ether_init(struct sbus_dev *sdev) * XXX only a valid version for PCI cards? Ask feldy... */ DET(("Mapping regs for cpuvers < CPUVERS_4_0\n")); - mp->regs = sbus_ioremap(&sdev->resource[0], 0, - mp->reg_size, "MyriCOM Regs"); + mp->regs = of_ioremap(&op->resource[0], 0, + mp->reg_size, "MyriCOM Regs"); if (!mp->regs) { printk("MyriCOM: Cannot map MyriCOM registers.\n"); goto err; @@ -1001,13 +999,12 @@ static int __devinit myri_ether_init(struct sbus_dev *sdev) mp->lregs = mp->lanai + (0x10000 * 2); } else { DET(("Mapping regs for cpuvers >= CPUVERS_4_0\n")); - mp->cregs = sbus_ioremap(&sdev->resource[0], 0, - PAGE_SIZE, "MyriCOM Control Regs"); - mp->lregs = sbus_ioremap(&sdev->resource[0], (256 * 1024), + mp->cregs = of_ioremap(&op->resource[0], 0, + PAGE_SIZE, "MyriCOM Control Regs"); + mp->lregs = of_ioremap(&op->resource[0], (256 * 1024), PAGE_SIZE, "MyriCOM LANAI Regs"); - mp->lanai = - sbus_ioremap(&sdev->resource[0], (512 * 1024), - mp->eeprom.ramsz, "MyriCOM SRAM"); + mp->lanai = of_ioremap(&op->resource[0], (512 * 1024), + mp->eeprom.ramsz, "MyriCOM SRAM"); } DET(("Registers mapped: cregs[%p] lregs[%p] lanai[%p]\n", mp->cregs, mp->lregs, mp->lanai)); @@ -1039,16 +1036,15 @@ static int __devinit myri_ether_init(struct sbus_dev *sdev) myri_reset_on(mp->cregs); /* Get the supported DVMA burst sizes from our SBUS. */ - mp->myri_bursts = prom_getintdefault(mp->myri_sdev->bus->prom_node, - "burst-sizes", 0x00); - - if (!sbus_can_burst64(sdev)) + mp->myri_bursts = of_getintprop_default(dp->parent, + "burst-sizes", 0x00); + if (!sbus_can_burst64()) mp->myri_bursts &= ~(DMA_BURST64); DET(("MYRI bursts %02x\n", mp->myri_bursts)); /* Encode SBUS interrupt level in second control register. */ - i = prom_getint(sdev->prom_node, "interrupts"); + i = of_getintprop_default(dp, "interrupts", 0); if (i == 0) i = 4; DET(("prom_getint(interrupts)==%d, irqlvl set to %04x\n", @@ -1063,7 +1059,7 @@ static int __devinit myri_ether_init(struct sbus_dev *sdev) dev->tx_timeout = &myri_tx_timeout; dev->watchdog_timeo = 5*HZ; dev->set_multicast_list = &myri_set_multicast; - dev->irq = sdev->irqs[0]; + dev->irq = op->irqs[0]; /* Register interrupt handler now. */ DET(("Requesting MYRIcom IRQ line.\n")); @@ -1088,7 +1084,7 @@ static int __devinit myri_ether_init(struct sbus_dev *sdev) goto err_free_irq; } - dev_set_drvdata(&sdev->ofdev.dev, mp); + dev_set_drvdata(&op->dev, mp); num++; @@ -1105,17 +1101,9 @@ err: return -ENODEV; } - -static int __devinit myri_sbus_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - - return myri_ether_init(sdev); -} - -static int __devexit myri_sbus_remove(struct of_device *dev) +static int __devexit myri_sbus_remove(struct of_device *op) { - struct myri_eth *mp = dev_get_drvdata(&dev->dev); + struct myri_eth *mp = dev_get_drvdata(&op->dev); struct net_device *net_dev = mp->dev; unregister_netdev(net_dev); @@ -1123,21 +1111,21 @@ static int __devexit myri_sbus_remove(struct of_device *dev) free_irq(net_dev->irq, net_dev); if (mp->eeprom.cpuvers < CPUVERS_4_0) { - sbus_iounmap(mp->regs, mp->reg_size); + of_iounmap(&op->resource[0], mp->regs, mp->reg_size); } else { - sbus_iounmap(mp->cregs, PAGE_SIZE); - sbus_iounmap(mp->lregs, (256 * 1024)); - sbus_iounmap(mp->lanai, (512 * 1024)); + of_iounmap(&op->resource[0], mp->cregs, PAGE_SIZE); + of_iounmap(&op->resource[0], mp->lregs, (256 * 1024)); + of_iounmap(&op->resource[0], mp->lanai, (512 * 1024)); } free_netdev(net_dev); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id myri_sbus_match[] = { +static const struct of_device_id myri_sbus_match[] = { { .name = "MYRICOM,mlanai", }, @@ -1158,7 +1146,7 @@ static struct of_platform_driver myri_sbus_driver = { static int __init myri_sbus_init(void) { - return of_register_driver(&myri_sbus_driver, &sbus_bus_type); + return of_register_driver(&myri_sbus_driver, &of_bus_type); } static void __exit myri_sbus_exit(void) diff --git a/drivers/net/myri_sbus.h b/drivers/net/myri_sbus.h index 5d93fcc95d5..ff363e95d9c 100644 --- a/drivers/net/myri_sbus.h +++ b/drivers/net/myri_sbus.h @@ -288,7 +288,7 @@ struct myri_eth { struct myri_eeprom eeprom; /* Local copy of EEPROM. */ unsigned int reg_size; /* Size of register space. */ unsigned int shmem_base; /* Offset to shared ram. */ - struct sbus_dev *myri_sdev; /* Our SBUS device struct. */ + struct of_device *myri_op; /* Our OF device struct. */ }; /* We use this to acquire receive skb's that we can DMA directly into. */ diff --git a/drivers/net/niu.c b/drivers/net/niu.c index e3be81eba8a..ebc81270290 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -9130,7 +9130,7 @@ static int __devexit niu_of_remove(struct of_device *op) return 0; } -static struct of_device_id niu_match[] = { +static const struct of_device_id niu_match[] = { { .name = "network", .compatible = "SUNW,niusl", diff --git a/drivers/net/qlge/qlge_ethtool.c b/drivers/net/qlge/qlge_ethtool.c index 6457f8c4fda..b62fbd4bf00 100644 --- a/drivers/net/qlge/qlge_ethtool.c +++ b/drivers/net/qlge/qlge_ethtool.c @@ -29,7 +29,6 @@ #include <linux/skbuff.h> #include <linux/rtnetlink.h> #include <linux/if_vlan.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/mm.h> #include <linux/vmalloc.h> diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 3af822b6226..297877b68c4 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -36,7 +36,6 @@ #include <linux/skbuff.h> #include <linux/rtnetlink.h> #include <linux/if_vlan.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/mm.h> #include <linux/vmalloc.h> diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index bdae2c59a75..c821da21d8e 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -2154,6 +2154,8 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) spin_lock_init(&tp->lock); + tp->mmio_addr = ioaddr; + rtl_init_mac_address(tp, ioaddr); /* Get MAC address */ @@ -2186,7 +2188,6 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #endif tp->intr_mask = 0xffff; - tp->mmio_addr = ioaddr; tp->align = cfg->align; tp->hw_start = cfg->hw_start; tp->intr_event = cfg->intr_event; diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 3805b9318be..3813d15e2df 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -3034,7 +3034,8 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; - if (wol->wolopts & ~sky2_wol_supported(sky2->hw)) + if ((wol->wolopts & ~sky2_wol_supported(sky2->hw)) + || !device_can_wakeup(&hw->pdev->dev)) return -EOPNOTSUPP; sky2->wol = wol->wolopts; @@ -3045,6 +3046,8 @@ static int sky2_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) sky2_write32(hw, B0_CTST, sky2->wol ? Y2_HW_WOL_ON : Y2_HW_WOL_OFF); + device_set_wakeup_enable(&hw->pdev->dev, sky2->wol); + if (!netif_running(dev)) sky2_wol_init(sky2); return 0; @@ -4179,18 +4182,6 @@ static int __devinit sky2_test_msi(struct sky2_hw *hw) return err; } -static int __devinit pci_wake_enabled(struct pci_dev *dev) -{ - int pm = pci_find_capability(dev, PCI_CAP_ID_PM); - u16 value; - - if (!pm) - return 0; - if (pci_read_config_word(dev, pm + PCI_PM_CTRL, &value)) - return 0; - return value & PCI_PM_CTRL_PME_ENABLE; -} - /* * Read and parse the first part of Vital Product Data */ @@ -4314,7 +4305,7 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } } - wol_default = pci_wake_enabled(pdev) ? WAKE_MAGIC : 0; + wol_default = device_may_wakeup(&pdev->dev) ? WAKE_MAGIC : 0; err = -ENOMEM; hw = kzalloc(sizeof(*hw), GFP_KERNEL); diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 02cc064c2c8..8aa7460ef0e 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -685,8 +685,10 @@ static void smc911x_phy_detect(struct net_device *dev) * PHY#1 to PHY#31, and then PHY#0 last. */ switch(lp->version) { - case 0x115: - case 0x117: + case CHIP_9115: + case CHIP_9117: + case CHIP_9215: + case CHIP_9217: cfg = SMC_GET_HW_CFG(lp); if (cfg & HW_CFG_EXT_PHY_DET_) { cfg &= ~HW_CFG_PHY_CLK_SEL_; @@ -722,6 +724,9 @@ static void smc911x_phy_detect(struct net_device *dev) break; } } + if (phyaddr < 32) + /* Found an external PHY */ + break; } default: /* Internal media only */ diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h index 2abfc284519..bf6240f23f5 100644 --- a/drivers/net/smc911x.h +++ b/drivers/net/smc911x.h @@ -666,10 +666,13 @@ smc_pxa_dma_outsl(struct smc911x_local *lp, u_long physaddr, #define LAN911X_INTERNAL_PHY_ID (0x0007C000) /* Chip ID values */ -#define CHIP_9115 0x115 -#define CHIP_9116 0x116 -#define CHIP_9117 0x117 -#define CHIP_9118 0x118 +#define CHIP_9115 0x0115 +#define CHIP_9116 0x0116 +#define CHIP_9117 0x0117 +#define CHIP_9118 0x0118 +#define CHIP_9215 0x115A +#define CHIP_9217 0x117A +#define CHIP_9218 0x118A struct chip_id { u16 id; @@ -681,6 +684,9 @@ static const struct chip_id chip_ids[] = { { CHIP_9116, "LAN9116" }, { CHIP_9117, "LAN9117" }, { CHIP_9118, "LAN9118" }, + { CHIP_9215, "LAN9215" }, + { CHIP_9217, "LAN9217" }, + { CHIP_9218, "LAN9218" }, { 0, NULL }, }; diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 31e7384e312..018d0fca942 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -1,7 +1,6 @@ -/* $Id: sunbmac.c,v 1.30 2002/01/15 06:48:55 davem Exp $ - * sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters. +/* sunbmac.c: Driver for Sparc BigMAC 100baseT ethernet adapters. * - * Copyright (C) 1997, 1998, 1999, 2003 David S. Miller (davem@redhat.com) + * Copyright (C) 1997, 1998, 1999, 2003, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> @@ -23,6 +22,9 @@ #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/auxio.h> #include <asm/byteorder.h> @@ -32,15 +34,14 @@ #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #include <asm/system.h> #include "sunbmac.h" #define DRV_NAME "sunbmac" -#define DRV_VERSION "2.0" -#define DRV_RELDATE "11/24/03" -#define DRV_AUTHOR "David S. Miller (davem@redhat.com)" +#define DRV_VERSION "2.1" +#define DRV_RELDATE "August 26, 2008" +#define DRV_AUTHOR "David S. Miller (davem@davemloft.net)" static char version[] = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " " DRV_AUTHOR "\n"; @@ -96,8 +97,8 @@ static int qec_global_reset(void __iomem *gregs) static void qec_init(struct bigmac *bp) { + struct of_device *qec_op = bp->qec_op; void __iomem *gregs = bp->gregs; - struct sbus_dev *qec_sdev = bp->qec_sdev; u8 bsizes = bp->bigmac_bursts; u32 regval; @@ -112,13 +113,13 @@ static void qec_init(struct bigmac *bp) sbus_writel(GLOB_PSIZE_2048, gregs + GLOB_PSIZE); /* All of memsize is given to bigmac. */ - sbus_writel(qec_sdev->reg_addrs[1].reg_size, + sbus_writel(resource_size(&qec_op->resource[1]), gregs + GLOB_MSIZE); /* Half to the transmitter, half to the receiver. */ - sbus_writel(qec_sdev->reg_addrs[1].reg_size >> 1, + sbus_writel(resource_size(&qec_op->resource[1]) >> 1, gregs + GLOB_TSIZE); - sbus_writel(qec_sdev->reg_addrs[1].reg_size >> 1, + sbus_writel(resource_size(&qec_op->resource[1]) >> 1, gregs + GLOB_RSIZE); } @@ -239,9 +240,10 @@ static void bigmac_init_rings(struct bigmac *bp, int from_irq) skb_reserve(skb, 34); bb->be_rxd[i].rx_addr = - sbus_map_single(bp->bigmac_sdev, skb->data, - RX_BUF_ALLOC_SIZE - 34, - SBUS_DMA_FROMDEVICE); + dma_map_single(&bp->bigmac_op->dev, + skb->data, + RX_BUF_ALLOC_SIZE - 34, + DMA_FROM_DEVICE); bb->be_rxd[i].rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); } @@ -776,9 +778,9 @@ static void bigmac_tx(struct bigmac *bp) skb = bp->tx_skbs[elem]; bp->enet_stats.tx_packets++; bp->enet_stats.tx_bytes += skb->len; - sbus_unmap_single(bp->bigmac_sdev, - this->tx_addr, skb->len, - SBUS_DMA_TODEVICE); + dma_unmap_single(&bp->bigmac_op->dev, + this->tx_addr, skb->len, + DMA_TO_DEVICE); DTX(("skb(%p) ", skb)); bp->tx_skbs[elem] = NULL; @@ -831,18 +833,19 @@ static void bigmac_rx(struct bigmac *bp) drops++; goto drop_it; } - sbus_unmap_single(bp->bigmac_sdev, - this->rx_addr, - RX_BUF_ALLOC_SIZE - 34, - SBUS_DMA_FROMDEVICE); + dma_unmap_single(&bp->bigmac_op->dev, + this->rx_addr, + RX_BUF_ALLOC_SIZE - 34, + DMA_FROM_DEVICE); bp->rx_skbs[elem] = new_skb; new_skb->dev = bp->dev; skb_put(new_skb, ETH_FRAME_LEN); skb_reserve(new_skb, 34); - this->rx_addr = sbus_map_single(bp->bigmac_sdev, - new_skb->data, - RX_BUF_ALLOC_SIZE - 34, - SBUS_DMA_FROMDEVICE); + this->rx_addr = + dma_map_single(&bp->bigmac_op->dev, + new_skb->data, + RX_BUF_ALLOC_SIZE - 34, + DMA_FROM_DEVICE); this->rx_flags = (RXD_OWN | ((RX_BUF_ALLOC_SIZE - 34) & RXD_LENGTH)); @@ -857,13 +860,13 @@ static void bigmac_rx(struct bigmac *bp) } skb_reserve(copy_skb, 2); skb_put(copy_skb, len); - sbus_dma_sync_single_for_cpu(bp->bigmac_sdev, - this->rx_addr, len, - SBUS_DMA_FROMDEVICE); + dma_sync_single_for_cpu(&bp->bigmac_op->dev, + this->rx_addr, len, + DMA_FROM_DEVICE); skb_copy_to_linear_data(copy_skb, (unsigned char *)skb->data, len); - sbus_dma_sync_single_for_device(bp->bigmac_sdev, - this->rx_addr, len, - SBUS_DMA_FROMDEVICE); + dma_sync_single_for_device(&bp->bigmac_op->dev, + this->rx_addr, len, + DMA_FROM_DEVICE); /* Reuse original ring buffer. */ this->rx_flags = @@ -959,7 +962,8 @@ static int bigmac_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 mapping; len = skb->len; - mapping = sbus_map_single(bp->bigmac_sdev, skb->data, len, SBUS_DMA_TODEVICE); + mapping = dma_map_single(&bp->bigmac_op->dev, skb->data, + len, DMA_TO_DEVICE); /* Avoid a race... */ spin_lock_irq(&bp->lock); @@ -1051,12 +1055,8 @@ static void bigmac_set_multicast(struct net_device *dev) /* Ethtool support... */ static void bigmac_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct bigmac *bp = dev->priv; - strcpy(info->driver, "sunbmac"); strcpy(info->version, "2.0"); - sprintf(info->bus_info, "SBUS:%d", - bp->qec_sdev->slot); } static u32 bigmac_get_link(struct net_device *dev) @@ -1075,14 +1075,15 @@ static const struct ethtool_ops bigmac_ethtool_ops = { .get_link = bigmac_get_link, }; -static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) +static int __devinit bigmac_ether_init(struct of_device *op, + struct of_device *qec_op) { - struct net_device *dev; static int version_printed; - struct bigmac *bp; + struct net_device *dev; u8 bsizes, bsizes_more; - int i; DECLARE_MAC_BUF(mac); + struct bigmac *bp; + int i; /* Get a new device struct for this interface. */ dev = alloc_etherdev(sizeof(struct bigmac)); @@ -1092,32 +1093,21 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) if (version_printed++ == 0) printk(KERN_INFO "%s", version); - dev->base_addr = (long) qec_sdev; for (i = 0; i < 6; i++) dev->dev_addr[i] = idprom->id_ethaddr[i]; /* Setup softc, with backpointers to QEC and BigMAC SBUS device structs. */ - bp = dev->priv; - bp->qec_sdev = qec_sdev; - bp->bigmac_sdev = qec_sdev->child; + bp = netdev_priv(dev); + bp->qec_op = qec_op; + bp->bigmac_op = op; - SET_NETDEV_DEV(dev, &bp->bigmac_sdev->ofdev.dev); + SET_NETDEV_DEV(dev, &op->dev); spin_lock_init(&bp->lock); - /* Verify the registers we expect, are actually there. */ - if ((bp->bigmac_sdev->num_registers != 3) || - (bp->qec_sdev->num_registers != 2)) { - printk(KERN_ERR "BIGMAC: Device does not have 2 and 3 regs, it has %d and %d.\n", - bp->qec_sdev->num_registers, - bp->bigmac_sdev->num_registers); - printk(KERN_ERR "BIGMAC: Would you like that for here or to go?\n"); - goto fail_and_cleanup; - } - /* Map in QEC global control registers. */ - bp->gregs = sbus_ioremap(&bp->qec_sdev->resource[0], 0, - GLOB_REG_SIZE, "BigMAC QEC GLobal Regs"); + bp->gregs = of_ioremap(&qec_op->resource[0], 0, + GLOB_REG_SIZE, "BigMAC QEC GLobal Regs"); if (!bp->gregs) { printk(KERN_ERR "BIGMAC: Cannot map QEC global registers.\n"); goto fail_and_cleanup; @@ -1134,13 +1124,8 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) goto fail_and_cleanup; /* Get supported SBUS burst sizes. */ - bsizes = prom_getintdefault(bp->qec_sdev->prom_node, - "burst-sizes", - 0xff); - - bsizes_more = prom_getintdefault(bp->qec_sdev->bus->prom_node, - "burst-sizes", - 0xff); + bsizes = of_getintprop_default(qec_op->node, "burst-sizes", 0xff); + bsizes_more = of_getintprop_default(qec_op->node, "burst-sizes", 0xff); bsizes &= 0xff; if (bsizes_more != 0xff) @@ -1154,16 +1139,16 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) qec_init(bp); /* Map in the BigMAC channel registers. */ - bp->creg = sbus_ioremap(&bp->bigmac_sdev->resource[0], 0, - CREG_REG_SIZE, "BigMAC QEC Channel Regs"); + bp->creg = of_ioremap(&op->resource[0], 0, + CREG_REG_SIZE, "BigMAC QEC Channel Regs"); if (!bp->creg) { printk(KERN_ERR "BIGMAC: Cannot map QEC channel registers.\n"); goto fail_and_cleanup; } /* Map in the BigMAC control registers. */ - bp->bregs = sbus_ioremap(&bp->bigmac_sdev->resource[1], 0, - BMAC_REG_SIZE, "BigMAC Primary Regs"); + bp->bregs = of_ioremap(&op->resource[1], 0, + BMAC_REG_SIZE, "BigMAC Primary Regs"); if (!bp->bregs) { printk(KERN_ERR "BIGMAC: Cannot map BigMAC primary registers.\n"); goto fail_and_cleanup; @@ -1172,8 +1157,8 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) /* Map in the BigMAC transceiver registers, this is how you poke at * the BigMAC's PHY. */ - bp->tregs = sbus_ioremap(&bp->bigmac_sdev->resource[2], 0, - TCVR_REG_SIZE, "BigMAC Transceiver Regs"); + bp->tregs = of_ioremap(&op->resource[2], 0, + TCVR_REG_SIZE, "BigMAC Transceiver Regs"); if (!bp->tregs) { printk(KERN_ERR "BIGMAC: Cannot map BigMAC transceiver registers.\n"); goto fail_and_cleanup; @@ -1183,17 +1168,17 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) bigmac_stop(bp); /* Allocate transmit/receive descriptor DVMA block. */ - bp->bmac_block = sbus_alloc_consistent(bp->bigmac_sdev, - PAGE_SIZE, - &bp->bblock_dvma); + bp->bmac_block = dma_alloc_coherent(&bp->bigmac_op->dev, + PAGE_SIZE, + &bp->bblock_dvma, GFP_ATOMIC); if (bp->bmac_block == NULL || bp->bblock_dvma == 0) { printk(KERN_ERR "BIGMAC: Cannot allocate consistent DMA.\n"); goto fail_and_cleanup; } /* Get the board revision of this BigMAC. */ - bp->board_rev = prom_getintdefault(bp->bigmac_sdev->prom_node, - "board-version", 1); + bp->board_rev = of_getintprop_default(bp->bigmac_op->node, + "board-version", 1); /* Init auto-negotiation timer state. */ init_timer(&bp->bigmac_timer); @@ -1217,7 +1202,7 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) dev->watchdog_timeo = 5*HZ; /* Finish net device registration. */ - dev->irq = bp->bigmac_sdev->irqs[0]; + dev->irq = bp->bigmac_op->irqs[0]; dev->dma = 0; if (register_netdev(dev)) { @@ -1225,7 +1210,7 @@ static int __devinit bigmac_ether_init(struct sbus_dev *qec_sdev) goto fail_and_cleanup; } - dev_set_drvdata(&bp->bigmac_sdev->ofdev.dev, bp); + dev_set_drvdata(&bp->bigmac_op->dev, bp); printk(KERN_INFO "%s: BigMAC 100baseT Ethernet %s\n", dev->name, print_mac(mac, dev->dev_addr)); @@ -1236,66 +1221,67 @@ fail_and_cleanup: /* Something went wrong, undo whatever we did so far. */ /* Free register mappings if any. */ if (bp->gregs) - sbus_iounmap(bp->gregs, GLOB_REG_SIZE); + of_iounmap(&qec_op->resource[0], bp->gregs, GLOB_REG_SIZE); if (bp->creg) - sbus_iounmap(bp->creg, CREG_REG_SIZE); + of_iounmap(&op->resource[0], bp->creg, CREG_REG_SIZE); if (bp->bregs) - sbus_iounmap(bp->bregs, BMAC_REG_SIZE); + of_iounmap(&op->resource[1], bp->bregs, BMAC_REG_SIZE); if (bp->tregs) - sbus_iounmap(bp->tregs, TCVR_REG_SIZE); + of_iounmap(&op->resource[2], bp->tregs, TCVR_REG_SIZE); if (bp->bmac_block) - sbus_free_consistent(bp->bigmac_sdev, - PAGE_SIZE, - bp->bmac_block, - bp->bblock_dvma); + dma_free_coherent(&bp->bigmac_op->dev, + PAGE_SIZE, + bp->bmac_block, + bp->bblock_dvma); /* This also frees the co-located 'dev->priv' */ free_netdev(dev); return -ENODEV; } -/* QEC can be the parent of either QuadEthernet or - * a BigMAC. We want the latter. +/* QEC can be the parent of either QuadEthernet or a BigMAC. We want + * the latter. */ -static int __devinit bigmac_sbus_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit bigmac_sbus_probe(struct of_device *op, + const struct of_device_id *match) { - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct device_node *dp = dev->node; + struct device *parent = op->dev.parent; + struct of_device *qec_op; - if (!strcmp(dp->name, "be")) - sdev = sdev->parent; + qec_op = to_of_device(parent); - return bigmac_ether_init(sdev); + return bigmac_ether_init(op, qec_op); } -static int __devexit bigmac_sbus_remove(struct of_device *dev) +static int __devexit bigmac_sbus_remove(struct of_device *op) { - struct bigmac *bp = dev_get_drvdata(&dev->dev); + struct bigmac *bp = dev_get_drvdata(&op->dev); + struct device *parent = op->dev.parent; struct net_device *net_dev = bp->dev; + struct of_device *qec_op; + + qec_op = to_of_device(parent); unregister_netdev(net_dev); - sbus_iounmap(bp->gregs, GLOB_REG_SIZE); - sbus_iounmap(bp->creg, CREG_REG_SIZE); - sbus_iounmap(bp->bregs, BMAC_REG_SIZE); - sbus_iounmap(bp->tregs, TCVR_REG_SIZE); - sbus_free_consistent(bp->bigmac_sdev, - PAGE_SIZE, - bp->bmac_block, - bp->bblock_dvma); + of_iounmap(&qec_op->resource[0], bp->gregs, GLOB_REG_SIZE); + of_iounmap(&op->resource[0], bp->creg, CREG_REG_SIZE); + of_iounmap(&op->resource[1], bp->bregs, BMAC_REG_SIZE); + of_iounmap(&op->resource[2], bp->tregs, TCVR_REG_SIZE); + dma_free_coherent(&op->dev, + PAGE_SIZE, + bp->bmac_block, + bp->bblock_dvma); free_netdev(net_dev); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id bigmac_sbus_match[] = { - { - .name = "qec", - }, +static const struct of_device_id bigmac_sbus_match[] = { { .name = "be", }, @@ -1313,7 +1299,7 @@ static struct of_platform_driver bigmac_sbus_driver = { static int __init bigmac_init(void) { - return of_register_driver(&bigmac_sbus_driver, &sbus_bus_type); + return of_register_driver(&bigmac_sbus_driver, &of_bus_type); } static void __exit bigmac_exit(void) diff --git a/drivers/net/sunbmac.h b/drivers/net/sunbmac.h index b563d3c2993..8840bc0b840 100644 --- a/drivers/net/sunbmac.h +++ b/drivers/net/sunbmac.h @@ -329,8 +329,8 @@ struct bigmac { unsigned int timer_ticks; struct net_device_stats enet_stats; - struct sbus_dev *qec_sdev; - struct sbus_dev *bigmac_sdev; + struct of_device *qec_op; + struct of_device *bigmac_op; struct net_device *dev; }; diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index b79d5f018f7..f1ebeb5f65b 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -3,7 +3,7 @@ * "Happy Meal Ethernet" found on SunSwift SBUS cards. * * Copyright (C) 1996, 1998, 1999, 2002, 2003, - 2006 David S. Miller (davem@davemloft.net) + * 2006, 2008 David S. Miller (davem@davemloft.net) * * Changes : * 2000/11/11 Willy Tarreau <willy AT meta-x.org> @@ -34,6 +34,7 @@ #include <linux/skbuff.h> #include <linux/mm.h> #include <linux/bitops.h> +#include <linux/dma-mapping.h> #include <asm/system.h> #include <asm/io.h> @@ -41,8 +42,9 @@ #include <asm/byteorder.h> #ifdef CONFIG_SPARC +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/idprom.h> -#include <asm/sbus.h> #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/prom.h> @@ -60,8 +62,8 @@ #include "sunhme.h" #define DRV_NAME "sunhme" -#define DRV_VERSION "3.00" -#define DRV_RELDATE "June 23, 2006" +#define DRV_VERSION "3.10" +#define DRV_RELDATE "August 26, 2008" #define DRV_AUTHOR "David S. Miller (davem@davemloft.net)" static char version[] = @@ -251,13 +253,13 @@ static u32 pci_hme_read_desc32(hme32 *p) #define hme_read_desc32(__hp, __p) \ ((__hp)->read_desc32(__p)) #define hme_dma_map(__hp, __ptr, __size, __dir) \ - ((__hp)->dma_map((__hp)->happy_dev, (__ptr), (__size), (__dir))) + ((__hp)->dma_map((__hp)->dma_dev, (__ptr), (__size), (__dir))) #define hme_dma_unmap(__hp, __addr, __size, __dir) \ - ((__hp)->dma_unmap((__hp)->happy_dev, (__addr), (__size), (__dir))) + ((__hp)->dma_unmap((__hp)->dma_dev, (__addr), (__size), (__dir))) #define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \ - ((__hp)->dma_sync_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir))) + ((__hp)->dma_sync_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir))) #define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ - ((__hp)->dma_sync_for_device((__hp)->happy_dev, (__addr), (__size), (__dir))) + ((__hp)->dma_sync_for_device((__hp)->dma_dev, (__addr), (__size), (__dir))) #else #ifdef CONFIG_SBUS /* SBUS only compilation */ @@ -277,13 +279,13 @@ do { (__txd)->tx_addr = (__force hme32)(u32)(__addr); \ } while(0) #define hme_read_desc32(__hp, __p) ((__force u32)(hme32)*(__p)) #define hme_dma_map(__hp, __ptr, __size, __dir) \ - sbus_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir)) + dma_map_single((__hp)->dma_dev, (__ptr), (__size), (__dir)) #define hme_dma_unmap(__hp, __addr, __size, __dir) \ - sbus_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir)) + dma_unmap_single((__hp)->dma_dev, (__addr), (__size), (__dir)) #define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \ - sbus_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir)) + dma_dma_sync_single_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir)) #define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ - sbus_dma_sync_single_for_device((__hp)->happy_dev, (__addr), (__size), (__dir)) + dma_dma_sync_single_for_device((__hp)->dma_dev, (__addr), (__size), (__dir)) #else /* PCI only compilation */ #define hme_write32(__hp, __reg, __val) \ @@ -305,36 +307,17 @@ static inline u32 hme_read_desc32(struct happy_meal *hp, hme32 *p) return le32_to_cpup((__le32 *)p); } #define hme_dma_map(__hp, __ptr, __size, __dir) \ - pci_map_single((__hp)->happy_dev, (__ptr), (__size), (__dir)) + pci_map_single((__hp)->dma_dev, (__ptr), (__size), (__dir)) #define hme_dma_unmap(__hp, __addr, __size, __dir) \ - pci_unmap_single((__hp)->happy_dev, (__addr), (__size), (__dir)) + pci_unmap_single((__hp)->dma_dev, (__addr), (__size), (__dir)) #define hme_dma_sync_for_cpu(__hp, __addr, __size, __dir) \ - pci_dma_sync_single_for_cpu((__hp)->happy_dev, (__addr), (__size), (__dir)) + pci_dma_sync_single_for_cpu((__hp)->dma_dev, (__addr), (__size), (__dir)) #define hme_dma_sync_for_device(__hp, __addr, __size, __dir) \ - pci_dma_sync_single_for_device((__hp)->happy_dev, (__addr), (__size), (__dir)) + pci_dma_sync_single_for_device((__hp)->dma_dev, (__addr), (__size), (__dir)) #endif #endif -#ifdef SBUS_DMA_BIDIRECTIONAL -# define DMA_BIDIRECTIONAL SBUS_DMA_BIDIRECTIONAL -#else -# define DMA_BIDIRECTIONAL 0 -#endif - -#ifdef SBUS_DMA_FROMDEVICE -# define DMA_FROMDEVICE SBUS_DMA_FROMDEVICE -#else -# define DMA_TODEVICE 1 -#endif - -#ifdef SBUS_DMA_TODEVICE -# define DMA_TODEVICE SBUS_DMA_TODEVICE -#else -# define DMA_FROMDEVICE 2 -#endif - - /* Oh yes, the MIF BitBang is mighty fun to program. BitBucket is more like it. */ static void BB_PUT_BIT(struct happy_meal *hp, void __iomem *tregs, int bit) { @@ -1224,7 +1207,8 @@ static void happy_meal_clean_rings(struct happy_meal *hp) rxd = &hp->happy_block->happy_meal_rxd[i]; dma_addr = hme_read_desc32(hp, &rxd->rx_addr); - hme_dma_unmap(hp, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE); + dma_unmap_single(hp->dma_dev, dma_addr, + RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE); dev_kfree_skb_any(skb); hp->rx_skbs[i] = NULL; } @@ -1242,10 +1226,10 @@ static void happy_meal_clean_rings(struct happy_meal *hp) for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { txd = &hp->happy_block->happy_meal_txd[i]; dma_addr = hme_read_desc32(hp, &txd->tx_addr); - hme_dma_unmap(hp, dma_addr, - (hme_read_desc32(hp, &txd->tx_flags) - & TXFLAG_SIZE), - DMA_TODEVICE); + dma_unmap_single(hp->dma_dev, dma_addr, + (hme_read_desc32(hp, &txd->tx_flags) + & TXFLAG_SIZE), + DMA_TO_DEVICE); if (frag != skb_shinfo(skb)->nr_frags) i++; @@ -1287,7 +1271,8 @@ static void happy_meal_init_rings(struct happy_meal *hp) skb_put(skb, (ETH_FRAME_LEN + RX_OFFSET + 4)); hme_write_rxd(hp, &hb->happy_meal_rxd[i], (RXFLAG_OWN | ((RX_BUF_ALLOC_SIZE - RX_OFFSET) << 16)), - hme_dma_map(hp, skb->data, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE)); + dma_map_single(hp->dma_dev, skb->data, RX_BUF_ALLOC_SIZE, + DMA_FROM_DEVICE)); skb_reserve(skb, RX_OFFSET); } @@ -1593,7 +1578,7 @@ static int happy_meal_init(struct happy_meal *hp) if ((hp->happy_bursts & DMA_BURST64) && ((hp->happy_flags & HFLAG_PCI) != 0 #ifdef CONFIG_SBUS - || sbus_can_burst64(hp->happy_dev) + || sbus_can_burst64() #endif || 0)) { u32 gcfg = GREG_CFG_BURST64; @@ -1603,11 +1588,13 @@ static int happy_meal_init(struct happy_meal *hp) * do not. -DaveM */ #ifdef CONFIG_SBUS - if ((hp->happy_flags & HFLAG_PCI) == 0 && - sbus_can_dma_64bit(hp->happy_dev)) { - sbus_set_sbus64(hp->happy_dev, - hp->happy_bursts); - gcfg |= GREG_CFG_64BIT; + if ((hp->happy_flags & HFLAG_PCI) == 0) { + struct of_device *op = hp->happy_dev; + if (sbus_can_dma_64bit()) { + sbus_set_sbus64(&op->dev, + hp->happy_bursts); + gcfg |= GREG_CFG_64BIT; + } } #endif @@ -1966,7 +1953,7 @@ static void happy_meal_tx(struct happy_meal *hp) dma_len = hme_read_desc32(hp, &this->tx_flags); dma_len &= TXFLAG_SIZE; - hme_dma_unmap(hp, dma_addr, dma_len, DMA_TODEVICE); + dma_unmap_single(hp->dma_dev, dma_addr, dma_len, DMA_TO_DEVICE); elem = NEXT_TX(elem); this = &txbase[elem]; @@ -2044,13 +2031,14 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) drops++; goto drop_it; } - hme_dma_unmap(hp, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE); + dma_unmap_single(hp->dma_dev, dma_addr, RX_BUF_ALLOC_SIZE, DMA_FROM_DEVICE); hp->rx_skbs[elem] = new_skb; new_skb->dev = dev; skb_put(new_skb, (ETH_FRAME_LEN + RX_OFFSET + 4)); hme_write_rxd(hp, this, (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), - hme_dma_map(hp, new_skb->data, RX_BUF_ALLOC_SIZE, DMA_FROMDEVICE)); + dma_map_single(hp->dma_dev, new_skb->data, RX_BUF_ALLOC_SIZE, + DMA_FROM_DEVICE)); skb_reserve(new_skb, RX_OFFSET); /* Trim the original skb for the netif. */ @@ -2065,10 +2053,9 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) skb_reserve(copy_skb, 2); skb_put(copy_skb, len); - hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE); + dma_sync_single_for_cpu(hp->dma_dev, dma_addr, len, DMA_FROM_DEVICE); skb_copy_from_linear_data(skb, copy_skb->data, len); - hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE); - + dma_sync_single_for_device(hp->dma_dev, dma_addr, len, DMA_FROM_DEVICE); /* Reuse original ring buffer. */ hme_write_rxd(hp, this, (RXFLAG_OWN|((RX_BUF_ALLOC_SIZE-RX_OFFSET)<<16)), @@ -2300,7 +2287,7 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 mapping, len; len = skb->len; - mapping = hme_dma_map(hp, skb->data, len, DMA_TODEVICE); + mapping = dma_map_single(hp->dma_dev, skb->data, len, DMA_TO_DEVICE); tx_flags |= (TXFLAG_SOP | TXFLAG_EOP); hme_write_txd(hp, &hp->happy_block->happy_meal_txd[entry], (tx_flags | (len & TXFLAG_SIZE)), @@ -2314,7 +2301,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev) * Otherwise we could race with the device. */ first_len = skb_headlen(skb); - first_mapping = hme_dma_map(hp, skb->data, first_len, DMA_TODEVICE); + first_mapping = dma_map_single(hp->dma_dev, skb->data, first_len, + DMA_TO_DEVICE); entry = NEXT_TX(entry); for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { @@ -2322,10 +2310,9 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev) u32 len, mapping, this_txflags; len = this_frag->size; - mapping = hme_dma_map(hp, - ((void *) page_address(this_frag->page) + - this_frag->page_offset), - len, DMA_TODEVICE); + mapping = dma_map_page(hp->dma_dev, this_frag->page, + this_frag->page_offset, len, + DMA_TO_DEVICE); this_txflags = tx_flags; if (frag == skb_shinfo(skb)->nr_frags - 1) this_txflags |= TXFLAG_EOP; @@ -2493,9 +2480,12 @@ static void hme_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info } #ifdef CONFIG_SBUS else { - struct sbus_dev *sdev = hp->happy_dev; - sprintf(info->bus_info, "SBUS:%d", - sdev->slot); + const struct linux_prom_registers *regs; + struct of_device *op = hp->happy_dev; + regs = of_get_property(op->node, "regs", NULL); + if (regs) + sprintf(info->bus_info, "SBUS:%d", + regs->which_io); } #endif } @@ -2521,63 +2511,21 @@ static const struct ethtool_ops hme_ethtool_ops = { static int hme_version_printed; #ifdef CONFIG_SBUS -void __devinit quattro_get_ranges(struct quattro *qp) -{ - struct sbus_dev *sdev = qp->quattro_dev; - int err; - - err = prom_getproperty(sdev->prom_node, - "ranges", - (char *)&qp->ranges[0], - sizeof(qp->ranges)); - if (err == 0 || err == -1) { - qp->nranges = 0; - return; - } - qp->nranges = (err / sizeof(struct linux_prom_ranges)); -} - -static void __devinit quattro_apply_ranges(struct quattro *qp, struct happy_meal *hp) -{ - struct sbus_dev *sdev = hp->happy_dev; - int rng; - - for (rng = 0; rng < qp->nranges; rng++) { - struct linux_prom_ranges *rngp = &qp->ranges[rng]; - int reg; - - for (reg = 0; reg < 5; reg++) { - if (sdev->reg_addrs[reg].which_io == - rngp->ot_child_space) - break; - } - if (reg == 5) - continue; - - sdev->reg_addrs[reg].which_io = rngp->ot_parent_space; - sdev->reg_addrs[reg].phys_addr += rngp->ot_parent_base; - } -} - /* Given a happy meal sbus device, find it's quattro parent. * If none exist, allocate and return a new one. * * Return NULL on failure. */ -static struct quattro * __devinit quattro_sbus_find(struct sbus_dev *goal_sdev) +static struct quattro * __devinit quattro_sbus_find(struct of_device *child) { - struct sbus_dev *sdev; + struct device *parent = child->dev.parent; + struct of_device *op; struct quattro *qp; - int i; - for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) { - for (i = 0, sdev = qp->quattro_dev; - (sdev != NULL) && (i < 4); - sdev = sdev->next, i++) { - if (sdev == goal_sdev) - return qp; - } - } + op = to_of_device(parent); + qp = dev_get_drvdata(&op->dev); + if (qp) + return qp; qp = kmalloc(sizeof(struct quattro), GFP_KERNEL); if (qp != NULL) { @@ -2586,10 +2534,11 @@ static struct quattro * __devinit quattro_sbus_find(struct sbus_dev *goal_sdev) for (i = 0; i < 4; i++) qp->happy_meals[i] = NULL; - qp->quattro_dev = goal_sdev; + qp->quattro_dev = child; qp->next = qfe_sbus_list; qfe_sbus_list = qp; - quattro_get_ranges(qp); + + dev_set_drvdata(&op->dev, qp); } return qp; } @@ -2602,10 +2551,10 @@ static void __init quattro_sbus_register_irqs(void) struct quattro *qp; for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) { - struct sbus_dev *sdev = qp->quattro_dev; + struct of_device *op = qp->quattro_dev; int err; - err = request_irq(sdev->irqs[0], + err = request_irq(op->irqs[0], quattro_sbus_interrupt, IRQF_SHARED, "Quattro", qp); @@ -2621,9 +2570,9 @@ static void quattro_sbus_free_irqs(void) struct quattro *qp; for (qp = qfe_sbus_list; qp != NULL; qp = qp->next) { - struct sbus_dev *sdev = qp->quattro_dev; + struct of_device *op = qp->quattro_dev; - free_irq(sdev->irqs[0], qp); + free_irq(op->irqs[0], qp); } } #endif /* CONFIG_SBUS */ @@ -2660,9 +2609,9 @@ static struct quattro * __devinit quattro_pci_find(struct pci_dev *pdev) #endif /* CONFIG_PCI */ #ifdef CONFIG_SBUS -static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe) +static int __devinit happy_meal_sbus_probe_one(struct of_device *op, int is_qfe) { - struct device_node *dp = sdev->ofdev.node; + struct device_node *dp = op->node, *sbus_dp; struct quattro *qp = NULL; struct happy_meal *hp; struct net_device *dev; @@ -2671,7 +2620,7 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe DECLARE_MAC_BUF(mac); if (is_qfe) { - qp = quattro_sbus_find(sdev); + qp = quattro_sbus_find(op); if (qp == NULL) goto err_out; for (qfe_slot = 0; qfe_slot < 4; qfe_slot++) @@ -2685,7 +2634,7 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe dev = alloc_etherdev(sizeof(struct happy_meal)); if (!dev) goto err_out; - SET_NETDEV_DEV(dev, &sdev->ofdev.dev); + SET_NETDEV_DEV(dev, &op->dev); if (hme_version_printed++ == 0) printk(KERN_INFO "%s", version); @@ -2713,56 +2662,50 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe memcpy(dev->dev_addr, idprom->id_ethaddr, 6); } - hp = dev->priv; + hp = netdev_priv(dev); - hp->happy_dev = sdev; + hp->happy_dev = op; + hp->dma_dev = &op->dev; spin_lock_init(&hp->happy_lock); err = -ENODEV; - if (sdev->num_registers != 5) { - printk(KERN_ERR "happymeal: Device needs 5 regs, has %d.\n", - sdev->num_registers); - goto err_out_free_netdev; - } - if (qp != NULL) { hp->qfe_parent = qp; hp->qfe_ent = qfe_slot; qp->happy_meals[qfe_slot] = dev; - quattro_apply_ranges(qp, hp); } - hp->gregs = sbus_ioremap(&sdev->resource[0], 0, - GREG_REG_SIZE, "HME Global Regs"); + hp->gregs = of_ioremap(&op->resource[0], 0, + GREG_REG_SIZE, "HME Global Regs"); if (!hp->gregs) { printk(KERN_ERR "happymeal: Cannot map global registers.\n"); goto err_out_free_netdev; } - hp->etxregs = sbus_ioremap(&sdev->resource[1], 0, - ETX_REG_SIZE, "HME TX Regs"); + hp->etxregs = of_ioremap(&op->resource[1], 0, + ETX_REG_SIZE, "HME TX Regs"); if (!hp->etxregs) { printk(KERN_ERR "happymeal: Cannot map MAC TX registers.\n"); goto err_out_iounmap; } - hp->erxregs = sbus_ioremap(&sdev->resource[2], 0, - ERX_REG_SIZE, "HME RX Regs"); + hp->erxregs = of_ioremap(&op->resource[2], 0, + ERX_REG_SIZE, "HME RX Regs"); if (!hp->erxregs) { printk(KERN_ERR "happymeal: Cannot map MAC RX registers.\n"); goto err_out_iounmap; } - hp->bigmacregs = sbus_ioremap(&sdev->resource[3], 0, - BMAC_REG_SIZE, "HME BIGMAC Regs"); + hp->bigmacregs = of_ioremap(&op->resource[3], 0, + BMAC_REG_SIZE, "HME BIGMAC Regs"); if (!hp->bigmacregs) { printk(KERN_ERR "happymeal: Cannot map BIGMAC registers.\n"); goto err_out_iounmap; } - hp->tcvregs = sbus_ioremap(&sdev->resource[4], 0, - TCVR_REG_SIZE, "HME Tranceiver Regs"); + hp->tcvregs = of_ioremap(&op->resource[4], 0, + TCVR_REG_SIZE, "HME Tranceiver Regs"); if (!hp->tcvregs) { printk(KERN_ERR "happymeal: Cannot map TCVR registers.\n"); goto err_out_iounmap; @@ -2781,13 +2724,18 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe if (qp != NULL) hp->happy_flags |= HFLAG_QUATTRO; + sbus_dp = to_of_device(op->dev.parent)->node; + if (is_qfe) + sbus_dp = to_of_device(op->dev.parent->parent)->node; + /* Get the supported DVMA burst sizes from our Happy SBUS. */ - hp->happy_bursts = of_getintprop_default(sdev->bus->ofdev.node, + hp->happy_bursts = of_getintprop_default(sbus_dp, "burst-sizes", 0x00); - hp->happy_block = sbus_alloc_consistent(hp->happy_dev, - PAGE_SIZE, - &hp->hblock_dvma); + hp->happy_block = dma_alloc_coherent(hp->dma_dev, + PAGE_SIZE, + &hp->hblock_dvma, + GFP_ATOMIC); err = -ENOMEM; if (!hp->happy_block) { printk(KERN_ERR "happymeal: Cannot allocate descriptors.\n"); @@ -2816,19 +2764,13 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe /* Happy Meal can do it all... */ dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; - dev->irq = sdev->irqs[0]; + dev->irq = op->irqs[0]; #if defined(CONFIG_SBUS) && defined(CONFIG_PCI) - /* Hook up PCI register/dma accessors. */ + /* Hook up SBUS register/descriptor accessors. */ hp->read_desc32 = sbus_hme_read_desc32; hp->write_txd = sbus_hme_write_txd; hp->write_rxd = sbus_hme_write_rxd; - hp->dma_map = (u32 (*)(void *, void *, long, int))sbus_map_single; - hp->dma_unmap = (void (*)(void *, u32, long, int))sbus_unmap_single; - hp->dma_sync_for_cpu = (void (*)(void *, u32, long, int)) - sbus_dma_sync_single_for_cpu; - hp->dma_sync_for_device = (void (*)(void *, u32, long, int)) - sbus_dma_sync_single_for_device; hp->read32 = sbus_hme_read32; hp->write32 = sbus_hme_write32; #endif @@ -2843,10 +2785,10 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe if (register_netdev(hp->dev)) { printk(KERN_ERR "happymeal: Cannot register net device, " "aborting.\n"); - goto err_out_free_consistent; + goto err_out_free_coherent; } - dev_set_drvdata(&sdev->ofdev.dev, hp); + dev_set_drvdata(&op->dev, hp); if (qfe_slot != -1) printk(KERN_INFO "%s: Quattro HME slot %d (SBUS) 10/100baseT Ethernet ", @@ -2859,23 +2801,23 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe return 0; -err_out_free_consistent: - sbus_free_consistent(hp->happy_dev, - PAGE_SIZE, - hp->happy_block, - hp->hblock_dvma); +err_out_free_coherent: + dma_free_coherent(hp->dma_dev, + PAGE_SIZE, + hp->happy_block, + hp->hblock_dvma); err_out_iounmap: if (hp->gregs) - sbus_iounmap(hp->gregs, GREG_REG_SIZE); + of_iounmap(&op->resource[0], hp->gregs, GREG_REG_SIZE); if (hp->etxregs) - sbus_iounmap(hp->etxregs, ETX_REG_SIZE); + of_iounmap(&op->resource[1], hp->etxregs, ETX_REG_SIZE); if (hp->erxregs) - sbus_iounmap(hp->erxregs, ERX_REG_SIZE); + of_iounmap(&op->resource[2], hp->erxregs, ERX_REG_SIZE); if (hp->bigmacregs) - sbus_iounmap(hp->bigmacregs, BMAC_REG_SIZE); + of_iounmap(&op->resource[3], hp->bigmacregs, BMAC_REG_SIZE); if (hp->tcvregs) - sbus_iounmap(hp->tcvregs, TCVR_REG_SIZE); + of_iounmap(&op->resource[4], hp->tcvregs, TCVR_REG_SIZE); err_out_free_netdev: free_netdev(dev); @@ -3035,6 +2977,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, memset(hp, 0, sizeof(*hp)); hp->happy_dev = pdev; + hp->dma_dev = &pdev->dev; spin_lock_init(&hp->happy_lock); @@ -3121,7 +3064,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, #endif hp->happy_block = (struct hmeal_init_block *) - pci_alloc_consistent(pdev, PAGE_SIZE, &hp->hblock_dvma); + dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &hp->hblock_dvma, GFP_KERNEL); err = -ENODEV; if (!hp->happy_block) { @@ -3151,16 +3094,10 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; #if defined(CONFIG_SBUS) && defined(CONFIG_PCI) - /* Hook up PCI register/dma accessors. */ + /* Hook up PCI register/descriptor accessors. */ hp->read_desc32 = pci_hme_read_desc32; hp->write_txd = pci_hme_write_txd; hp->write_rxd = pci_hme_write_rxd; - hp->dma_map = (u32 (*)(void *, void *, long, int))pci_map_single; - hp->dma_unmap = (void (*)(void *, u32, long, int))pci_unmap_single; - hp->dma_sync_for_cpu = (void (*)(void *, u32, long, int)) - pci_dma_sync_single_for_cpu; - hp->dma_sync_for_device = (void (*)(void *, u32, long, int)) - pci_dma_sync_single_for_device; hp->read32 = pci_hme_read32; hp->write32 = pci_hme_write32; #endif @@ -3231,10 +3168,8 @@ static void __devexit happy_meal_pci_remove(struct pci_dev *pdev) unregister_netdev(net_dev); - pci_free_consistent(hp->happy_dev, - PAGE_SIZE, - hp->happy_block, - hp->hblock_dvma); + dma_free_coherent(hp->dma_dev, PAGE_SIZE, + hp->happy_block, hp->hblock_dvma); iounmap(hp->gregs); pci_release_regions(hp->happy_dev); @@ -3279,46 +3214,45 @@ static void happy_meal_pci_exit(void) #endif #ifdef CONFIG_SBUS -static int __devinit hme_sbus_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit hme_sbus_probe(struct of_device *op, const struct of_device_id *match) { - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct device_node *dp = dev->node; + struct device_node *dp = op->node; const char *model = of_get_property(dp, "model", NULL); int is_qfe = (match->data != NULL); if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe")) is_qfe = 1; - return happy_meal_sbus_probe_one(sdev, is_qfe); + return happy_meal_sbus_probe_one(op, is_qfe); } -static int __devexit hme_sbus_remove(struct of_device *dev) +static int __devexit hme_sbus_remove(struct of_device *op) { - struct happy_meal *hp = dev_get_drvdata(&dev->dev); + struct happy_meal *hp = dev_get_drvdata(&op->dev); struct net_device *net_dev = hp->dev; unregister_netdev(net_dev); /* XXX qfe parent interrupt... */ - sbus_iounmap(hp->gregs, GREG_REG_SIZE); - sbus_iounmap(hp->etxregs, ETX_REG_SIZE); - sbus_iounmap(hp->erxregs, ERX_REG_SIZE); - sbus_iounmap(hp->bigmacregs, BMAC_REG_SIZE); - sbus_iounmap(hp->tcvregs, TCVR_REG_SIZE); - sbus_free_consistent(hp->happy_dev, - PAGE_SIZE, - hp->happy_block, - hp->hblock_dvma); + of_iounmap(&op->resource[0], hp->gregs, GREG_REG_SIZE); + of_iounmap(&op->resource[1], hp->etxregs, ETX_REG_SIZE); + of_iounmap(&op->resource[2], hp->erxregs, ERX_REG_SIZE); + of_iounmap(&op->resource[3], hp->bigmacregs, BMAC_REG_SIZE); + of_iounmap(&op->resource[4], hp->tcvregs, TCVR_REG_SIZE); + dma_free_coherent(hp->dma_dev, + PAGE_SIZE, + hp->happy_block, + hp->hblock_dvma); free_netdev(net_dev); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id hme_sbus_match[] = { +static const struct of_device_id hme_sbus_match[] = { { .name = "SUNW,hme", }, @@ -3346,7 +3280,7 @@ static int __init happy_meal_sbus_init(void) { int err; - err = of_register_driver(&hme_sbus_driver, &sbus_bus_type); + err = of_register_driver(&hme_sbus_driver, &of_bus_type); if (!err) quattro_sbus_register_irqs(); diff --git a/drivers/net/sunhme.h b/drivers/net/sunhme.h index 4da5539fac7..efd2ca0fcad 100644 --- a/drivers/net/sunhme.h +++ b/drivers/net/sunhme.h @@ -405,14 +405,11 @@ struct happy_meal { u32 (*read_desc32)(hme32 *); void (*write_txd)(struct happy_meal_txd *, u32, u32); void (*write_rxd)(struct happy_meal_rxd *, u32, u32); - u32 (*dma_map)(void *, void *, long, int); - void (*dma_unmap)(void *, u32, long, int); - void (*dma_sync_for_cpu)(void *, u32, long, int); - void (*dma_sync_for_device)(void *, u32, long, int); #endif - /* This is either a sbus_dev or a pci_dev. */ + /* This is either an of_device or a pci_dev. */ void *happy_dev; + struct device *dma_dev; spinlock_t happy_lock; diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 4e994f87469..704301a5a7f 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -91,6 +91,9 @@ static char lancestr[] = "LANCE"; #include <linux/skbuff.h> #include <linux/ethtool.h> #include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/system.h> #include <asm/io.h> @@ -98,7 +101,6 @@ static char lancestr[] = "LANCE"; #include <asm/pgtable.h> #include <asm/byteorder.h> /* Used by the checksum routines */ #include <asm/idprom.h> -#include <asm/sbus.h> #include <asm/prom.h> #include <asm/auxio.h> /* For tpe-link-test? setting */ #include <asm/irq.h> @@ -248,7 +250,7 @@ struct lance_private { int rx_new, tx_new; int rx_old, tx_old; - struct sbus_dma *ledma; /* If set this points to ledma */ + struct of_device *ledma; /* If set this points to ledma */ char tpe; /* cable-selection is TPE */ char auto_select; /* cable-selection by carrier */ char burst_sizes; /* ledma SBus burst sizes */ @@ -263,7 +265,8 @@ struct lance_private { char *name; dma_addr_t init_block_dvma; struct net_device *dev; /* Backpointer */ - struct sbus_dev *sdev; + struct of_device *op; + struct of_device *lebuffer; struct timer_list multicast_timer; }; @@ -1272,27 +1275,29 @@ static void lance_set_multicast_retry(unsigned long _opaque) static void lance_free_hwresources(struct lance_private *lp) { if (lp->lregs) - sbus_iounmap(lp->lregs, LANCE_REG_SIZE); + of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE); + if (lp->dregs) { + struct of_device *ledma = lp->ledma; + + of_iounmap(&ledma->resource[0], lp->dregs, + resource_size(&ledma->resource[0])); + } if (lp->init_block_iomem) { - sbus_iounmap(lp->init_block_iomem, - sizeof(struct lance_init_block)); + of_iounmap(&lp->lebuffer->resource[0], lp->init_block_iomem, + sizeof(struct lance_init_block)); } else if (lp->init_block_mem) { - sbus_free_consistent(lp->sdev, - sizeof(struct lance_init_block), - lp->init_block_mem, - lp->init_block_dvma); + dma_free_coherent(&lp->op->dev, + sizeof(struct lance_init_block), + lp->init_block_mem, + lp->init_block_dvma); } } /* Ethtool support... */ static void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct lance_private *lp = netdev_priv(dev); - strcpy(info->driver, "sunlance"); strcpy(info->version, "2.02"); - sprintf(info->bus_info, "SBUS:%d", - lp->sdev->slot); } static u32 sparc_lance_get_link(struct net_device *dev) @@ -1308,16 +1313,16 @@ static const struct ethtool_ops sparc_lance_ethtool_ops = { .get_link = sparc_lance_get_link, }; -static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev, - struct sbus_dma *ledma, - struct sbus_dev *lebuffer) +static int __devinit sparc_lance_probe_one(struct of_device *op, + struct of_device *ledma, + struct of_device *lebuffer) { + struct device_node *dp = op->node; static unsigned version_printed; - struct device_node *dp = sdev->ofdev.node; - struct net_device *dev; struct lance_private *lp; - int i; + struct net_device *dev; DECLARE_MAC_BUF(mac); + int i; dev = alloc_etherdev(sizeof(struct lance_private) + 8); if (!dev) @@ -1338,14 +1343,27 @@ static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev, dev->dev_addr[i] = idprom->id_ethaddr[i]; /* Get the IO region */ - lp->lregs = sbus_ioremap(&sdev->resource[0], 0, - LANCE_REG_SIZE, lancestr); + lp->lregs = of_ioremap(&op->resource[0], 0, + LANCE_REG_SIZE, lancestr); if (!lp->lregs) { printk(KERN_ERR "SunLance: Cannot map registers.\n"); goto fail; } - lp->sdev = sdev; + lp->ledma = ledma; + if (lp->ledma) { + lp->dregs = of_ioremap(&ledma->resource[0], 0, + resource_size(&ledma->resource[0]), + "ledma"); + if (!lp->dregs) { + printk(KERN_ERR "SunLance: Cannot map " + "ledma registers.\n"); + goto fail; + } + } + + lp->op = op; + lp->lebuffer = lebuffer; if (lebuffer) { /* sanity check */ if (lebuffer->resource[0].start & 7) { @@ -1353,8 +1371,8 @@ static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev, goto fail; } lp->init_block_iomem = - sbus_ioremap(&lebuffer->resource[0], 0, - sizeof(struct lance_init_block), "lebuffer"); + of_ioremap(&lebuffer->resource[0], 0, + sizeof(struct lance_init_block), "lebuffer"); if (!lp->init_block_iomem) { printk(KERN_ERR "SunLance: Cannot map PIO buffer.\n"); goto fail; @@ -1366,9 +1384,10 @@ static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev, lp->tx = lance_tx_pio; } else { lp->init_block_mem = - sbus_alloc_consistent(sdev, sizeof(struct lance_init_block), - &lp->init_block_dvma); - if (!lp->init_block_mem || lp->init_block_dvma == 0) { + dma_alloc_coherent(&op->dev, + sizeof(struct lance_init_block), + &lp->init_block_dvma, GFP_ATOMIC); + if (!lp->init_block_mem) { printk(KERN_ERR "SunLance: Cannot allocate consistent DMA memory.\n"); goto fail; } @@ -1383,13 +1402,13 @@ static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev, LE_C3_BCON)); lp->name = lancestr; - lp->ledma = ledma; lp->burst_sizes = 0; if (lp->ledma) { - struct device_node *ledma_dp = ledma->sdev->ofdev.node; - const char *prop; + struct device_node *ledma_dp = ledma->node; + struct device_node *sbus_dp; unsigned int sbmask; + const char *prop; u32 csr; /* Find burst-size property for ledma */ @@ -1397,7 +1416,8 @@ static int __devinit sparc_lance_probe_one(struct sbus_dev *sdev, "burst-sizes", 0); /* ledma may be capable of fast bursts, but sbus may not. */ - sbmask = of_getintprop_default(ledma_dp, "burst-sizes", + sbus_dp = ledma_dp->parent; + sbmask = of_getintprop_default(sbus_dp, "burst-sizes", DMA_BURSTBITS); lp->burst_sizes &= sbmask; @@ -1435,8 +1455,6 @@ no_link_test: lp->tpe = 1; } - lp->dregs = ledma->regs; - /* Reset ledma */ csr = sbus_readl(lp->dregs + DMA_CSR); sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR); @@ -1446,7 +1464,7 @@ no_link_test: lp->dregs = NULL; lp->dev = dev; - SET_NETDEV_DEV(dev, &sdev->ofdev.dev); + SET_NETDEV_DEV(dev, &op->dev); dev->open = &lance_open; dev->stop = &lance_close; dev->hard_start_xmit = &lance_start_xmit; @@ -1455,9 +1473,7 @@ no_link_test: dev->set_multicast_list = &lance_set_multicast; dev->ethtool_ops = &sparc_lance_ethtool_ops; - dev->irq = sdev->irqs[0]; - - dev->dma = 0; + dev->irq = op->irqs[0]; /* We cannot sleep if the chip is busy during a * multicast list update event, because such events @@ -1473,7 +1489,7 @@ no_link_test: goto fail; } - dev_set_drvdata(&sdev->ofdev.dev, lp); + dev_set_drvdata(&op->dev, lp); printk(KERN_INFO "%s: LANCE %s\n", dev->name, print_mac(mac, dev->dev_addr)); @@ -1486,80 +1502,25 @@ fail: return -ENODEV; } -/* On 4m, find the associated dma for the lance chip */ -static struct sbus_dma * __devinit find_ledma(struct sbus_dev *sdev) -{ - struct sbus_dma *p; - - for_each_dvma(p) { - if (p->sdev == sdev) - return p; - } - return NULL; -} - -#ifdef CONFIG_SUN4 - -#include <asm/sun4paddr.h> -#include <asm/machines.h> - -/* Find all the lance cards on the system and initialize them */ -static struct sbus_dev sun4_sdev; -static int __devinit sparc_lance_init(void) -{ - if ((idprom->id_machtype == (SM_SUN4|SM_4_330)) || - (idprom->id_machtype == (SM_SUN4|SM_4_470))) { - memset(&sun4_sdev, 0, sizeof(struct sbus_dev)); - sun4_sdev.reg_addrs[0].phys_addr = sun4_eth_physaddr; - sun4_sdev.irqs[0] = 6; - return sparc_lance_probe_one(&sun4_sdev, NULL, NULL); - } - return -ENODEV; -} - -static int __exit sunlance_sun4_remove(void) +static int __devinit sunlance_sbus_probe(struct of_device *op, const struct of_device_id *match) { - struct lance_private *lp = dev_get_drvdata(&sun4_sdev.ofdev.dev); - struct net_device *net_dev = lp->dev; - - unregister_netdev(net_dev); - - lance_free_hwresources(lp); - - free_netdev(net_dev); - - dev_set_drvdata(&sun4_sdev.ofdev.dev, NULL); - - return 0; -} - -#else /* !CONFIG_SUN4 */ - -static int __devinit sunlance_sbus_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); + struct of_device *parent = to_of_device(op->dev.parent); + struct device_node *parent_dp = parent->node; int err; - if (sdev->parent) { - struct of_device *parent = &sdev->parent->ofdev; - - if (!strcmp(parent->node->name, "ledma")) { - struct sbus_dma *ledma = find_ledma(to_sbus_device(&parent->dev)); - - err = sparc_lance_probe_one(sdev, ledma, NULL); - } else if (!strcmp(parent->node->name, "lebuffer")) { - err = sparc_lance_probe_one(sdev, NULL, to_sbus_device(&parent->dev)); - } else - err = sparc_lance_probe_one(sdev, NULL, NULL); + if (!strcmp(parent_dp->name, "ledma")) { + err = sparc_lance_probe_one(op, parent, NULL); + } else if (!strcmp(parent_dp->name, "lebuffer")) { + err = sparc_lance_probe_one(op, NULL, parent); } else - err = sparc_lance_probe_one(sdev, NULL, NULL); + err = sparc_lance_probe_one(op, NULL, NULL); return err; } -static int __devexit sunlance_sbus_remove(struct of_device *dev) +static int __devexit sunlance_sbus_remove(struct of_device *op) { - struct lance_private *lp = dev_get_drvdata(&dev->dev); + struct lance_private *lp = dev_get_drvdata(&op->dev); struct net_device *net_dev = lp->dev; unregister_netdev(net_dev); @@ -1568,12 +1529,12 @@ static int __devexit sunlance_sbus_remove(struct of_device *dev) free_netdev(net_dev); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id sunlance_sbus_match[] = { +static const struct of_device_id sunlance_sbus_match[] = { { .name = "le", }, @@ -1593,17 +1554,12 @@ static struct of_platform_driver sunlance_sbus_driver = { /* Find all the lance cards on the system and initialize them */ static int __init sparc_lance_init(void) { - return of_register_driver(&sunlance_sbus_driver, &sbus_bus_type); + return of_register_driver(&sunlance_sbus_driver, &of_bus_type); } -#endif /* !CONFIG_SUN4 */ static void __exit sparc_lance_exit(void) { -#ifdef CONFIG_SUN4 - sunlance_sun4_remove(); -#else of_unregister_driver(&sunlance_sbus_driver); -#endif } module_init(sparc_lance_init); diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index e811331d460..f63644744ff 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -3,7 +3,7 @@ * controller out there can be most efficiently programmed * if you make it look like a LANCE. * - * Copyright (C) 1996, 1999, 2003, 2006 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 1999, 2003, 2006, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> @@ -24,13 +24,15 @@ #include <linux/skbuff.h> #include <linux/ethtool.h> #include <linux/bitops.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/system.h> #include <asm/io.h> #include <asm/dma.h> #include <asm/byteorder.h> #include <asm/idprom.h> -#include <asm/sbus.h> #include <asm/openprom.h> #include <asm/oplib.h> #include <asm/auxio.h> @@ -40,8 +42,8 @@ #include "sunqe.h" #define DRV_NAME "sunqe" -#define DRV_VERSION "4.0" -#define DRV_RELDATE "June 23, 2006" +#define DRV_VERSION "4.1" +#define DRV_RELDATE "August 27, 2008" #define DRV_AUTHOR "David S. Miller (davem@davemloft.net)" static char version[] = @@ -690,12 +692,18 @@ static void qe_set_multicast(struct net_device *dev) /* Ethtool support... */ static void qe_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { + const struct linux_prom_registers *regs; struct sunqe *qep = dev->priv; + struct of_device *op; strcpy(info->driver, "sunqe"); strcpy(info->version, "3.0"); - sprintf(info->bus_info, "SBUS:%d", - qep->qe_sdev->slot); + + op = qep->op; + regs = of_get_property(op->node, "reg", NULL); + if (regs) + sprintf(info->bus_info, "SBUS:%d", regs->which_io); + } static u32 qe_get_link(struct net_device *dev) @@ -717,11 +725,11 @@ static const struct ethtool_ops qe_ethtool_ops = { }; /* This is only called once at boot time for each card probed. */ -static inline void qec_init_once(struct sunqec *qecp, struct sbus_dev *qsdev) +static void qec_init_once(struct sunqec *qecp, struct of_device *op) { u8 bsizes = qecp->qec_bursts; - if (sbus_can_burst64(qsdev) && (bsizes & DMA_BURST64)) { + if (sbus_can_burst64() && (bsizes & DMA_BURST64)) { sbus_writel(GLOB_CTRL_B64, qecp->gregs + GLOB_CTRL); } else if (bsizes & DMA_BURST32) { sbus_writel(GLOB_CTRL_B32, qecp->gregs + GLOB_CTRL); @@ -735,15 +743,15 @@ static inline void qec_init_once(struct sunqec *qecp, struct sbus_dev *qsdev) sbus_writel(GLOB_PSIZE_2048, qecp->gregs + GLOB_PSIZE); /* Set the local memsize register, divided up to one piece per QE channel. */ - sbus_writel((qsdev->reg_addrs[1].reg_size >> 2), + sbus_writel((resource_size(&op->resource[1]) >> 2), qecp->gregs + GLOB_MSIZE); /* Divide up the local QEC memory amongst the 4 QE receiver and * transmitter FIFOs. Basically it is (total / 2 / num_channels). */ - sbus_writel((qsdev->reg_addrs[1].reg_size >> 2) >> 1, + sbus_writel((resource_size(&op->resource[1]) >> 2) >> 1, qecp->gregs + GLOB_TSIZE); - sbus_writel((qsdev->reg_addrs[1].reg_size >> 2) >> 1, + sbus_writel((resource_size(&op->resource[1]) >> 2) >> 1, qecp->gregs + GLOB_RSIZE); } @@ -767,24 +775,21 @@ static u8 __devinit qec_get_burst(struct device_node *dp) return bsizes; } -static struct sunqec * __devinit get_qec(struct sbus_dev *child_sdev) +static struct sunqec * __devinit get_qec(struct of_device *child) { - struct sbus_dev *qec_sdev = child_sdev->parent; + struct of_device *op = to_of_device(child->dev.parent); struct sunqec *qecp; - for (qecp = root_qec_dev; qecp; qecp = qecp->next_module) { - if (qecp->qec_sdev == qec_sdev) - break; - } + qecp = dev_get_drvdata(&op->dev); if (!qecp) { qecp = kzalloc(sizeof(struct sunqec), GFP_KERNEL); if (qecp) { u32 ctrl; - qecp->qec_sdev = qec_sdev; - qecp->gregs = sbus_ioremap(&qec_sdev->resource[0], 0, - GLOB_REG_SIZE, - "QEC Global Registers"); + qecp->op = op; + qecp->gregs = of_ioremap(&op->resource[0], 0, + GLOB_REG_SIZE, + "QEC Global Registers"); if (!qecp->gregs) goto fail; @@ -799,16 +804,18 @@ static struct sunqec * __devinit get_qec(struct sbus_dev *child_sdev) if (qec_global_reset(qecp->gregs)) goto fail; - qecp->qec_bursts = qec_get_burst(qec_sdev->ofdev.node); + qecp->qec_bursts = qec_get_burst(op->node); - qec_init_once(qecp, qec_sdev); + qec_init_once(qecp, op); - if (request_irq(qec_sdev->irqs[0], &qec_interrupt, + if (request_irq(op->irqs[0], &qec_interrupt, IRQF_SHARED, "qec", (void *) qecp)) { printk(KERN_ERR "qec: Can't register irq.\n"); goto fail; } + dev_set_drvdata(&op->dev, qecp); + qecp->next_module = root_qec_dev; root_qec_dev = qecp; } @@ -818,17 +825,17 @@ static struct sunqec * __devinit get_qec(struct sbus_dev *child_sdev) fail: if (qecp->gregs) - sbus_iounmap(qecp->gregs, GLOB_REG_SIZE); + of_iounmap(&op->resource[0], qecp->gregs, GLOB_REG_SIZE); kfree(qecp); return NULL; } -static int __devinit qec_ether_init(struct sbus_dev *sdev) +static int __devinit qec_ether_init(struct of_device *op) { static unsigned version_printed; struct net_device *dev; - struct sunqe *qe; struct sunqec *qecp; + struct sunqe *qe; int i, res; if (version_printed++ == 0) @@ -842,49 +849,42 @@ static int __devinit qec_ether_init(struct sbus_dev *sdev) qe = netdev_priv(dev); - i = of_getintprop_default(sdev->ofdev.node, "channel#", -1); - if (i == -1) { - struct sbus_dev *td = sdev->parent->child; - i = 0; - while (td != sdev) { - td = td->next; - i++; - } - } + res = -ENODEV; + + i = of_getintprop_default(op->node, "channel#", -1); + if (i == -1) + goto fail; qe->channel = i; spin_lock_init(&qe->lock); - res = -ENODEV; - qecp = get_qec(sdev); + qecp = get_qec(op); if (!qecp) goto fail; qecp->qes[qe->channel] = qe; qe->dev = dev; qe->parent = qecp; - qe->qe_sdev = sdev; + qe->op = op; res = -ENOMEM; - qe->qcregs = sbus_ioremap(&qe->qe_sdev->resource[0], 0, - CREG_REG_SIZE, "QEC Channel Registers"); + qe->qcregs = of_ioremap(&op->resource[0], 0, + CREG_REG_SIZE, "QEC Channel Registers"); if (!qe->qcregs) { printk(KERN_ERR "qe: Cannot map channel registers.\n"); goto fail; } - qe->mregs = sbus_ioremap(&qe->qe_sdev->resource[1], 0, - MREGS_REG_SIZE, "QE MACE Registers"); + qe->mregs = of_ioremap(&op->resource[1], 0, + MREGS_REG_SIZE, "QE MACE Registers"); if (!qe->mregs) { printk(KERN_ERR "qe: Cannot map MACE registers.\n"); goto fail; } - qe->qe_block = sbus_alloc_consistent(qe->qe_sdev, - PAGE_SIZE, - &qe->qblock_dvma); - qe->buffers = sbus_alloc_consistent(qe->qe_sdev, - sizeof(struct sunqe_buffers), - &qe->buffers_dvma); + qe->qe_block = dma_alloc_coherent(&op->dev, PAGE_SIZE, + &qe->qblock_dvma, GFP_ATOMIC); + qe->buffers = dma_alloc_coherent(&op->dev, sizeof(struct sunqe_buffers), + &qe->buffers_dvma, GFP_ATOMIC); if (qe->qe_block == NULL || qe->qblock_dvma == 0 || qe->buffers == NULL || qe->buffers_dvma == 0) goto fail; @@ -892,7 +892,7 @@ static int __devinit qec_ether_init(struct sbus_dev *sdev) /* Stop this QE. */ qe_stop(qe); - SET_NETDEV_DEV(dev, &sdev->ofdev.dev); + SET_NETDEV_DEV(dev, &op->dev); dev->open = qe_open; dev->stop = qe_close; @@ -900,7 +900,7 @@ static int __devinit qec_ether_init(struct sbus_dev *sdev) dev->set_multicast_list = qe_set_multicast; dev->tx_timeout = qe_tx_timeout; dev->watchdog_timeo = 5*HZ; - dev->irq = sdev->irqs[0]; + dev->irq = op->irqs[0]; dev->dma = 0; dev->ethtool_ops = &qe_ethtool_ops; @@ -908,7 +908,7 @@ static int __devinit qec_ether_init(struct sbus_dev *sdev) if (res) goto fail; - dev_set_drvdata(&sdev->ofdev.dev, qe); + dev_set_drvdata(&op->dev, qe); printk(KERN_INFO "%s: qe channel[%d] ", dev->name, qe->channel); for (i = 0; i < 6; i++) @@ -922,58 +922,50 @@ static int __devinit qec_ether_init(struct sbus_dev *sdev) fail: if (qe->qcregs) - sbus_iounmap(qe->qcregs, CREG_REG_SIZE); + of_iounmap(&op->resource[0], qe->qcregs, CREG_REG_SIZE); if (qe->mregs) - sbus_iounmap(qe->mregs, MREGS_REG_SIZE); + of_iounmap(&op->resource[1], qe->mregs, MREGS_REG_SIZE); if (qe->qe_block) - sbus_free_consistent(qe->qe_sdev, - PAGE_SIZE, - qe->qe_block, - qe->qblock_dvma); + dma_free_coherent(&op->dev, PAGE_SIZE, + qe->qe_block, qe->qblock_dvma); if (qe->buffers) - sbus_free_consistent(qe->qe_sdev, - sizeof(struct sunqe_buffers), - qe->buffers, - qe->buffers_dvma); + dma_free_coherent(&op->dev, + sizeof(struct sunqe_buffers), + qe->buffers, + qe->buffers_dvma); free_netdev(dev); return res; } -static int __devinit qec_sbus_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit qec_sbus_probe(struct of_device *op, const struct of_device_id *match) { - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - - return qec_ether_init(sdev); + return qec_ether_init(op); } -static int __devexit qec_sbus_remove(struct of_device *dev) +static int __devexit qec_sbus_remove(struct of_device *op) { - struct sunqe *qp = dev_get_drvdata(&dev->dev); + struct sunqe *qp = dev_get_drvdata(&op->dev); struct net_device *net_dev = qp->dev; unregister_netdev(net_dev); - sbus_iounmap(qp->qcregs, CREG_REG_SIZE); - sbus_iounmap(qp->mregs, MREGS_REG_SIZE); - sbus_free_consistent(qp->qe_sdev, - PAGE_SIZE, - qp->qe_block, - qp->qblock_dvma); - sbus_free_consistent(qp->qe_sdev, - sizeof(struct sunqe_buffers), - qp->buffers, - qp->buffers_dvma); + of_iounmap(&op->resource[0], qp->qcregs, CREG_REG_SIZE); + of_iounmap(&op->resource[1], qp->mregs, MREGS_REG_SIZE); + dma_free_coherent(&op->dev, PAGE_SIZE, + qp->qe_block, qp->qblock_dvma); + dma_free_coherent(&op->dev, sizeof(struct sunqe_buffers), + qp->buffers, qp->buffers_dvma); free_netdev(net_dev); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id qec_sbus_match[] = { +static const struct of_device_id qec_sbus_match[] = { { .name = "qe", }, @@ -991,7 +983,7 @@ static struct of_platform_driver qec_sbus_driver = { static int __init qec_init(void) { - return of_register_driver(&qec_sbus_driver, &sbus_bus_type); + return of_register_driver(&qec_sbus_driver, &of_bus_type); } static void __exit qec_exit(void) @@ -1000,11 +992,11 @@ static void __exit qec_exit(void) while (root_qec_dev) { struct sunqec *next = root_qec_dev->next_module; + struct of_device *op = root_qec_dev->op; - free_irq(root_qec_dev->qec_sdev->irqs[0], - (void *) root_qec_dev); - sbus_iounmap(root_qec_dev->gregs, GLOB_REG_SIZE); - + free_irq(op->irqs[0], (void *) root_qec_dev); + of_iounmap(&op->resource[0], root_qec_dev->gregs, + GLOB_REG_SIZE); kfree(root_qec_dev); root_qec_dev = next; diff --git a/drivers/net/sunqe.h b/drivers/net/sunqe.h index 347c8ddc159..5813a7b2faa 100644 --- a/drivers/net/sunqe.h +++ b/drivers/net/sunqe.h @@ -314,7 +314,7 @@ struct sunqec { void __iomem *gregs; /* QEC Global Registers */ struct sunqe *qes[4]; /* Each child MACE */ unsigned int qec_bursts; /* Support burst sizes */ - struct sbus_dev *qec_sdev; /* QEC's SBUS device */ + struct of_device *op; /* QEC's OF device */ struct sunqec *next_module; /* List of all QECs in system */ }; @@ -342,7 +342,7 @@ struct sunqe { __u32 buffers_dvma; /* DVMA visible address. */ struct sunqec *parent; u8 mconfig; /* Base MACE mconfig value */ - struct sbus_dev *qe_sdev; /* QE's SBUS device struct */ + struct of_device *op; /* QE's OF device struct */ struct net_device *dev; /* QE's netdevice struct */ int channel; /* Who am I? */ }; diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index 6415ce15c2e..a720065553d 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -1,6 +1,6 @@ /* sunvnet.c: Sun LDOM Virtual Network Driver. * - * Copyright (C) 2007 David S. Miller <davem@davemloft.net> + * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/module.h> @@ -1260,7 +1260,7 @@ static int vnet_port_remove(struct vio_dev *vdev) return 0; } -static struct vio_device_id vnet_port_match[] = { +static const struct vio_device_id vnet_port_match[] = { { .type = "vnet-port", }, diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 4980b12b621..df20cafff7d 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -796,7 +796,7 @@ err_out_unregister_bus: mdiobus_unregister(lp->mii_bus); err_out_free_mdio_irq: kfree(lp->mii_bus->irq); -err_out_free_mii_bus; +err_out_free_mii_bus: mdiobus_free(lp->mii_bus); err_out: return err; diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index 2ae2ec40015..21efd99b929 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -205,7 +205,7 @@ config WANXL_BUILD_FIRMWARE config PC300 tristate "Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)" - depends on HDLC && PCI + depends on HDLC && PCI && BROKEN ---help--- Driver for the Cyclades-PC300 synchronous communication boards. diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index c749bdba214..3c3dd403f5d 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1794,10 +1794,10 @@ static struct xenbus_driver netfront = { static int __init netif_init(void) { - if (!is_running_on_xen()) + if (!xen_domain()) return -ENODEV; - if (is_initial_xendomain()) + if (xen_initial_domain()) return 0; printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); @@ -1809,7 +1809,7 @@ module_init(netif_init); static void __exit netif_exit(void) { - if (is_initial_xendomain()) + if (xen_initial_domain()) return; xenbus_unregister_driver(&netfront); diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 9304c455507..ed982273fb8 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -5,6 +5,7 @@ * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> + * @author Barry Kasindorf * * This is the core of the buffer management. Each * CPU buffer is processed and entered into the @@ -33,7 +34,7 @@ #include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" - + static LIST_HEAD(dying_tasks); static LIST_HEAD(dead_tasks); static cpumask_t marked_cpus = CPU_MASK_NONE; @@ -48,10 +49,11 @@ static void process_task_mortuary(void); * Can be invoked from softirq via RCU callback due to * call_rcu() of the task struct, hence the _irqsave. */ -static int task_free_notify(struct notifier_block * self, unsigned long val, void * data) +static int +task_free_notify(struct notifier_block *self, unsigned long val, void *data) { unsigned long flags; - struct task_struct * task = data; + struct task_struct *task = data; spin_lock_irqsave(&task_mortuary, flags); list_add(&task->tasks, &dying_tasks); spin_unlock_irqrestore(&task_mortuary, flags); @@ -62,13 +64,14 @@ static int task_free_notify(struct notifier_block * self, unsigned long val, voi /* The task is on its way out. A sync of the buffer means we can catch * any remaining samples for this task. */ -static int task_exit_notify(struct notifier_block * self, unsigned long val, void * data) +static int +task_exit_notify(struct notifier_block *self, unsigned long val, void *data) { /* To avoid latency problems, we only process the current CPU, * hoping that most samples for the task are on this CPU */ sync_buffer(raw_smp_processor_id()); - return 0; + return 0; } @@ -77,11 +80,12 @@ static int task_exit_notify(struct notifier_block * self, unsigned long val, voi * we don't lose any. This does not have to be exact, it's a QoI issue * only. */ -static int munmap_notify(struct notifier_block * self, unsigned long val, void * data) +static int +munmap_notify(struct notifier_block *self, unsigned long val, void *data) { unsigned long addr = (unsigned long)data; - struct mm_struct * mm = current->mm; - struct vm_area_struct * mpnt; + struct mm_struct *mm = current->mm; + struct vm_area_struct *mpnt; down_read(&mm->mmap_sem); @@ -99,11 +103,12 @@ static int munmap_notify(struct notifier_block * self, unsigned long val, void * return 0; } - + /* We need to be told about new modules so we don't attribute to a previously * loaded module, or drop the samples on the floor. */ -static int module_load_notify(struct notifier_block * self, unsigned long val, void * data) +static int +module_load_notify(struct notifier_block *self, unsigned long val, void *data) { #ifdef CONFIG_MODULES if (val != MODULE_STATE_COMING) @@ -118,7 +123,7 @@ static int module_load_notify(struct notifier_block * self, unsigned long val, v return 0; } - + static struct notifier_block task_free_nb = { .notifier_call = task_free_notify, }; @@ -135,7 +140,7 @@ static struct notifier_block module_load_nb = { .notifier_call = module_load_notify, }; - + static void end_sync(void) { end_cpu_work(); @@ -208,14 +213,14 @@ static inline unsigned long fast_get_dcookie(struct path *path) * not strictly necessary but allows oprofile to associate * shared-library samples with particular applications */ -static unsigned long get_exec_dcookie(struct mm_struct * mm) +static unsigned long get_exec_dcookie(struct mm_struct *mm) { unsigned long cookie = NO_COOKIE; - struct vm_area_struct * vma; - + struct vm_area_struct *vma; + if (!mm) goto out; - + for (vma = mm->mmap; vma; vma = vma->vm_next) { if (!vma->vm_file) continue; @@ -235,13 +240,14 @@ out: * sure to do this lookup before a mm->mmap modification happens so * we don't lose track. */ -static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, off_t * offset) +static unsigned long +lookup_dcookie(struct mm_struct *mm, unsigned long addr, off_t *offset) { unsigned long cookie = NO_COOKIE; - struct vm_area_struct * vma; + struct vm_area_struct *vma; for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) { - + if (addr < vma->vm_start || addr >= vma->vm_end) continue; @@ -263,9 +269,20 @@ static unsigned long lookup_dcookie(struct mm_struct * mm, unsigned long addr, o return cookie; } +static void increment_tail(struct oprofile_cpu_buffer *b) +{ + unsigned long new_tail = b->tail_pos + 1; + + rmb(); /* be sure fifo pointers are synchromized */ + + if (new_tail < b->buffer_size) + b->tail_pos = new_tail; + else + b->tail_pos = 0; +} static unsigned long last_cookie = INVALID_COOKIE; - + static void add_cpu_switch(int i) { add_event_entry(ESCAPE_CODE); @@ -278,16 +295,16 @@ static void add_kernel_ctx_switch(unsigned int in_kernel) { add_event_entry(ESCAPE_CODE); if (in_kernel) - add_event_entry(KERNEL_ENTER_SWITCH_CODE); + add_event_entry(KERNEL_ENTER_SWITCH_CODE); else - add_event_entry(KERNEL_EXIT_SWITCH_CODE); + add_event_entry(KERNEL_EXIT_SWITCH_CODE); } - + static void -add_user_ctx_switch(struct task_struct const * task, unsigned long cookie) +add_user_ctx_switch(struct task_struct const *task, unsigned long cookie) { add_event_entry(ESCAPE_CODE); - add_event_entry(CTX_SWITCH_CODE); + add_event_entry(CTX_SWITCH_CODE); add_event_entry(task->pid); add_event_entry(cookie); /* Another code for daemon back-compat */ @@ -296,7 +313,7 @@ add_user_ctx_switch(struct task_struct const * task, unsigned long cookie) add_event_entry(task->tgid); } - + static void add_cookie_switch(unsigned long cookie) { add_event_entry(ESCAPE_CODE); @@ -304,13 +321,78 @@ static void add_cookie_switch(unsigned long cookie) add_event_entry(cookie); } - + static void add_trace_begin(void) { add_event_entry(ESCAPE_CODE); add_event_entry(TRACE_BEGIN_CODE); } +#ifdef CONFIG_OPROFILE_IBS + +#define IBS_FETCH_CODE_SIZE 2 +#define IBS_OP_CODE_SIZE 5 +#define IBS_EIP(offset) \ + (((struct op_sample *)&cpu_buf->buffer[(offset)])->eip) +#define IBS_EVENT(offset) \ + (((struct op_sample *)&cpu_buf->buffer[(offset)])->event) + +/* + * Add IBS fetch and op entries to event buffer + */ +static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code, + int in_kernel, struct mm_struct *mm) +{ + unsigned long rip; + int i, count; + unsigned long ibs_cookie = 0; + off_t offset; + + increment_tail(cpu_buf); /* move to RIP entry */ + + rip = IBS_EIP(cpu_buf->tail_pos); + +#ifdef __LP64__ + rip += IBS_EVENT(cpu_buf->tail_pos) << 32; +#endif + + if (mm) { + ibs_cookie = lookup_dcookie(mm, rip, &offset); + + if (ibs_cookie == NO_COOKIE) + offset = rip; + if (ibs_cookie == INVALID_COOKIE) { + atomic_inc(&oprofile_stats.sample_lost_no_mapping); + offset = rip; + } + if (ibs_cookie != last_cookie) { + add_cookie_switch(ibs_cookie); + last_cookie = ibs_cookie; + } + } else + offset = rip; + + add_event_entry(ESCAPE_CODE); + add_event_entry(code); + add_event_entry(offset); /* Offset from Dcookie */ + + /* we send the Dcookie offset, but send the raw Linear Add also*/ + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); + + if (code == IBS_FETCH_CODE) + count = IBS_FETCH_CODE_SIZE; /*IBS FETCH is 2 int64s*/ + else + count = IBS_OP_CODE_SIZE; /*IBS OP is 5 int64s*/ + + for (i = 0; i < count; i++) { + increment_tail(cpu_buf); + add_event_entry(IBS_EIP(cpu_buf->tail_pos)); + add_event_entry(IBS_EVENT(cpu_buf->tail_pos)); + } +} + +#endif static void add_sample_entry(unsigned long offset, unsigned long event) { @@ -319,13 +401,13 @@ static void add_sample_entry(unsigned long offset, unsigned long event) } -static int add_us_sample(struct mm_struct * mm, struct op_sample * s) +static int add_us_sample(struct mm_struct *mm, struct op_sample *s) { unsigned long cookie; off_t offset; - - cookie = lookup_dcookie(mm, s->eip, &offset); - + + cookie = lookup_dcookie(mm, s->eip, &offset); + if (cookie == INVALID_COOKIE) { atomic_inc(&oprofile_stats.sample_lost_no_mapping); return 0; @@ -341,13 +423,13 @@ static int add_us_sample(struct mm_struct * mm, struct op_sample * s) return 1; } - + /* Add a sample to the global event buffer. If possible the * sample is converted into a persistent dentry/offset pair * for later lookup from userspace. */ static int -add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) +add_sample(struct mm_struct *mm, struct op_sample *s, int in_kernel) { if (in_kernel) { add_sample_entry(s->eip, s->event); @@ -359,9 +441,9 @@ add_sample(struct mm_struct * mm, struct op_sample * s, int in_kernel) } return 0; } - -static void release_mm(struct mm_struct * mm) + +static void release_mm(struct mm_struct *mm) { if (!mm) return; @@ -370,9 +452,9 @@ static void release_mm(struct mm_struct * mm) } -static struct mm_struct * take_tasks_mm(struct task_struct * task) +static struct mm_struct *take_tasks_mm(struct task_struct *task) { - struct mm_struct * mm = get_task_mm(task); + struct mm_struct *mm = get_task_mm(task); if (mm) down_read(&mm->mmap_sem); return mm; @@ -383,10 +465,10 @@ static inline int is_code(unsigned long val) { return val == ESCAPE_CODE; } - + /* "acquire" as many cpu buffer slots as we can */ -static unsigned long get_slots(struct oprofile_cpu_buffer * b) +static unsigned long get_slots(struct oprofile_cpu_buffer *b) { unsigned long head = b->head_pos; unsigned long tail = b->tail_pos; @@ -412,19 +494,6 @@ static unsigned long get_slots(struct oprofile_cpu_buffer * b) } -static void increment_tail(struct oprofile_cpu_buffer * b) -{ - unsigned long new_tail = b->tail_pos + 1; - - rmb(); - - if (new_tail < b->buffer_size) - b->tail_pos = new_tail; - else - b->tail_pos = 0; -} - - /* Move tasks along towards death. Any tasks on dead_tasks * will definitely have no remaining references in any * CPU buffers at this point, because we use two lists, @@ -435,8 +504,8 @@ static void process_task_mortuary(void) { unsigned long flags; LIST_HEAD(local_dead_tasks); - struct task_struct * task; - struct task_struct * ttask; + struct task_struct *task; + struct task_struct *ttask; spin_lock_irqsave(&task_mortuary, flags); @@ -493,7 +562,7 @@ void sync_buffer(int cpu) { struct oprofile_cpu_buffer *cpu_buf = &per_cpu(cpu_buffer, cpu); struct mm_struct *mm = NULL; - struct task_struct * new; + struct task_struct *new; unsigned long cookie = 0; int in_kernel = 1; unsigned int i; @@ -501,7 +570,7 @@ void sync_buffer(int cpu) unsigned long available; mutex_lock(&buffer_mutex); - + add_cpu_switch(cpu); /* Remember, only we can modify tail_pos */ @@ -509,8 +578,8 @@ void sync_buffer(int cpu) available = get_slots(cpu_buf); for (i = 0; i < available; ++i) { - struct op_sample * s = &cpu_buf->buffer[cpu_buf->tail_pos]; - + struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos]; + if (is_code(s->eip)) { if (s->event <= CPU_IS_KERNEL) { /* kernel/userspace switch */ @@ -521,8 +590,18 @@ void sync_buffer(int cpu) } else if (s->event == CPU_TRACE_BEGIN) { state = sb_bt_start; add_trace_begin(); +#ifdef CONFIG_OPROFILE_IBS + } else if (s->event == IBS_FETCH_BEGIN) { + state = sb_bt_start; + add_ibs_begin(cpu_buf, + IBS_FETCH_CODE, in_kernel, mm); + } else if (s->event == IBS_OP_BEGIN) { + state = sb_bt_start; + add_ibs_begin(cpu_buf, + IBS_OP_CODE, in_kernel, mm); +#endif } else { - struct mm_struct * oldmm = mm; + struct mm_struct *oldmm = mm; /* userspace context switch */ new = (struct task_struct *)s->event; @@ -533,13 +612,11 @@ void sync_buffer(int cpu) cookie = get_exec_dcookie(mm); add_user_ctx_switch(new, cookie); } - } else { - if (state >= sb_bt_start && - !add_sample(mm, s, in_kernel)) { - if (state == sb_bt_start) { - state = sb_bt_ignore; - atomic_inc(&oprofile_stats.bt_lost_no_mapping); - } + } else if (state >= sb_bt_start && + !add_sample(mm, s, in_kernel)) { + if (state == sb_bt_start) { + state = sb_bt_ignore; + atomic_inc(&oprofile_stats.bt_lost_no_mapping); } } diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 7ba78e6d210..e1bd5a937f6 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -5,6 +5,7 @@ * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> + * @author Barry Kasindorf <barry.kasindorf@amd.com> * * Each CPU has a local buffer that stores PC value/event * pairs. We also log context switches when we notice them. @@ -209,7 +210,7 @@ static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc, return 1; } -static int oprofile_begin_trace(struct oprofile_cpu_buffer * cpu_buf) +static int oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) { if (nr_available_slots(cpu_buf) < 4) { cpu_buf->sample_lost_overflow++; @@ -254,6 +255,75 @@ void oprofile_add_sample(struct pt_regs * const regs, unsigned long event) oprofile_add_ext_sample(pc, regs, event, is_kernel); } +#ifdef CONFIG_OPROFILE_IBS + +#define MAX_IBS_SAMPLE_SIZE 14 +static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf, + unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code) +{ + struct task_struct *task; + + cpu_buf->sample_received++; + + if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) { + cpu_buf->sample_lost_overflow++; + return 0; + } + + is_kernel = !!is_kernel; + + /* notice a switch from user->kernel or vice versa */ + if (cpu_buf->last_is_kernel != is_kernel) { + cpu_buf->last_is_kernel = is_kernel; + add_code(cpu_buf, is_kernel); + } + + /* notice a task switch */ + if (!is_kernel) { + task = current; + + if (cpu_buf->last_task != task) { + cpu_buf->last_task = task; + add_code(cpu_buf, (unsigned long)task); + } + } + + add_code(cpu_buf, ibs_code); + add_sample(cpu_buf, ibs[0], ibs[1]); + add_sample(cpu_buf, ibs[2], ibs[3]); + add_sample(cpu_buf, ibs[4], ibs[5]); + + if (ibs_code == IBS_OP_BEGIN) { + add_sample(cpu_buf, ibs[6], ibs[7]); + add_sample(cpu_buf, ibs[8], ibs[9]); + add_sample(cpu_buf, ibs[10], ibs[11]); + } + + return 1; +} + +void oprofile_add_ibs_sample(struct pt_regs *const regs, + unsigned int * const ibs_sample, u8 code) +{ + int is_kernel = !user_mode(regs); + unsigned long pc = profile_pc(regs); + + struct oprofile_cpu_buffer *cpu_buf = + &per_cpu(cpu_buffer, smp_processor_id()); + + if (!backtrace_depth) { + log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code); + return; + } + + /* if log_sample() fails we can't backtrace since we lost the source + * of this event */ + if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code)) + oprofile_ops.backtrace(regs, backtrace_depth); +} + +#endif + void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) { struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); @@ -296,7 +366,7 @@ static void wq_sync_buffer(struct work_struct *work) struct oprofile_cpu_buffer * b = container_of(work, struct oprofile_cpu_buffer, work.work); if (b->cpu != smp_processor_id()) { - printk("WQ on CPU%d, prefer CPU%d\n", + printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n", smp_processor_id(), b->cpu); } sync_buffer(b->cpu); diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index c3e366b5226..9c44d004da6 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h @@ -55,5 +55,7 @@ void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf); /* transient events for the CPU buffer -> event buffer */ #define CPU_IS_KERNEL 1 #define CPU_TRACE_BEGIN 2 +#define IBS_FETCH_BEGIN 3 +#define IBS_OP_BEGIN 4 #endif /* OPROFILE_CPU_BUFFER_H */ diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index 9d595aa91e4..065f229580d 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -26,6 +26,8 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/parport.h> @@ -34,7 +36,6 @@ #include <asm/io.h> #include <asm/oplib.h> /* OpenProm Library */ -#include <asm/sbus.h> #include <asm/dma.h> /* BPP uses LSI 64854 for DMA */ #include <asm/irq.h> #include <asm/sunbpp.h> @@ -285,38 +286,37 @@ static struct parport_operations parport_sunbpp_ops = .owner = THIS_MODULE, }; -static int __devinit init_one_port(struct sbus_dev *sdev) +static int __devinit bpp_probe(struct of_device *op, const struct of_device_id *match) { - struct parport *p; - /* at least in theory there may be a "we don't dma" case */ struct parport_operations *ops; - void __iomem *base; - int irq, dma, err = 0, size; struct bpp_regs __iomem *regs; + int irq, dma, err = 0, size; unsigned char value_tcr; + void __iomem *base; + struct parport *p; - irq = sdev->irqs[0]; - base = sbus_ioremap(&sdev->resource[0], 0, - sdev->reg_addrs[0].reg_size, - "sunbpp"); + irq = op->irqs[0]; + base = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "sunbpp"); if (!base) return -ENODEV; - size = sdev->reg_addrs[0].reg_size; + size = resource_size(&op->resource[0]); dma = PARPORT_DMA_NONE; ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL); if (!ops) goto out_unmap; - memcpy (ops, &parport_sunbpp_ops, sizeof (struct parport_operations)); + memcpy (ops, &parport_sunbpp_ops, sizeof(struct parport_operations)); dprintk(("register_port\n")); if (!(p = parport_register_port((unsigned long)base, irq, dma, ops))) goto out_free_ops; p->size = size; - p->dev = &sdev->ofdev.dev; + p->dev = &op->dev; if ((err = request_irq(p->irq, parport_irq_handler, IRQF_SHARED, p->name, p)) != 0) { @@ -333,7 +333,7 @@ static int __devinit init_one_port(struct sbus_dev *sdev) printk(KERN_INFO "%s: sunbpp at 0x%lx\n", p->name, p->base); - dev_set_drvdata(&sdev->ofdev.dev, p); + dev_set_drvdata(&op->dev, p); parport_announce_port(p); @@ -346,21 +346,14 @@ out_free_ops: kfree(ops); out_unmap: - sbus_iounmap(base, size); + of_iounmap(&op->resource[0], base, size); return err; } -static int __devinit bpp_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - - return init_one_port(sdev); -} - -static int __devexit bpp_remove(struct of_device *dev) +static int __devexit bpp_remove(struct of_device *op) { - struct parport *p = dev_get_drvdata(&dev->dev); + struct parport *p = dev_get_drvdata(&op->dev); struct parport_operations *ops = p->ops; parport_remove_port(p); @@ -370,16 +363,16 @@ static int __devexit bpp_remove(struct of_device *dev) free_irq(p->irq, p); } - sbus_iounmap((void __iomem *) p->base, p->size); + of_iounmap(&op->resource[0], (void __iomem *) p->base, p->size); parport_put_port(p); kfree(ops); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id bpp_match[] = { +static const struct of_device_id bpp_match[] = { { .name = "SUNW,bpp", }, @@ -397,7 +390,7 @@ static struct of_platform_driver bpp_sbus_driver = { static int __init parport_sunbpp_init(void) { - return of_register_driver(&bpp_sbus_driver, &sbus_bus_type); + return of_register_driver(&bpp_sbus_driver, &of_bus_type); } static void __exit parport_sunbpp_exit(void) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 1982f8b4278..63bb5791044 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -58,7 +58,7 @@ config BATTERY_TOSA config BATTERY_WM97XX bool "WM97xx generic battery driver" - depends on TOUCHSCREEN_WM97XX + depends on TOUCHSCREEN_WM97XX=y help Say Y to enable support for battery measured by WM97xx aux port. diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9a9755c92fa..b57fba5c6d0 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -329,7 +329,7 @@ comment "Platform RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS'" - depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS + depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64 default y if X86 help Say "yes" here to get direct support for the real time clock @@ -406,14 +406,26 @@ config RTC_DRV_M48T86 will be called rtc-m48t86. config RTC_DRV_M48T59 - tristate "ST M48T59" + tristate "ST M48T59/M48T08/M48T02" help If you say Y here you will get support for the - ST M48T59 RTC chip. + ST M48T59 RTC chip and compatible ST M48T08 and M48T02. + + These chips are usually found in Sun SPARC and UltraSPARC + workstations. This driver can also be built as a module, if so, the module will be called "rtc-m48t59". +config RTC_DRV_BQ4802 + tristate "TI BQ4802" + help + If you say Y here you will get support for the TI + BQ4802 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-bq4802. + config RTC_DRV_V3020 tristate "EM Microelectronic V3020" help @@ -583,4 +595,18 @@ config RTC_DRV_PPC the RTC. This exposes that functionality through the generic RTC class. +config RTC_DRV_SUN4V + bool "SUN4V Hypervisor RTC" + depends on SPARC64 + help + If you say Y here you will get support for the Hypervisor + based RTC on SUN4V systems. + +config RTC_DRV_STARFIRE + bool "Starfire RTC" + depends on SPARC64 + help + If you say Y here you will get support for the RTC found on + Starfire systems. + endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 18622ef84ca..10f41f85c38 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -38,6 +38,9 @@ obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o +obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o +obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c new file mode 100644 index 00000000000..189a018bdf3 --- /dev/null +++ b/drivers/rtc/rtc-bq4802.c @@ -0,0 +1,230 @@ +/* rtc-bq4802.c: TI BQ4802 RTC driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/bcd.h> + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("TI BQ4802 RTC driver"); +MODULE_LICENSE("GPL"); + +struct bq4802 { + void __iomem *regs; + unsigned long ioport; + struct rtc_device *rtc; + spinlock_t lock; + struct resource *r; + u8 (*read)(struct bq4802 *, int); + void (*write)(struct bq4802 *, int, u8); +}; + +static u8 bq4802_read_io(struct bq4802 *p, int off) +{ + return inb(p->ioport + off); +} + +static void bq4802_write_io(struct bq4802 *p, int off, u8 val) +{ + outb(val, p->ioport + off); +} + +static u8 bq4802_read_mem(struct bq4802 *p, int off) +{ + return readb(p->regs + off); +} + +static void bq4802_write_mem(struct bq4802 *p, int off, u8 val) +{ + writeb(val, p->regs + off); +} + +static int bq4802_read_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bq4802 *p = platform_get_drvdata(pdev); + unsigned long flags; + unsigned int century; + u8 val; + + spin_lock_irqsave(&p->lock, flags); + + val = p->read(p, 0x0e); + p->write(p, 0xe, val | 0x08); + + tm->tm_sec = p->read(p, 0x00); + tm->tm_min = p->read(p, 0x02); + tm->tm_hour = p->read(p, 0x04); + tm->tm_mday = p->read(p, 0x06); + tm->tm_mon = p->read(p, 0x09); + tm->tm_year = p->read(p, 0x0a); + tm->tm_wday = p->read(p, 0x08); + century = p->read(p, 0x0f); + + p->write(p, 0x0e, val); + + spin_unlock_irqrestore(&p->lock, flags); + + BCD_TO_BIN(tm->tm_sec); + BCD_TO_BIN(tm->tm_min); + BCD_TO_BIN(tm->tm_hour); + BCD_TO_BIN(tm->tm_mday); + BCD_TO_BIN(tm->tm_mon); + BCD_TO_BIN(tm->tm_year); + BCD_TO_BIN(tm->tm_wday); + BCD_TO_BIN(century); + + tm->tm_year += (century * 100); + tm->tm_year -= 1900; + + tm->tm_mon--; + + return 0; +} + +static int bq4802_set_time(struct device *dev, struct rtc_time *tm) +{ + struct platform_device *pdev = to_platform_device(dev); + struct bq4802 *p = platform_get_drvdata(pdev); + u8 sec, min, hrs, day, mon, yrs, century, val; + unsigned long flags; + unsigned int year; + + year = tm->tm_year + 1900; + century = year / 100; + yrs = year % 100; + + mon = tm->tm_mon + 1; /* tm_mon starts at zero */ + day = tm->tm_mday; + hrs = tm->tm_hour; + min = tm->tm_min; + sec = tm->tm_sec; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hrs); + BIN_TO_BCD(day); + BIN_TO_BCD(mon); + BIN_TO_BCD(yrs); + BIN_TO_BCD(century); + + spin_lock_irqsave(&p->lock, flags); + + val = p->read(p, 0x0e); + p->write(p, 0x0e, val | 0x08); + + p->write(p, 0x00, sec); + p->write(p, 0x02, min); + p->write(p, 0x04, hrs); + p->write(p, 0x06, day); + p->write(p, 0x09, mon); + p->write(p, 0x0a, yrs); + p->write(p, 0x0f, century); + + p->write(p, 0x0e, val); + + spin_unlock_irqrestore(&p->lock, flags); + + return 0; +} + +static const struct rtc_class_ops bq4802_ops = { + .read_time = bq4802_read_time, + .set_time = bq4802_set_time, +}; + +static int __devinit bq4802_probe(struct platform_device *pdev) +{ + struct bq4802 *p = kzalloc(sizeof(*p), GFP_KERNEL); + int err = -ENOMEM; + + if (!p) + goto out; + + spin_lock_init(&p->lock); + + p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!p->r) { + p->r = platform_get_resource(pdev, IORESOURCE_IO, 0); + err = -EINVAL; + if (!p->r) + goto out_free; + } + if (p->r->flags & IORESOURCE_IO) { + p->ioport = p->r->start; + p->read = bq4802_read_io; + p->write = bq4802_write_io; + } else if (p->r->flags & IORESOURCE_MEM) { + p->regs = ioremap(p->r->start, resource_size(p->r)); + p->read = bq4802_read_mem; + p->write = bq4802_write_mem; + } else { + err = -EINVAL; + goto out_free; + } + + p->rtc = rtc_device_register("bq4802", &pdev->dev, + &bq4802_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + err = PTR_ERR(p->rtc); + goto out_iounmap; + } + + platform_set_drvdata(pdev, p); + err = 0; +out: + return err; + +out_iounmap: + if (p->r->flags & IORESOURCE_MEM) + iounmap(p->regs); +out_free: + kfree(p); + goto out; +} + +static int __devexit bq4802_remove(struct platform_device *pdev) +{ + struct bq4802 *p = platform_get_drvdata(pdev); + + rtc_device_unregister(p->rtc); + if (p->r->flags & IORESOURCE_MEM) + iounmap(p->regs); + + platform_set_drvdata(pdev, NULL); + + kfree(p); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc-bq4802"); + +static struct platform_driver bq4802_driver = { + .driver = { + .name = "rtc-bq4802", + .owner = THIS_MODULE, + }, + .probe = bq4802_probe, + .remove = __devexit_p(bq4802_remove), +}; + +static int __init bq4802_init(void) +{ + return platform_driver_register(&bq4802_driver); +} + +static void __exit bq4802_exit(void) +{ + platform_driver_unregister(&bq4802_driver); +} + +module_init(bq4802_init); +module_exit(bq4802_exit); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index b184367637d..b23af0c2a86 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -636,7 +636,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) */ #if defined(CONFIG_ATARI) address_space = 64; -#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__sparc__) address_space = 128; #else #warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes. @@ -699,7 +699,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) /* FIXME teach the alarm code how to handle binary mode; * <asm-generic/rtc.h> doesn't know 12-hour mode either. */ - if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) { + if (is_valid_irq(rtc_irq) && + (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))) { dev_dbg(dev, "only 24-hr BCD mode supported\n"); retval = -ENXIO; goto cleanup1; diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c index 013e6c103b9..ce4eff6a8d5 100644 --- a/drivers/rtc/rtc-m48t59.c +++ b/drivers/rtc/rtc-m48t59.c @@ -24,8 +24,9 @@ #define NO_IRQ (-1) #endif -#define M48T59_READ(reg) pdata->read_byte(dev, reg) -#define M48T59_WRITE(val, reg) pdata->write_byte(dev, reg, val) +#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg)) +#define M48T59_WRITE(val, reg) \ + (pdata->write_byte(dev, pdata->offset + reg, val)) #define M48T59_SET_BITS(mask, reg) \ M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg)) @@ -34,7 +35,6 @@ struct m48t59_private { void __iomem *ioaddr; - unsigned int size; /* iomem size */ int irq; struct rtc_device *rtc; spinlock_t lock; /* serialize the NVRAM and RTC access */ @@ -82,7 +82,8 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm) tm->tm_mday = BCD2BIN(M48T59_READ(M48T59_MDAY)); val = M48T59_READ(M48T59_WDAY); - if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { + if ((pdata->type == M48T59RTC_TYPE_M48T59) && + (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) { dev_dbg(dev, "Century bit is enabled\n"); tm->tm_year += 100; /* one century */ } @@ -126,7 +127,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) M48T59_WRITE((BIN2BCD(tm->tm_mon + 1) & 0x1F), M48T59_MONTH); M48T59_WRITE(BIN2BCD(tm->tm_year % 100), M48T59_YEAR); - if (tm->tm_year/100) + if (pdata->type == M48T59RTC_TYPE_M48T59 && (tm->tm_year / 100)) val = (M48T59_WDAY_CEB | M48T59_WDAY_CB); val |= (BIN2BCD(tm->tm_wday) & 0x07); M48T59_WRITE(val, M48T59_WDAY); @@ -310,6 +311,11 @@ static const struct rtc_class_ops m48t59_rtc_ops = { .proc = m48t59_rtc_proc, }; +static const struct rtc_class_ops m48t02_rtc_ops = { + .read_time = m48t59_rtc_read_time, + .set_time = m48t59_rtc_set_time, +}; + static ssize_t m48t59_nvram_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t size) @@ -321,7 +327,7 @@ static ssize_t m48t59_nvram_read(struct kobject *kobj, ssize_t cnt = 0; unsigned long flags; - for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + for (; size > 0 && pos < pdata->offset; cnt++, size--) { spin_lock_irqsave(&m48t59->lock, flags); *buf++ = M48T59_READ(cnt); spin_unlock_irqrestore(&m48t59->lock, flags); @@ -341,7 +347,7 @@ static ssize_t m48t59_nvram_write(struct kobject *kobj, ssize_t cnt = 0; unsigned long flags; - for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) { + for (; size > 0 && pos < pdata->offset; cnt++, size--) { spin_lock_irqsave(&m48t59->lock, flags); M48T59_WRITE(*buf++, cnt); spin_unlock_irqrestore(&m48t59->lock, flags); @@ -358,7 +364,6 @@ static struct bin_attribute m48t59_nvram_attr = { }, .read = m48t59_nvram_read, .write = m48t59_nvram_write, - .size = M48T59_NVRAM_SIZE, }; static int __devinit m48t59_rtc_probe(struct platform_device *pdev) @@ -367,6 +372,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) struct m48t59_private *m48t59 = NULL; struct resource *res; int ret = -ENOMEM; + char *name; + const struct rtc_class_ops *ops; /* This chip could be memory-mapped or I/O-mapped */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -391,6 +398,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) /* Ensure we only kmalloc platform data once */ pdev->dev.platform_data = pdata; } + if (!pdata->type) + pdata->type = M48T59RTC_TYPE_M48T59; /* Try to use the generic memory read/write ops */ if (!pdata->write_byte) @@ -403,10 +412,14 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (!m48t59) return -ENOMEM; - m48t59->size = res->end - res->start + 1; - m48t59->ioaddr = ioremap(res->start, m48t59->size); - if (!m48t59->ioaddr) - goto out; + m48t59->ioaddr = pdata->ioaddr; + + if (!m48t59->ioaddr) { + /* ioaddr not mapped externally */ + m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1); + if (!m48t59->ioaddr) + goto out; + } /* Try to get irq number. We also can work in * the mode without IRQ. @@ -421,14 +434,36 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev) if (ret) goto out; } + switch (pdata->type) { + case M48T59RTC_TYPE_M48T59: + name = "m48t59"; + ops = &m48t59_rtc_ops; + pdata->offset = 0x1ff0; + break; + case M48T59RTC_TYPE_M48T02: + name = "m48t02"; + ops = &m48t02_rtc_ops; + pdata->offset = 0x7f0; + break; + case M48T59RTC_TYPE_M48T08: + name = "m48t08"; + ops = &m48t02_rtc_ops; + pdata->offset = 0x1ff0; + break; + default: + dev_err(&pdev->dev, "Unknown RTC type\n"); + ret = -ENODEV; + goto out; + } - m48t59->rtc = rtc_device_register("m48t59", &pdev->dev, - &m48t59_rtc_ops, THIS_MODULE); + m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE); if (IS_ERR(m48t59->rtc)) { ret = PTR_ERR(m48t59->rtc); goto out; } + m48t59_nvram_attr.size = pdata->offset; + ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); if (ret) goto out; @@ -452,11 +487,12 @@ out: static int __devexit m48t59_rtc_remove(struct platform_device *pdev) { struct m48t59_private *m48t59 = platform_get_drvdata(pdev); + struct m48t59_plat_data *pdata = pdev->dev.platform_data; sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr); if (!IS_ERR(m48t59->rtc)) rtc_device_unregister(m48t59->rtc); - if (m48t59->ioaddr) + if (m48t59->ioaddr && !pdata->ioaddr) iounmap(m48t59->ioaddr); if (m48t59->irq != NO_IRQ) free_irq(m48t59->irq, &pdev->dev); @@ -491,5 +527,5 @@ module_init(m48t59_rtc_init); module_exit(m48t59_rtc_exit); MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>"); -MODULE_DESCRIPTION("M48T59 RTC driver"); +MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c new file mode 100644 index 00000000000..7ccb0dd700a --- /dev/null +++ b/drivers/rtc/rtc-starfire.c @@ -0,0 +1,120 @@ +/* rtc-starfire.c: Starfire platform RTC driver. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +#include <asm/oplib.h> + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("Starfire RTC driver"); +MODULE_LICENSE("GPL"); + +struct starfire_rtc { + struct rtc_device *rtc; + spinlock_t lock; +}; + +static u32 starfire_get_time(void) +{ + static char obp_gettod[32]; + static u32 unix_tod; + + sprintf(obp_gettod, "h# %08x unix-gettod", + (unsigned int) (long) &unix_tod); + prom_feval(obp_gettod); + + return unix_tod; +} + +static int starfire_read_time(struct device *dev, struct rtc_time *tm) +{ + struct starfire_rtc *p = dev_get_drvdata(dev); + unsigned long flags, secs; + + spin_lock_irqsave(&p->lock, flags); + secs = starfire_get_time(); + spin_unlock_irqrestore(&p->lock, flags); + + rtc_time_to_tm(secs, tm); + + return 0; +} + +static int starfire_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned long secs; + int err; + + err = rtc_tm_to_time(tm, &secs); + if (err) + return err; + + /* Do nothing, time is set using the service processor + * console on this platform. + */ + return 0; +} + +static const struct rtc_class_ops starfire_rtc_ops = { + .read_time = starfire_read_time, + .set_time = starfire_set_time, +}; + +static int __devinit starfire_rtc_probe(struct platform_device *pdev) +{ + struct starfire_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + spin_lock_init(&p->lock); + + p->rtc = rtc_device_register("starfire", &pdev->dev, + &starfire_rtc_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + int err = PTR_ERR(p->rtc); + kfree(p); + return err; + } + platform_set_drvdata(pdev, p); + return 0; +} + +static int __devexit starfire_rtc_remove(struct platform_device *pdev) +{ + struct starfire_rtc *p = platform_get_drvdata(pdev); + + rtc_device_unregister(p->rtc); + kfree(p); + + return 0; +} + +static struct platform_driver starfire_rtc_driver = { + .driver = { + .name = "rtc-starfire", + .owner = THIS_MODULE, + }, + .probe = starfire_rtc_probe, + .remove = __devexit_p(starfire_rtc_remove), +}; + +static int __init starfire_rtc_init(void) +{ + return platform_driver_register(&starfire_rtc_driver); +} + +static void __exit starfire_rtc_exit(void) +{ + platform_driver_unregister(&starfire_rtc_driver); +} + +module_init(starfire_rtc_init); +module_exit(starfire_rtc_exit); diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c new file mode 100644 index 00000000000..2012ccbb4a5 --- /dev/null +++ b/drivers/rtc/rtc-sun4v.c @@ -0,0 +1,153 @@ +/* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems. + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +#include <asm/hypervisor.h> + +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); +MODULE_DESCRIPTION("SUN4V RTC driver"); +MODULE_LICENSE("GPL"); + +struct sun4v_rtc { + struct rtc_device *rtc; + spinlock_t lock; +}; + +static unsigned long hypervisor_get_time(void) +{ + unsigned long ret, time; + int retries = 10000; + +retry: + ret = sun4v_tod_get(&time); + if (ret == HV_EOK) + return time; + if (ret == HV_EWOULDBLOCK) { + if (--retries > 0) { + udelay(100); + goto retry; + } + printk(KERN_WARNING "SUN4V: tod_get() timed out.\n"); + return 0; + } + printk(KERN_WARNING "SUN4V: tod_get() not supported.\n"); + return 0; +} + +static int sun4v_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sun4v_rtc *p = dev_get_drvdata(dev); + unsigned long flags, secs; + + spin_lock_irqsave(&p->lock, flags); + secs = hypervisor_get_time(); + spin_unlock_irqrestore(&p->lock, flags); + + rtc_time_to_tm(secs, tm); + + return 0; +} + +static int hypervisor_set_time(unsigned long secs) +{ + unsigned long ret; + int retries = 10000; + +retry: + ret = sun4v_tod_set(secs); + if (ret == HV_EOK) + return 0; + if (ret == HV_EWOULDBLOCK) { + if (--retries > 0) { + udelay(100); + goto retry; + } + printk(KERN_WARNING "SUN4V: tod_set() timed out.\n"); + return -EAGAIN; + } + printk(KERN_WARNING "SUN4V: tod_set() not supported.\n"); + return -EOPNOTSUPP; +} + +static int sun4v_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sun4v_rtc *p = dev_get_drvdata(dev); + unsigned long flags, secs; + int err; + + err = rtc_tm_to_time(tm, &secs); + if (err) + return err; + + spin_lock_irqsave(&p->lock, flags); + err = hypervisor_set_time(secs); + spin_unlock_irqrestore(&p->lock, flags); + + return err; +} + +static const struct rtc_class_ops sun4v_rtc_ops = { + .read_time = sun4v_read_time, + .set_time = sun4v_set_time, +}; + +static int __devinit sun4v_rtc_probe(struct platform_device *pdev) +{ + struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL); + + if (!p) + return -ENOMEM; + + spin_lock_init(&p->lock); + + p->rtc = rtc_device_register("sun4v", &pdev->dev, + &sun4v_rtc_ops, THIS_MODULE); + if (IS_ERR(p->rtc)) { + int err = PTR_ERR(p->rtc); + kfree(p); + return err; + } + platform_set_drvdata(pdev, p); + return 0; +} + +static int __devexit sun4v_rtc_remove(struct platform_device *pdev) +{ + struct sun4v_rtc *p = platform_get_drvdata(pdev); + + rtc_device_unregister(p->rtc); + kfree(p); + + return 0; +} + +static struct platform_driver sun4v_rtc_driver = { + .driver = { + .name = "rtc-sun4v", + .owner = THIS_MODULE, + }, + .probe = sun4v_rtc_probe, + .remove = __devexit_p(sun4v_rtc_remove), +}; + +static int __init sun4v_rtc_init(void) +{ + return platform_driver_register(&sun4v_rtc_driver); +} + +static void __exit sun4v_rtc_exit(void) +{ + platform_driver_unregister(&sun4v_rtc_driver); +} + +module_init(sun4v_rtc_init); +module_exit(sun4v_rtc_exit); diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c index d18e6d2e0b4..40759c33477 100644 --- a/drivers/s390/char/fs3270.c +++ b/drivers/s390/char/fs3270.c @@ -418,25 +418,22 @@ fs3270_open(struct inode *inode, struct file *filp) { struct fs3270 *fp; struct idal_buffer *ib; - int minor, rc; + int minor, rc = 0; if (imajor(filp->f_path.dentry->d_inode) != IBM_FS3270_MAJOR) return -ENODEV; - lock_kernel(); minor = iminor(filp->f_path.dentry->d_inode); /* Check for minor 0 multiplexer. */ if (minor == 0) { - struct tty_struct *tty; - mutex_lock(&tty_mutex); - tty = get_current_tty(); + struct tty_struct *tty = get_current_tty(); if (!tty || tty->driver->major != IBM_TTY3270_MAJOR) { - mutex_unlock(&tty_mutex); - rc = -ENODEV; - goto out; + tty_kref_put(tty); + return -ENODEV; } minor = tty->index + RAW3270_FIRSTMINOR; - mutex_unlock(&tty_mutex); + tty_kref_put(tty); } + lock_kernel(); /* Check if some other program is already using fullscreen mode. */ fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); if (!IS_ERR(fp)) { @@ -478,7 +475,7 @@ fs3270_open(struct inode *inode, struct file *filp) filp->private_data = fp; out: unlock_kernel(); - return 0; + return rc; } /* diff --git a/drivers/sbus/Makefile b/drivers/sbus/Makefile index 7b1d24d9530..e94dc25805f 100644 --- a/drivers/sbus/Makefile +++ b/drivers/sbus/Makefile @@ -2,8 +2,4 @@ # Makefile for the linux kernel. # -ifneq ($(ARCH),m68k) -obj-y := sbus.o dvma.o -endif - obj-$(CONFIG_SBUSCHAR) += char/ diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig index 400c65bfb8c..73cde85d04d 100644 --- a/drivers/sbus/char/Kconfig +++ b/drivers/sbus/char/Kconfig @@ -13,16 +13,6 @@ config SUN_OPENPROMIO If unsure, say Y. -config SUN_MOSTEK_RTC - tristate "Mostek real time clock support" - depends on SPARC32 - help - The Mostek RTC chip is used on all known Sun computers except - some JavaStations. For a JavaStation you need to say Y both here - and to "Enhanced Real Time Clock Support". - - Say Y here unless you are building a special purpose kernel. - config OBP_FLASH tristate "OBP Flash Device support" depends on SPARC64 @@ -30,26 +20,9 @@ config OBP_FLASH The OpenBoot PROM on Ultra systems is flashable. If you want to be able to upgrade the OBP firmware, say Y here. -config SUN_BPP - tristate "Bidirectional parallel port support (OBSOLETE)" - depends on EXPERIMENTAL - help - Say Y here to support Sun's obsolete variant of IEEE1284 - bidirectional parallel port protocol as /dev/bppX. Can be built on - x86 machines. - -config SUN_VIDEOPIX - tristate "Videopix Frame Grabber (EXPERIMENTAL)" - depends on EXPERIMENTAL && (BROKEN || !64BIT) - help - Say Y here to support the Videopix Frame Grabber from Sun - Microsystems, commonly found on SPARCstations. This card, which is - based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and - SVIDEO signals. - config TADPOLE_TS102_UCTRL tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)" - depends on EXPERIMENTAL && SPARC32 + depends on EXPERIMENTAL help Say Y here to directly support the TS102 Microcontroller interface on the Tadpole Sparcbook 3. This device handles power-management diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index 7ab060e9a5f..78b6183c986 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -7,18 +7,12 @@ # Rewritten to use lists instead of if-statements. # -vfc-objs := vfc_dev.o vfc_i2c.o bbc-objs := bbc_i2c.o bbc_envctrl.o obj-$(CONFIG_ENVCTRL) += envctrl.o obj-$(CONFIG_DISPLAY7SEG) += display7seg.o -obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwatchdog.o -obj-$(CONFIG_WATCHDOG_RIO) += riowatchdog.o obj-$(CONFIG_OBP_FLASH) += flash.o obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o -obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o -obj-$(CONFIG_SUN_BPP) += bpp.o -obj-$(CONFIG_SUN_VIDEOPIX) += vfc.o obj-$(CONFIG_TADPOLE_TS102_UCTRL) += uctrl.o obj-$(CONFIG_SUN_JSFLASH) += jsflash.o obj-$(CONFIG_BBC_I2C) += bbc.o diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c index 0bde26989a2..15dab96d05e 100644 --- a/drivers/sbus/char/bbc_envctrl.c +++ b/drivers/sbus/char/bbc_envctrl.c @@ -1,15 +1,15 @@ -/* $Id: bbc_envctrl.c,v 1.4 2001/04/06 16:48:08 davem Exp $ - * bbc_envctrl.c: UltraSPARC-III environment control driver. +/* bbc_envctrl.c: UltraSPARC-III environment control driver. * - * Copyright (C) 2001 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kthread.h> #include <linux/delay.h> #include <linux/kmod.h> #include <linux/reboot.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/oplib.h> -#include <asm/ebus.h> #include "bbc_i2c.h" #include "max1617.h" @@ -75,43 +75,8 @@ static struct temp_limits amb_temp_limits[2] = { { 65, 55, 40, 5, -5, -10 }, }; -enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX }; - -struct bbc_cpu_temperature { - struct bbc_cpu_temperature *next; - - struct bbc_i2c_client *client; - int index; - - /* Current readings, and history. */ - s8 curr_cpu_temp; - s8 curr_amb_temp; - s8 prev_cpu_temp; - s8 prev_amb_temp; - s8 avg_cpu_temp; - s8 avg_amb_temp; - - int sample_tick; - - enum fan_action fan_todo[2]; -#define FAN_AMBIENT 0 -#define FAN_CPU 1 -}; - -struct bbc_cpu_temperature *all_bbc_temps; - -struct bbc_fan_control { - struct bbc_fan_control *next; - - struct bbc_i2c_client *client; - int index; - - int psupply_fan_on; - int cpu_fan_speed; - int system_fan_speed; -}; - -struct bbc_fan_control *all_bbc_fans; +static LIST_HEAD(all_temps); +static LIST_HEAD(all_fans); #define CPU_FAN_REG 0xf0 #define SYS_FAN_REG 0xf2 @@ -330,7 +295,7 @@ static enum fan_action prioritize_fan_action(int which_fan) * recommend we do, and perform that action on all the * fans. */ - for (tp = all_bbc_temps; tp; tp = tp->next) { + list_for_each_entry(tp, &all_temps, glob_list) { if (tp->fan_todo[which_fan] == FAN_FULLBLAST) { decision = FAN_FULLBLAST; break; @@ -439,7 +404,7 @@ static void fans_full_blast(void) /* Since we will not be monitoring things anymore, put * the fans on full blast. */ - for (fp = all_bbc_fans; fp; fp = fp->next) { + list_for_each_entry(fp, &all_fans, glob_list) { fp->cpu_fan_speed = FAN_SPEED_MAX; fp->system_fan_speed = FAN_SPEED_MAX; fp->psupply_fan_on = 1; @@ -463,11 +428,11 @@ static int kenvctrld(void *__unused) if (kthread_should_stop()) break; - for (tp = all_bbc_temps; tp; tp = tp->next) { + list_for_each_entry(tp, &all_temps, glob_list) { get_current_temps(tp); analyze_temps(tp, &last_warning_jiffies); } - for (fp = all_bbc_fans; fp; fp = fp->next) + list_for_each_entry(fp, &all_fans, glob_list) maybe_new_fan_speeds(fp); } printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n"); @@ -477,7 +442,8 @@ static int kenvctrld(void *__unused) return 0; } -static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx) +static void attach_one_temp(struct bbc_i2c_bus *bp, struct of_device *op, + int temp_idx) { struct bbc_cpu_temperature *tp; @@ -485,20 +451,17 @@ static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx) if (!tp) return; - tp->client = bbc_i2c_attach(echild); + tp->client = bbc_i2c_attach(bp, op); if (!tp->client) { kfree(tp); return; } + tp->index = temp_idx; - { - struct bbc_cpu_temperature **tpp = &all_bbc_temps; - while (*tpp) - tpp = &((*tpp)->next); - tp->next = NULL; - *tpp = tp; - } + + list_add(&tp->glob_list, &all_temps); + list_add(&tp->bp_list, &bp->temps); /* Tell it to convert once every 5 seconds, clear all cfg * bits. @@ -524,7 +487,8 @@ static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx) tp->fan_todo[FAN_CPU] = FAN_SAME; } -static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx) +static void attach_one_fan(struct bbc_i2c_bus *bp, struct of_device *op, + int fan_idx) { struct bbc_fan_control *fp; @@ -532,7 +496,7 @@ static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx) if (!fp) return; - fp->client = bbc_i2c_attach(echild); + fp->client = bbc_i2c_attach(bp, op); if (!fp->client) { kfree(fp); return; @@ -540,13 +504,8 @@ static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx) fp->index = fan_idx; - { - struct bbc_fan_control **fpp = &all_bbc_fans; - while (*fpp) - fpp = &((*fpp)->next); - fp->next = NULL; - *fpp = fp; - } + list_add(&fp->glob_list, &all_fans); + list_add(&fp->bp_list, &bp->fans); /* The i2c device controlling the fans is write-only. * So the only way to keep track of the current power @@ -563,18 +522,18 @@ static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx) set_fan_speeds(fp); } -int bbc_envctrl_init(void) +int bbc_envctrl_init(struct bbc_i2c_bus *bp) { - struct linux_ebus_child *echild; + struct of_device *op; int temp_index = 0; int fan_index = 0; int devidx = 0; - while ((echild = bbc_i2c_getdev(devidx++)) != NULL) { - if (!strcmp(echild->prom_node->name, "temperature")) - attach_one_temp(echild, temp_index++); - if (!strcmp(echild->prom_node->name, "fan-control")) - attach_one_fan(echild, fan_index++); + while ((op = bbc_i2c_getdev(bp, devidx++)) != NULL) { + if (!strcmp(op->node->name, "temperature")) + attach_one_temp(bp, op, temp_index++); + if (!strcmp(op->node->name, "fan-control")) + attach_one_fan(bp, op, fan_index++); } if (temp_index != 0 && fan_index != 0) { kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld"); @@ -597,26 +556,22 @@ static void destroy_one_fan(struct bbc_fan_control *fp) kfree(fp); } -void bbc_envctrl_cleanup(void) +void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp) { - struct bbc_cpu_temperature *tp; - struct bbc_fan_control *fp; + struct bbc_cpu_temperature *tp, *tpos; + struct bbc_fan_control *fp, *fpos; kthread_stop(kenvctrld_task); - tp = all_bbc_temps; - while (tp != NULL) { - struct bbc_cpu_temperature *next = tp->next; + list_for_each_entry_safe(tp, tpos, &bp->temps, bp_list) { + list_del(&tp->bp_list); + list_del(&tp->glob_list); destroy_one_temp(tp); - tp = next; } - all_bbc_temps = NULL; - fp = all_bbc_fans; - while (fp != NULL) { - struct bbc_fan_control *next = fp->next; + list_for_each_entry_safe(fp, fpos, &bp->fans, bp_list) { + list_del(&fp->bp_list); + list_del(&fp->glob_list); destroy_one_fan(fp); - fp = next; } - all_bbc_fans = NULL; } diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index ac8ef2ce07f..f08e169ba1b 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -1,8 +1,7 @@ -/* $Id: bbc_i2c.c,v 1.2 2001/04/02 09:59:08 davem Exp $ - * bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III +/* bbc_i2c.c: I2C low-level driver for BBC device on UltraSPARC-III * platforms. * - * Copyright (C) 2001 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> @@ -14,9 +13,8 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/interrupt.h> -#include <asm/oplib.h> -#include <asm/ebus.h> -#include <asm/spitfire.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/bbc.h> #include <asm/io.h> @@ -53,54 +51,12 @@ * The second controller also connects to the smartcard reader, if present. */ -#define NUM_CHILDREN 8 -struct bbc_i2c_bus { - struct bbc_i2c_bus *next; - int index; - spinlock_t lock; - void __iomem *i2c_bussel_reg; - void __iomem *i2c_control_regs; - unsigned char own, clock; - - wait_queue_head_t wq; - volatile int waiting; - - struct linux_ebus_device *bus_edev; - struct { - struct linux_ebus_child *device; - int client_claimed; - } devs[NUM_CHILDREN]; -}; - -static struct bbc_i2c_bus *all_bbc_i2c; - -struct bbc_i2c_client { - struct bbc_i2c_bus *bp; - struct linux_ebus_child *echild; - int bus; - int address; -}; - -static int find_device(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild) +static void set_device_claimage(struct bbc_i2c_bus *bp, struct of_device *op, int val) { int i; for (i = 0; i < NUM_CHILDREN; i++) { - if (bp->devs[i].device == echild) { - if (bp->devs[i].client_claimed) - return 0; - return 1; - } - } - return 0; -} - -static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child *echild, int val) -{ - int i; - - for (i = 0; i < NUM_CHILDREN; i++) { - if (bp->devs[i].device == echild) { + if (bp->devs[i].device == op) { bp->devs[i].client_claimed = val; return; } @@ -110,61 +66,47 @@ static void set_device_claimage(struct bbc_i2c_bus *bp, struct linux_ebus_child #define claim_device(BP,ECHILD) set_device_claimage(BP,ECHILD,1) #define release_device(BP,ECHILD) set_device_claimage(BP,ECHILD,0) -static struct bbc_i2c_bus *find_bus_for_device(struct linux_ebus_child *echild) +struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *bp, int index) { - struct bbc_i2c_bus *bp = all_bbc_i2c; + struct of_device *op = NULL; + int curidx = 0, i; - while (bp != NULL) { - if (find_device(bp, echild) != 0) + for (i = 0; i < NUM_CHILDREN; i++) { + if (!(op = bp->devs[i].device)) break; - bp = bp->next; + if (curidx == index) + goto out; + op = NULL; + curidx++; } - return bp; -} - -struct linux_ebus_child *bbc_i2c_getdev(int index) -{ - struct bbc_i2c_bus *bp = all_bbc_i2c; - struct linux_ebus_child *echild = NULL; - int curidx = 0; - - while (bp != NULL) { - struct bbc_i2c_bus *next = bp->next; - int i; - - for (i = 0; i < NUM_CHILDREN; i++) { - if (!(echild = bp->devs[i].device)) - break; - if (curidx == index) - goto out; - echild = NULL; - curidx++; - } - bp = next; - } out: if (curidx == index) - return echild; + return op; return NULL; } -struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild) +struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *op) { - struct bbc_i2c_bus *bp = find_bus_for_device(echild); struct bbc_i2c_client *client; + const u32 *reg; - if (!bp) - return NULL; client = kzalloc(sizeof(*client), GFP_KERNEL); if (!client) return NULL; client->bp = bp; - client->echild = echild; - client->bus = echild->resource[0].start; - client->address = echild->resource[1].start; + client->op = op; + + reg = of_get_property(op->node, "reg", NULL); + if (!reg) { + kfree(client); + return NULL; + } - claim_device(bp, echild); + client->bus = reg[0]; + client->address = reg[1]; + + claim_device(bp, op); return client; } @@ -172,9 +114,9 @@ struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *echild) void bbc_i2c_detach(struct bbc_i2c_client *client) { struct bbc_i2c_bus *bp = client->bp; - struct linux_ebus_child *echild = client->echild; + struct of_device *op = client->op; - release_device(bp, echild); + release_device(bp, op); kfree(client); } @@ -355,44 +297,43 @@ static void __init reset_one_i2c(struct bbc_i2c_bus *bp) writeb(I2C_PCF_IDLE, bp->i2c_control_regs + 0x0); } -static int __init attach_one_i2c(struct linux_ebus_device *edev, int index) +static struct bbc_i2c_bus * __init attach_one_i2c(struct of_device *op, int index) { struct bbc_i2c_bus *bp; - struct linux_ebus_child *echild; + struct device_node *dp; int entry; bp = kzalloc(sizeof(*bp), GFP_KERNEL); if (!bp) - return -ENOMEM; + return NULL; - bp->i2c_control_regs = ioremap(edev->resource[0].start, 0x2); + bp->i2c_control_regs = of_ioremap(&op->resource[0], 0, 0x2, "bbc_i2c_regs"); if (!bp->i2c_control_regs) goto fail; - if (edev->num_addrs == 2) { - bp->i2c_bussel_reg = ioremap(edev->resource[1].start, 0x1); - if (!bp->i2c_bussel_reg) - goto fail; - } + bp->i2c_bussel_reg = of_ioremap(&op->resource[1], 0, 0x1, "bbc_i2c_bussel"); + if (!bp->i2c_bussel_reg) + goto fail; bp->waiting = 0; init_waitqueue_head(&bp->wq); - if (request_irq(edev->irqs[0], bbc_i2c_interrupt, + if (request_irq(op->irqs[0], bbc_i2c_interrupt, IRQF_SHARED, "bbc_i2c", bp)) goto fail; bp->index = index; - bp->bus_edev = edev; + bp->op = op; spin_lock_init(&bp->lock); - bp->next = all_bbc_i2c; - all_bbc_i2c = bp; entry = 0; - for (echild = edev->children; - echild && entry < 8; - echild = echild->next, entry++) { - bp->devs[entry].device = echild; + for (dp = op->node->child; + dp && entry < 8; + dp = dp->sibling, entry++) { + struct of_device *child_op; + + child_op = of_find_device_by_node(dp); + bp->devs[entry].device = child_op; bp->devs[entry].client_claimed = 0; } @@ -406,86 +347,90 @@ static int __init attach_one_i2c(struct linux_ebus_device *edev, int index) reset_one_i2c(bp); - return 0; + return bp; fail: if (bp->i2c_bussel_reg) - iounmap(bp->i2c_bussel_reg); + of_iounmap(&op->resource[1], bp->i2c_bussel_reg, 1); if (bp->i2c_control_regs) - iounmap(bp->i2c_control_regs); + of_iounmap(&op->resource[0], bp->i2c_control_regs, 2); kfree(bp); - return -EINVAL; -} - -static int __init bbc_present(void) -{ - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "bbc")) - return 1; - } - } - return 0; + return NULL; } -extern int bbc_envctrl_init(void); -extern void bbc_envctrl_cleanup(void); -static void bbc_i2c_cleanup(void); +extern int bbc_envctrl_init(struct bbc_i2c_bus *bp); +extern void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp); -static int __init bbc_i2c_init(void) +static int __devinit bbc_i2c_probe(struct of_device *op, + const struct of_device_id *match) { - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; + struct bbc_i2c_bus *bp; int err, index = 0; - if ((tlb_type != cheetah && tlb_type != cheetah_plus) || - !bbc_present()) - return -ENODEV; + bp = attach_one_i2c(op, index); + if (!bp) + return -EINVAL; - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "i2c")) { - if (!attach_one_i2c(edev, index)) - index++; - } - } + err = bbc_envctrl_init(bp); + if (err) { + free_irq(op->irqs[0], bp); + if (bp->i2c_bussel_reg) + of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1); + if (bp->i2c_control_regs) + of_iounmap(&op->resource[1], bp->i2c_control_regs, 2); + kfree(bp); + } else { + dev_set_drvdata(&op->dev, bp); } - if (!index) - return -ENODEV; - - err = bbc_envctrl_init(); - if (err) - bbc_i2c_cleanup(); return err; } -static void bbc_i2c_cleanup(void) +static int __devexit bbc_i2c_remove(struct of_device *op) { - struct bbc_i2c_bus *bp = all_bbc_i2c; + struct bbc_i2c_bus *bp = dev_get_drvdata(&op->dev); + + bbc_envctrl_cleanup(bp); + + free_irq(op->irqs[0], bp); - bbc_envctrl_cleanup(); + if (bp->i2c_bussel_reg) + of_iounmap(&op->resource[0], bp->i2c_bussel_reg, 1); + if (bp->i2c_control_regs) + of_iounmap(&op->resource[1], bp->i2c_control_regs, 2); - while (bp != NULL) { - struct bbc_i2c_bus *next = bp->next; + kfree(bp); - free_irq(bp->bus_edev->irqs[0], bp); + return 0; +} - if (bp->i2c_bussel_reg) - iounmap(bp->i2c_bussel_reg); - if (bp->i2c_control_regs) - iounmap(bp->i2c_control_regs); +static const struct of_device_id bbc_i2c_match[] = { + { + .name = "i2c", + .compatible = "SUNW,bbc-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, bbc_i2c_match); - kfree(bp); +static struct of_platform_driver bbc_i2c_driver = { + .name = "bbc_i2c", + .match_table = bbc_i2c_match, + .probe = bbc_i2c_probe, + .remove = __devexit_p(bbc_i2c_remove), +}; - bp = next; - } - all_bbc_i2c = NULL; +static int __init bbc_i2c_init(void) +{ + return of_register_driver(&bbc_i2c_driver, &of_bus_type); +} + +static void __exit bbc_i2c_exit(void) +{ + of_unregister_driver(&bbc_i2c_driver); } module_init(bbc_i2c_init); -module_exit(bbc_i2c_cleanup); +module_exit(bbc_i2c_exit); + MODULE_LICENSE("GPL"); diff --git a/drivers/sbus/char/bbc_i2c.h b/drivers/sbus/char/bbc_i2c.h index fb01bd17704..83c4811b7b5 100644 --- a/drivers/sbus/char/bbc_i2c.h +++ b/drivers/sbus/char/bbc_i2c.h @@ -1,14 +1,79 @@ -/* $Id: bbc_i2c.h,v 1.2 2001/04/02 09:59:25 davem Exp $ */ #ifndef _BBC_I2C_H #define _BBC_I2C_H -#include <asm/ebus.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/list.h> -struct bbc_i2c_client; +struct bbc_i2c_client { + struct bbc_i2c_bus *bp; + struct of_device *op; + int bus; + int address; +}; + +enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX }; + +struct bbc_cpu_temperature { + struct list_head bp_list; + struct list_head glob_list; + + struct bbc_i2c_client *client; + int index; + + /* Current readings, and history. */ + s8 curr_cpu_temp; + s8 curr_amb_temp; + s8 prev_cpu_temp; + s8 prev_amb_temp; + s8 avg_cpu_temp; + s8 avg_amb_temp; + + int sample_tick; + + enum fan_action fan_todo[2]; +#define FAN_AMBIENT 0 +#define FAN_CPU 1 +}; + +struct bbc_fan_control { + struct list_head bp_list; + struct list_head glob_list; + + struct bbc_i2c_client *client; + int index; + + int psupply_fan_on; + int cpu_fan_speed; + int system_fan_speed; +}; + +#define NUM_CHILDREN 8 + +struct bbc_i2c_bus { + struct bbc_i2c_bus *next; + int index; + spinlock_t lock; + void __iomem *i2c_bussel_reg; + void __iomem *i2c_control_regs; + unsigned char own, clock; + + wait_queue_head_t wq; + volatile int waiting; + + struct list_head temps; + struct list_head fans; + + struct of_device *op; + struct { + struct of_device *device; + int client_claimed; + } devs[NUM_CHILDREN]; +}; /* Probing and attachment. */ -extern struct linux_ebus_child *bbc_i2c_getdev(int); -extern struct bbc_i2c_client *bbc_i2c_attach(struct linux_ebus_child *); +extern struct of_device *bbc_i2c_getdev(struct bbc_i2c_bus *, int); +extern struct bbc_i2c_client *bbc_i2c_attach(struct bbc_i2c_bus *bp, struct of_device *); extern void bbc_i2c_detach(struct bbc_i2c_client *); /* Register read/write. NOTE: Blocking! */ diff --git a/drivers/sbus/char/bpp.c b/drivers/sbus/char/bpp.c deleted file mode 100644 index bba21e053a1..00000000000 --- a/drivers/sbus/char/bpp.c +++ /dev/null @@ -1,1055 +0,0 @@ -/* - * drivers/sbus/char/bpp.c - * - * Copyright (c) 1995 Picture Elements - * Stephen Williams (steve@icarus.com) - * Gus Baldauf (gbaldauf@ix.netcom.com) - * - * Linux/SPARC port by Peter Zaitcev. - * Integration into SPARC tree by Tom Dyas. - */ - - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/spinlock.h> -#include <linux/timer.h> -#include <linux/ioport.h> -#include <linux/major.h> -#include <linux/smp_lock.h> - -#include <asm/uaccess.h> -#include <asm/io.h> - -#if defined(__i386__) -# include <asm/system.h> -#endif - -#if defined(__sparc__) -# include <linux/init.h> -# include <linux/delay.h> /* udelay() */ - -# include <asm/oplib.h> /* OpenProm Library */ -# include <asm/sbus.h> -#endif - -#include <asm/bpp.h> - -#define BPP_PROBE_CODE 0x55 -#define BPP_DELAY 100 - -static const unsigned BPP_MAJOR = LP_MAJOR; -static const char *bpp_dev_name = "bpp"; - -/* When switching from compatibility to a mode where I can read, try - the following mode first. */ - -/* const unsigned char DEFAULT_ECP = 0x10; */ -static const unsigned char DEFAULT_ECP = 0x30; -static const unsigned char DEFAULT_NIBBLE = 0x00; - -/* - * These are 1284 time constraints, in units of jiffies. - */ - -static const unsigned long TIME_PSetup = 1; -static const unsigned long TIME_PResponse = 6; -static const unsigned long TIME_IDLE_LIMIT = 2000; - -/* - * One instance per supported subdevice... - */ -# define BPP_NO 3 - -enum IEEE_Mode { COMPATIBILITY, NIBBLE, ECP, ECP_RLE, EPP }; - -struct inst { - unsigned present : 1; /* True if the hardware exists */ - unsigned enhanced : 1; /* True if the hardware in "enhanced" */ - unsigned opened : 1; /* True if the device is opened already */ - unsigned run_flag : 1; /* True if waiting for a repeate byte */ - - unsigned char direction; /* 0 --> out, 0x20 --> IN */ - unsigned char pp_state; /* State of host controlled pins. */ - enum IEEE_Mode mode; - - unsigned char run_length; - unsigned char repeat_byte; -}; - -static struct inst instances[BPP_NO]; - -#if defined(__i386__) - -static const unsigned short base_addrs[BPP_NO] = { 0x278, 0x378, 0x3bc }; - -/* - * These are for data access. - * Control lines accesses are hidden in set_bits() and get_bits(). - * The exception is the probe procedure, which is system-dependent. - */ -#define bpp_outb_p(data, base) outb_p((data), (base)) -#define bpp_inb(base) inb(base) -#define bpp_inb_p(base) inb_p(base) - -/* - * This method takes the pin values mask and sets the hardware pins to - * the requested value: 1 == high voltage, 0 == low voltage. This - * burries the annoying PC bit inversion and preserves the direction - * flag. - */ -static void set_pins(unsigned short pins, unsigned minor) -{ - unsigned char bits = instances[minor].direction; /* == 0x20 */ - - if (! (pins & BPP_PP_nStrobe)) bits |= 1; - if (! (pins & BPP_PP_nAutoFd)) bits |= 2; - if ( pins & BPP_PP_nInit) bits |= 4; - if (! (pins & BPP_PP_nSelectIn)) bits |= 8; - - instances[minor].pp_state = bits; - - outb_p(bits, base_addrs[minor]+2); -} - -static unsigned short get_pins(unsigned minor) -{ - unsigned short bits = 0; - - unsigned value = instances[minor].pp_state; - if (! (value & 0x01)) bits |= BPP_PP_nStrobe; - if (! (value & 0x02)) bits |= BPP_PP_nAutoFd; - if (value & 0x04) bits |= BPP_PP_nInit; - if (! (value & 0x08)) bits |= BPP_PP_nSelectIn; - - value = inb_p(base_addrs[minor]+1); - if (value & 0x08) bits |= BPP_GP_nFault; - if (value & 0x10) bits |= BPP_GP_Select; - if (value & 0x20) bits |= BPP_GP_PError; - if (value & 0x40) bits |= BPP_GP_nAck; - if (! (value & 0x80)) bits |= BPP_GP_Busy; - - return bits; -} - -#endif /* __i386__ */ - -#if defined(__sparc__) - -/* - * Register block - */ - /* DMA registers */ -#define BPP_CSR 0x00 -#define BPP_ADDR 0x04 -#define BPP_BCNT 0x08 -#define BPP_TST_CSR 0x0C - /* Parallel Port registers */ -#define BPP_HCR 0x10 -#define BPP_OCR 0x12 -#define BPP_DR 0x14 -#define BPP_TCR 0x15 -#define BPP_OR 0x16 -#define BPP_IR 0x17 -#define BPP_ICR 0x18 -#define BPP_SIZE 0x1A - -/* BPP_CSR. Bits of type RW1 are cleared with writing '1'. */ -#define P_DEV_ID_MASK 0xf0000000 /* R */ -#define P_DEV_ID_ZEBRA 0x40000000 -#define P_DEV_ID_L64854 0xa0000000 /* == NCR 89C100+89C105. Pity. */ -#define P_NA_LOADED 0x08000000 /* R NA wirtten but was not used */ -#define P_A_LOADED 0x04000000 /* R */ -#define P_DMA_ON 0x02000000 /* R DMA is not disabled */ -#define P_EN_NEXT 0x01000000 /* RW */ -#define P_TCI_DIS 0x00800000 /* RW TCI forbidden from interrupts */ -#define P_DIAG 0x00100000 /* RW Disables draining and resetting - of P-FIFO on loading of P_ADDR*/ -#define P_BURST_SIZE 0x000c0000 /* RW SBus burst size */ -#define P_BURST_8 0x00000000 -#define P_BURST_4 0x00040000 -#define P_BURST_1 0x00080000 /* "No burst" write */ -#define P_TC 0x00004000 /* RW1 Term Count, can be cleared when - P_EN_NEXT=1 */ -#define P_EN_CNT 0x00002000 /* RW */ -#define P_EN_DMA 0x00000200 /* RW */ -#define P_WRITE 0x00000100 /* R DMA dir, 1=to ram, 0=to port */ -#define P_RESET 0x00000080 /* RW */ -#define P_SLAVE_ERR 0x00000040 /* RW1 Access size error */ -#define P_INVALIDATE 0x00000020 /* W Drop P-FIFO */ -#define P_INT_EN 0x00000010 /* RW OK to P_INT_PEND||P_ERR_PEND */ -#define P_DRAINING 0x0000000c /* R P-FIFO is draining to memory */ -#define P_ERR_PEND 0x00000002 /* R */ -#define P_INT_PEND 0x00000001 /* R */ - -/* BPP_HCR. Time is in increments of SBus clock. */ -#define P_HCR_TEST 0x8000 /* Allows buried counters to be read */ -#define P_HCR_DSW 0x7f00 /* Data strobe width (in ticks) */ -#define P_HCR_DDS 0x007f /* Data setup before strobe (in ticks) */ - -/* BPP_OCR. */ -#define P_OCR_MEM_CLR 0x8000 -#define P_OCR_DATA_SRC 0x4000 /* ) */ -#define P_OCR_DS_DSEL 0x2000 /* ) Bidirectional */ -#define P_OCR_BUSY_DSEL 0x1000 /* ) selects */ -#define P_OCR_ACK_DSEL 0x0800 /* ) */ -#define P_OCR_EN_DIAG 0x0400 -#define P_OCR_BUSY_OP 0x0200 /* Busy operation */ -#define P_OCR_ACK_OP 0x0100 /* Ack operation */ -#define P_OCR_SRST 0x0080 /* Reset state machines. Not selfcleaning. */ -#define P_OCR_IDLE 0x0008 /* PP data transfer state machine is idle */ -#define P_OCR_V_ILCK 0x0002 /* Versatec faded. Zebra only. */ -#define P_OCR_EN_VER 0x0001 /* Enable Versatec (0 - enable). Zebra only. */ - -/* BPP_TCR */ -#define P_TCR_DIR 0x08 -#define P_TCR_BUSY 0x04 -#define P_TCR_ACK 0x02 -#define P_TCR_DS 0x01 /* Strobe */ - -/* BPP_OR */ -#define P_OR_V3 0x20 /* ) */ -#define P_OR_V2 0x10 /* ) on Zebra only */ -#define P_OR_V1 0x08 /* ) */ -#define P_OR_INIT 0x04 -#define P_OR_AFXN 0x02 /* Auto Feed */ -#define P_OR_SLCT_IN 0x01 - -/* BPP_IR */ -#define P_IR_PE 0x04 -#define P_IR_SLCT 0x02 -#define P_IR_ERR 0x01 - -/* BPP_ICR */ -#define P_DS_IRQ 0x8000 /* RW1 */ -#define P_ACK_IRQ 0x4000 /* RW1 */ -#define P_BUSY_IRQ 0x2000 /* RW1 */ -#define P_PE_IRQ 0x1000 /* RW1 */ -#define P_SLCT_IRQ 0x0800 /* RW1 */ -#define P_ERR_IRQ 0x0400 /* RW1 */ -#define P_DS_IRQ_EN 0x0200 /* RW Always on rising edge */ -#define P_ACK_IRQ_EN 0x0100 /* RW Always on rising edge */ -#define P_BUSY_IRP 0x0080 /* RW 1= rising edge */ -#define P_BUSY_IRQ_EN 0x0040 /* RW */ -#define P_PE_IRP 0x0020 /* RW 1= rising edge */ -#define P_PE_IRQ_EN 0x0010 /* RW */ -#define P_SLCT_IRP 0x0008 /* RW 1= rising edge */ -#define P_SLCT_IRQ_EN 0x0004 /* RW */ -#define P_ERR_IRP 0x0002 /* RW1 1= rising edge */ -#define P_ERR_IRQ_EN 0x0001 /* RW */ - -static void __iomem *base_addrs[BPP_NO]; - -#define bpp_outb_p(data, base) sbus_writeb(data, (base) + BPP_DR) -#define bpp_inb_p(base) sbus_readb((base) + BPP_DR) -#define bpp_inb(base) sbus_readb((base) + BPP_DR) - -static void set_pins(unsigned short pins, unsigned minor) -{ - void __iomem *base = base_addrs[minor]; - unsigned char bits_tcr = 0, bits_or = 0; - - if (instances[minor].direction & 0x20) bits_tcr |= P_TCR_DIR; - if ( pins & BPP_PP_nStrobe) bits_tcr |= P_TCR_DS; - - if ( pins & BPP_PP_nAutoFd) bits_or |= P_OR_AFXN; - if (! (pins & BPP_PP_nInit)) bits_or |= P_OR_INIT; - if (! (pins & BPP_PP_nSelectIn)) bits_or |= P_OR_SLCT_IN; - - sbus_writeb(bits_or, base + BPP_OR); - sbus_writeb(bits_tcr, base + BPP_TCR); -} - -/* - * i386 people read output pins from a software image. - * We may get them back from hardware. - * Again, inversion of pins must he buried here. - */ -static unsigned short get_pins(unsigned minor) -{ - void __iomem *base = base_addrs[minor]; - unsigned short bits = 0; - unsigned value_tcr = sbus_readb(base + BPP_TCR); - unsigned value_ir = sbus_readb(base + BPP_IR); - unsigned value_or = sbus_readb(base + BPP_OR); - - if (value_tcr & P_TCR_DS) bits |= BPP_PP_nStrobe; - if (value_or & P_OR_AFXN) bits |= BPP_PP_nAutoFd; - if (! (value_or & P_OR_INIT)) bits |= BPP_PP_nInit; - if (! (value_or & P_OR_SLCT_IN)) bits |= BPP_PP_nSelectIn; - - if (value_ir & P_IR_ERR) bits |= BPP_GP_nFault; - if (! (value_ir & P_IR_SLCT)) bits |= BPP_GP_Select; - if (! (value_ir & P_IR_PE)) bits |= BPP_GP_PError; - if (! (value_tcr & P_TCR_ACK)) bits |= BPP_GP_nAck; - if (value_tcr & P_TCR_BUSY) bits |= BPP_GP_Busy; - - return bits; -} - -#endif /* __sparc__ */ - -static void snooze(unsigned long snooze_time, unsigned minor) -{ - schedule_timeout_uninterruptible(snooze_time + 1); -} - -static int wait_for(unsigned short set, unsigned short clr, - unsigned long delay, unsigned minor) -{ - unsigned short pins = get_pins(minor); - - unsigned long extime = 0; - - /* - * Try a real fast scan for the first jiffy, in case the device - * responds real good. The first while loop guesses an expire - * time accounting for possible wraparound of jiffies. - */ - while (time_after_eq(jiffies, extime)) extime = jiffies + 1; - while ( (time_before(jiffies, extime)) - && (((pins & set) != set) || ((pins & clr) != 0)) ) { - pins = get_pins(minor); - } - - delay -= 1; - - /* - * If my delay expired or the pins are still not where I want - * them, then resort to using the timer and greatly reduce my - * sample rate. If the peripheral is going to be slow, this will - * give the CPU up to some more worthy process. - */ - while ( delay && (((pins & set) != set) || ((pins & clr) != 0)) ) { - - snooze(1, minor); - pins = get_pins(minor); - delay -= 1; - } - - if (delay == 0) return -1; - else return pins; -} - -/* - * Return ZERO(0) If the negotiation succeeds, an errno otherwise. An - * errno means something broke, and I do not yet know how to fix it. - */ -static int negotiate(unsigned char mode, unsigned minor) -{ - int rc; - unsigned short pins = get_pins(minor); - if (pins & BPP_PP_nSelectIn) return -EIO; - - - /* Event 0: Write the mode to the data lines */ - bpp_outb_p(mode, base_addrs[minor]); - - snooze(TIME_PSetup, minor); - - /* Event 1: Strobe the mode code into the peripheral */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nInit, minor); - - /* Wait for Event 2: Peripheral responds as a 1284 device. */ - rc = wait_for(BPP_GP_PError|BPP_GP_Select|BPP_GP_nFault, - BPP_GP_nAck, - TIME_PResponse, - minor); - - if (rc == -1) return -ETIMEDOUT; - - /* Event 3: latch extensibility request */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nInit, minor); - - /* ... quick nap while peripheral ponders the byte i'm sending...*/ - snooze(1, minor); - - /* Event 4: restore strobe, to ACK peripheral's response. */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); - - /* Wait for Event 6: Peripheral latches response bits */ - rc = wait_for(BPP_GP_nAck, 0, TIME_PSetup+TIME_PResponse, minor); - if (rc == -1) return -EIO; - - /* A 1284 device cannot refuse nibble mode */ - if (mode == DEFAULT_NIBBLE) return 0; - - if (pins & BPP_GP_Select) return 0; - - return -EPROTONOSUPPORT; -} - -static int terminate(unsigned minor) -{ - int rc; - - /* Event 22: Request termination of 1284 mode */ - set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); - - /* Wait for Events 23 and 24: ACK termination request. */ - rc = wait_for(BPP_GP_Busy|BPP_GP_nFault, - BPP_GP_nAck, - TIME_PSetup+TIME_PResponse, - minor); - - instances[minor].direction = 0; - instances[minor].mode = COMPATIBILITY; - - if (rc == -1) { - return -EIO; - } - - /* Event 25: Handshake by lowering nAutoFd */ - set_pins(BPP_PP_nStrobe|BPP_PP_nInit, minor); - - /* Event 26: Peripheral wiggles lines... */ - - /* Event 27: Peripheral sets nAck HIGH to ack handshake */ - rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); - if (rc == -1) { - set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); - return -EIO; - } - - /* Event 28: Finish phase by raising nAutoFd */ - set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, minor); - - return 0; -} - -static DEFINE_SPINLOCK(bpp_open_lock); - -/* - * Allow only one process to open the device at a time. - */ -static int bpp_open(struct inode *inode, struct file *f) -{ - unsigned minor = iminor(inode); - int ret; - - lock_kernel(); - spin_lock(&bpp_open_lock); - ret = 0; - if (minor >= BPP_NO) { - ret = -ENODEV; - } else { - if (! instances[minor].present) { - ret = -ENODEV; - } else { - if (instances[minor].opened) - ret = -EBUSY; - else - instances[minor].opened = 1; - } - } - spin_unlock(&bpp_open_lock); - unlock_kernel(); - - return ret; -} - -/* - * When the process closes the device, this method is called to clean - * up and reset the hardware. Always leave the device in compatibility - * mode as this is a reasonable place to clean up from messes made by - * ioctls, or other mayhem. - */ -static int bpp_release(struct inode *inode, struct file *f) -{ - unsigned minor = iminor(inode); - - spin_lock(&bpp_open_lock); - instances[minor].opened = 0; - - if (instances[minor].mode != COMPATIBILITY) - terminate(minor); - - spin_unlock(&bpp_open_lock); - - return 0; -} - -static long read_nibble(unsigned minor, char __user *c, unsigned long cnt) -{ - unsigned long remaining = cnt; - long rc; - - while (remaining > 0) { - unsigned char byte = 0; - int pins; - - /* Event 7: request nibble */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor); - - /* Wait for event 9: Peripher strobes first nibble */ - pins = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor); - if (pins == -1) return -ETIMEDOUT; - - /* Event 10: I handshake nibble */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor); - if (pins & BPP_GP_nFault) byte |= 0x01; - if (pins & BPP_GP_Select) byte |= 0x02; - if (pins & BPP_GP_PError) byte |= 0x04; - if (pins & BPP_GP_Busy) byte |= 0x08; - - /* Wait for event 11: Peripheral handshakes nibble */ - rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); - - /* Event 7: request nibble */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe, minor); - - /* Wait for event 9: Peripher strobes first nibble */ - pins = wait_for(0, BPP_GP_nAck, TIME_PResponse, minor); - if (rc == -1) return -ETIMEDOUT; - - /* Event 10: I handshake nibble */ - set_pins(BPP_PP_nSelectIn|BPP_PP_nStrobe|BPP_PP_nAutoFd, minor); - if (pins & BPP_GP_nFault) byte |= 0x10; - if (pins & BPP_GP_Select) byte |= 0x20; - if (pins & BPP_GP_PError) byte |= 0x40; - if (pins & BPP_GP_Busy) byte |= 0x80; - - if (put_user(byte, c)) - return -EFAULT; - c += 1; - remaining -= 1; - - /* Wait for event 11: Peripheral handshakes nibble */ - rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); - if (rc == -1) return -EIO; - } - - return cnt - remaining; -} - -static long read_ecp(unsigned minor, char __user *c, unsigned long cnt) -{ - unsigned long remaining; - long rc; - - /* Turn ECP mode from forward to reverse if needed. */ - if (! instances[minor].direction) { - unsigned short pins = get_pins(minor); - - /* Event 38: Turn the bus around */ - instances[minor].direction = 0x20; - pins &= ~BPP_PP_nAutoFd; - set_pins(pins, minor); - - /* Event 39: Set pins for reverse mode. */ - snooze(TIME_PSetup, minor); - set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor); - - /* Wait for event 40: Peripheral ready to be strobed */ - rc = wait_for(0, BPP_GP_PError, TIME_PResponse, minor); - if (rc == -1) return -ETIMEDOUT; - } - - remaining = cnt; - - while (remaining > 0) { - - /* If there is a run length for a repeated byte, repeat */ - /* that byte a few times. */ - if (instances[minor].run_length && !instances[minor].run_flag) { - - char buffer[128]; - unsigned idx; - unsigned repeat = remaining < instances[minor].run_length - ? remaining - : instances[minor].run_length; - - for (idx = 0 ; idx < repeat ; idx += 1) - buffer[idx] = instances[minor].repeat_byte; - - if (copy_to_user(c, buffer, repeat)) - return -EFAULT; - remaining -= repeat; - c += repeat; - instances[minor].run_length -= repeat; - } - - if (remaining == 0) break; - - - /* Wait for Event 43: Data active on the bus. */ - rc = wait_for(0, BPP_GP_nAck, TIME_IDLE_LIMIT, minor); - if (rc == -1) break; - - if (rc & BPP_GP_Busy) { - /* OK, this is data. read it in. */ - unsigned char byte = bpp_inb(base_addrs[minor]); - if (put_user(byte, c)) - return -EFAULT; - c += 1; - remaining -= 1; - - if (instances[minor].run_flag) { - instances[minor].repeat_byte = byte; - instances[minor].run_flag = 0; - } - - } else { - unsigned char byte = bpp_inb(base_addrs[minor]); - if (byte & 0x80) { - printk("bpp%d: " - "Ignoring ECP channel %u from device.\n", - minor, byte & 0x7f); - } else { - instances[minor].run_length = byte; - instances[minor].run_flag = 1; - } - } - - /* Event 44: I got it. */ - set_pins(BPP_PP_nStrobe|BPP_PP_nAutoFd|BPP_PP_nSelectIn, minor); - - /* Wait for event 45: peripheral handshake */ - rc = wait_for(BPP_GP_nAck, 0, TIME_PResponse, minor); - if (rc == -1) return -ETIMEDOUT; - - /* Event 46: Finish handshake */ - set_pins(BPP_PP_nStrobe|BPP_PP_nSelectIn, minor); - - } - - - return cnt - remaining; -} - -static ssize_t bpp_read(struct file *f, char __user *c, size_t cnt, loff_t * ppos) -{ - long rc; - unsigned minor = iminor(f->f_path.dentry->d_inode); - if (minor >= BPP_NO) return -ENODEV; - if (!instances[minor].present) return -ENODEV; - - switch (instances[minor].mode) { - - default: - if (instances[minor].mode != COMPATIBILITY) - terminate(minor); - - if (instances[minor].enhanced) { - /* For now, do all reads with ECP-RLE mode */ - unsigned short pins; - - rc = negotiate(DEFAULT_ECP, minor); - if (rc < 0) break; - - instances[minor].mode = ECP_RLE; - - /* Event 30: set nAutoFd low to setup for ECP mode */ - pins = get_pins(minor); - pins &= ~BPP_PP_nAutoFd; - set_pins(pins, minor); - - /* Wait for Event 31: peripheral ready */ - rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor); - if (rc == -1) return -ETIMEDOUT; - - rc = read_ecp(minor, c, cnt); - - } else { - rc = negotiate(DEFAULT_NIBBLE, minor); - if (rc < 0) break; - - instances[minor].mode = NIBBLE; - - rc = read_nibble(minor, c, cnt); - } - break; - - case NIBBLE: - rc = read_nibble(minor, c, cnt); - break; - - case ECP: - case ECP_RLE: - rc = read_ecp(minor, c, cnt); - break; - - } - - - return rc; -} - -/* - * Compatibility mode handshaking is a matter of writing data, - * strobing it, and waiting for the printer to stop being busy. - */ -static long write_compat(unsigned minor, const char __user *c, unsigned long cnt) -{ - long rc; - unsigned short pins = get_pins(minor); - - unsigned long remaining = cnt; - - - while (remaining > 0) { - unsigned char byte; - - if (get_user(byte, c)) - return -EFAULT; - c += 1; - - rc = wait_for(BPP_GP_nAck, BPP_GP_Busy, TIME_IDLE_LIMIT, minor); - if (rc == -1) return -ETIMEDOUT; - - bpp_outb_p(byte, base_addrs[minor]); - remaining -= 1; - /* snooze(1, minor); */ - - pins &= ~BPP_PP_nStrobe; - set_pins(pins, minor); - - rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor); - - pins |= BPP_PP_nStrobe; - set_pins(pins, minor); - } - - return cnt - remaining; -} - -/* - * Write data using ECP mode. Watch out that the port may be set up - * for reading. If so, turn the port around. - */ -static long write_ecp(unsigned minor, const char __user *c, unsigned long cnt) -{ - unsigned short pins = get_pins(minor); - unsigned long remaining = cnt; - - if (instances[minor].direction) { - int rc; - - /* Event 47 Request bus be turned around */ - pins |= BPP_PP_nInit; - set_pins(pins, minor); - - /* Wait for Event 49: Peripheral relinquished bus */ - rc = wait_for(BPP_GP_PError, 0, TIME_PResponse, minor); - - pins |= BPP_PP_nAutoFd; - instances[minor].direction = 0; - set_pins(pins, minor); - } - - while (remaining > 0) { - unsigned char byte; - int rc; - - if (get_user(byte, c)) - return -EFAULT; - - rc = wait_for(0, BPP_GP_Busy, TIME_PResponse, minor); - if (rc == -1) return -ETIMEDOUT; - - c += 1; - - bpp_outb_p(byte, base_addrs[minor]); - - pins &= ~BPP_PP_nStrobe; - set_pins(pins, minor); - - pins |= BPP_PP_nStrobe; - rc = wait_for(BPP_GP_Busy, 0, TIME_PResponse, minor); - if (rc == -1) return -EIO; - - set_pins(pins, minor); - } - - return cnt - remaining; -} - -/* - * Write to the peripheral. Be sensitive of the current mode. If I'm - * in a mode that can be turned around (ECP) then just do - * that. Otherwise, terminate and do my writing in compat mode. This - * is the safest course as any device can handle it. - */ -static ssize_t bpp_write(struct file *f, const char __user *c, size_t cnt, loff_t * ppos) -{ - long errno = 0; - unsigned minor = iminor(f->f_path.dentry->d_inode); - if (minor >= BPP_NO) return -ENODEV; - if (!instances[minor].present) return -ENODEV; - - switch (instances[minor].mode) { - - case ECP: - case ECP_RLE: - errno = write_ecp(minor, c, cnt); - break; - case COMPATIBILITY: - errno = write_compat(minor, c, cnt); - break; - default: - terminate(minor); - errno = write_compat(minor, c, cnt); - } - - return errno; -} - -static int bpp_ioctl(struct inode *inode, struct file *f, unsigned int cmd, - unsigned long arg) -{ - int errno = 0; - - unsigned minor = iminor(inode); - if (minor >= BPP_NO) return -ENODEV; - if (!instances[minor].present) return -ENODEV; - - - switch (cmd) { - - case BPP_PUT_PINS: - set_pins(arg, minor); - break; - - case BPP_GET_PINS: - errno = get_pins(minor); - break; - - case BPP_PUT_DATA: - bpp_outb_p(arg, base_addrs[minor]); - break; - - case BPP_GET_DATA: - errno = bpp_inb_p(base_addrs[minor]); - break; - - case BPP_SET_INPUT: - if (arg) - if (instances[minor].enhanced) { - unsigned short bits = get_pins(minor); - instances[minor].direction = 0x20; - set_pins(bits, minor); - } else { - errno = -ENOTTY; - } - else { - unsigned short bits = get_pins(minor); - instances[minor].direction = 0x00; - set_pins(bits, minor); - } - break; - - default: - errno = -EINVAL; - } - - return errno; -} - -static const struct file_operations bpp_fops = { - .owner = THIS_MODULE, - .read = bpp_read, - .write = bpp_write, - .ioctl = bpp_ioctl, - .open = bpp_open, - .release = bpp_release, -}; - -#if defined(__i386__) - -#define collectLptPorts() {} - -static void probeLptPort(unsigned idx) -{ - unsigned int testvalue; - const unsigned short lpAddr = base_addrs[idx]; - - instances[idx].present = 0; - instances[idx].enhanced = 0; - instances[idx].direction = 0; - instances[idx].mode = COMPATIBILITY; - instances[idx].run_length = 0; - instances[idx].run_flag = 0; - if (!request_region(lpAddr,3, bpp_dev_name)) return; - - /* - * First, make sure the instance exists. Do this by writing to - * the data latch and reading the value back. If the port *is* - * present, test to see if it supports extended-mode - * operation. This will be required for IEEE1284 reverse - * transfers. - */ - - outb_p(BPP_PROBE_CODE, lpAddr); - for (testvalue=0; testvalue<BPP_DELAY; testvalue++) - ; - testvalue = inb_p(lpAddr); - if (testvalue == BPP_PROBE_CODE) { - unsigned save; - instances[idx].present = 1; - - save = inb_p(lpAddr+2); - for (testvalue=0; testvalue<BPP_DELAY; testvalue++) - ; - outb_p(save|0x20, lpAddr+2); - for (testvalue=0; testvalue<BPP_DELAY; testvalue++) - ; - outb_p(~BPP_PROBE_CODE, lpAddr); - for (testvalue=0; testvalue<BPP_DELAY; testvalue++) - ; - testvalue = inb_p(lpAddr); - if ((testvalue&0xff) == (0xff&~BPP_PROBE_CODE)) - instances[idx].enhanced = 0; - else - instances[idx].enhanced = 1; - outb_p(save, lpAddr+2); - } - else { - release_region(lpAddr,3); - } - /* - * Leave the port in compat idle mode. - */ - set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx); - - printk("bpp%d: Port at 0x%03x: Enhanced mode %s\n", idx, base_addrs[idx], - instances[idx].enhanced? "SUPPORTED" : "UNAVAILABLE"); -} - -static inline void freeLptPort(int idx) -{ - release_region(base_addrs[idx], 3); -} - -#endif - -#if defined(__sparc__) - -static void __iomem *map_bpp(struct sbus_dev *dev, int idx) -{ - return sbus_ioremap(&dev->resource[0], 0, BPP_SIZE, "bpp"); -} - -static int collectLptPorts(void) -{ - struct sbus_bus *bus; - struct sbus_dev *dev; - int count; - - count = 0; - for_all_sbusdev(dev, bus) { - if (strcmp(dev->prom_name, "SUNW,bpp") == 0) { - if (count >= BPP_NO) { - printk(KERN_NOTICE - "bpp: More than %d bpp ports," - " rest is ignored\n", BPP_NO); - return count; - } - base_addrs[count] = map_bpp(dev, count); - count++; - } - } - return count; -} - -static void probeLptPort(unsigned idx) -{ - void __iomem *rp = base_addrs[idx]; - __u32 csr; - char *brand; - - instances[idx].present = 0; - instances[idx].enhanced = 0; - instances[idx].direction = 0; - instances[idx].mode = COMPATIBILITY; - instances[idx].run_length = 0; - instances[idx].run_flag = 0; - - if (!rp) return; - - instances[idx].present = 1; - instances[idx].enhanced = 1; /* Sure */ - - csr = sbus_readl(rp + BPP_CSR); - if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) { - udelay(20); - csr = sbus_readl(rp + BPP_CSR); - if ((csr & P_DRAINING) != 0 && (csr & P_ERR_PEND) == 0) { - printk("bpp%d: DRAINING still active (0x%08x)\n", idx, csr); - } - } - printk("bpp%d: reset with 0x%08x ..", idx, csr); - sbus_writel((csr | P_RESET) & ~P_INT_EN, rp + BPP_CSR); - udelay(500); - sbus_writel(sbus_readl(rp + BPP_CSR) & ~P_RESET, rp + BPP_CSR); - csr = sbus_readl(rp + BPP_CSR); - printk(" done with csr=0x%08x ocr=0x%04x\n", - csr, sbus_readw(rp + BPP_OCR)); - - switch (csr & P_DEV_ID_MASK) { - case P_DEV_ID_ZEBRA: - brand = "Zebra"; - break; - case P_DEV_ID_L64854: - brand = "DMA2"; - break; - default: - brand = "Unknown"; - } - printk("bpp%d: %s at %p\n", idx, brand, rp); - - /* - * Leave the port in compat idle mode. - */ - set_pins(BPP_PP_nAutoFd|BPP_PP_nStrobe|BPP_PP_nInit, idx); - - return; -} - -static inline void freeLptPort(int idx) -{ - sbus_iounmap(base_addrs[idx], BPP_SIZE); -} - -#endif - -static int __init bpp_init(void) -{ - int rc; - unsigned idx; - - rc = collectLptPorts(); - if (rc == 0) - return -ENODEV; - - rc = register_chrdev(BPP_MAJOR, bpp_dev_name, &bpp_fops); - if (rc < 0) - return rc; - - for (idx = 0; idx < BPP_NO; idx++) { - instances[idx].opened = 0; - probeLptPort(idx); - } - - return 0; -} - -static void __exit bpp_cleanup(void) -{ - unsigned idx; - - unregister_chrdev(BPP_MAJOR, bpp_dev_name); - - for (idx = 0; idx < BPP_NO; idx++) { - if (instances[idx].present) - freeLptPort(idx); - } -} - -module_init(bpp_init); -module_exit(bpp_cleanup); - -MODULE_LICENSE("GPL"); - diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c deleted file mode 100644 index 23abfdfb44f..00000000000 --- a/drivers/sbus/char/cpwatchdog.c +++ /dev/null @@ -1,858 +0,0 @@ -/* cpwatchdog.c - driver implementation for hardware watchdog - * timers found on Sun Microsystems CP1400 and CP1500 boards. - * - * This device supports both the generic Linux watchdog - * interface and Solaris-compatible ioctls as best it is - * able. - * - * NOTE: CP1400 systems appear to have a defective intr_mask - * register on the PLD, preventing the disabling of - * timer interrupts. We use a timer to periodically - * reset 'stopped' watchdogs on affected platforms. - * - * Copyright (c) 2000 Eric Brower (ebrower@usa.net) - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/fs.h> -#include <linux/errno.h> -#include <linux/major.h> -#include <linux/init.h> -#include <linux/miscdevice.h> -#include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/timer.h> -#include <linux/smp_lock.h> -#include <linux/io.h> -#include <asm/irq.h> -#include <asm/ebus.h> -#include <asm/oplib.h> -#include <asm/uaccess.h> - -#include <asm/watchdog.h> - -#define WD_OBPNAME "watchdog" -#define WD_BADMODEL "SUNW,501-5336" -#define WD_BTIMEOUT (jiffies + (HZ * 1000)) -#define WD_BLIMIT 0xFFFF - -#define WD0_DEVNAME "watchdog0" -#define WD1_DEVNAME "watchdog1" -#define WD2_DEVNAME "watchdog2" - -#define WD0_MINOR 212 -#define WD1_MINOR 213 -#define WD2_MINOR 214 - - -/* Internal driver definitions - */ -#define WD0_ID 0 /* Watchdog0 */ -#define WD1_ID 1 /* Watchdog1 */ -#define WD2_ID 2 /* Watchdog2 */ -#define WD_NUMDEVS 3 /* Device contains 3 timers */ - -#define WD_INTR_OFF 0 /* Interrupt disable value */ -#define WD_INTR_ON 1 /* Interrupt enable value */ - -#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */ -#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */ -#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */ - -/* Register value definitions - */ -#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */ -#define WD1_INTR_MASK 0x02 -#define WD2_INTR_MASK 0x04 - -#define WD_S_RUNNING 0x01 /* Watchdog device status running */ -#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */ - -/* Sun uses Altera PLD EPF8820ATC144-4 - * providing three hardware watchdogs: - * - * 1) RIC - sends an interrupt when triggered - * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU - * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board - * - *** Timer register block definition (struct wd_timer_regblk) - * - * dcntr and limit registers (halfword access): - * ------------------- - * | 15 | ...| 1 | 0 | - * ------------------- - * |- counter val -| - * ------------------- - * dcntr - Current 16-bit downcounter value. - * When downcounter reaches '0' watchdog expires. - * Reading this register resets downcounter with 'limit' value. - * limit - 16-bit countdown value in 1/10th second increments. - * Writing this register begins countdown with input value. - * Reading from this register does not affect counter. - * NOTES: After watchdog reset, dcntr and limit contain '1' - * - * status register (byte access): - * --------------------------- - * | 7 | ... | 2 | 1 | 0 | - * --------------+------------ - * |- UNUSED -| EXP | RUN | - * --------------------------- - * status- Bit 0 - Watchdog is running - * Bit 1 - Watchdog has expired - * - *** PLD register block definition (struct wd_pld_regblk) - * - * intr_mask register (byte access): - * --------------------------------- - * | 7 | ... | 3 | 2 | 1 | 0 | - * +-------------+------------------ - * |- UNUSED -| WD3 | WD2 | WD1 | - * --------------------------------- - * WD3 - 1 == Interrupt disabled for watchdog 3 - * WD2 - 1 == Interrupt disabled for watchdog 2 - * WD1 - 1 == Interrupt disabled for watchdog 1 - * - * pld_status register (byte access): - * UNKNOWN, MAGICAL MYSTERY REGISTER - * - */ -#define WD_TIMER_REGSZ 16 -#define WD0_OFF 0 -#define WD1_OFF (WD_TIMER_REGSZ * 1) -#define WD2_OFF (WD_TIMER_REGSZ * 2) -#define PLD_OFF (WD_TIMER_REGSZ * 3) - -#define WD_DCNTR 0x00 -#define WD_LIMIT 0x04 -#define WD_STATUS 0x08 - -#define PLD_IMASK (PLD_OFF + 0x00) -#define PLD_STATUS (PLD_OFF + 0x04) - -/* Individual timer structure - */ -struct wd_timer { - __u16 timeout; - __u8 intr_mask; - unsigned char runstatus; - void __iomem *regs; -}; - -/* Device structure - */ -struct wd_device { - int irq; - spinlock_t lock; - unsigned char isbaddoggie; /* defective PLD */ - unsigned char opt_enable; - unsigned char opt_reboot; - unsigned short opt_timeout; - unsigned char initialized; - struct wd_timer watchdog[WD_NUMDEVS]; - void __iomem *regs; -}; - -static struct wd_device wd_dev = { - 0, __SPIN_LOCK_UNLOCKED(wd_dev.lock), 0, 0, 0, 0, -}; - -static struct timer_list wd_timer; - -static int wd0_timeout = 0; -static int wd1_timeout = 0; -static int wd2_timeout = 0; - -#ifdef MODULE -module_param (wd0_timeout, int, 0); -MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); -module_param (wd1_timeout, int, 0); -MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs"); -module_param (wd2_timeout, int, 0); -MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); - -MODULE_AUTHOR - ("Eric Brower <ebrower@usa.net>"); -MODULE_DESCRIPTION - ("Hardware watchdog driver for Sun Microsystems CP1400/1500"); -MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE - ("watchdog"); -#endif /* ifdef MODULE */ - -/* Forward declarations of internal methods - */ -#ifdef WD_DEBUG -static void wd_dumpregs(void); -#endif -static irqreturn_t wd_interrupt(int irq, void *dev_id); -static void wd_toggleintr(struct wd_timer* pTimer, int enable); -static void wd_pingtimer(struct wd_timer* pTimer); -static void wd_starttimer(struct wd_timer* pTimer); -static void wd_resetbrokentimer(struct wd_timer* pTimer); -static void wd_stoptimer(struct wd_timer* pTimer); -static void wd_brokentimer(unsigned long data); -static int wd_getstatus(struct wd_timer* pTimer); - -/* PLD expects words to be written in LSB format, - * so we must flip all words prior to writing them to regs - */ -static inline unsigned short flip_word(unsigned short word) -{ - return ((word & 0xff) << 8) | ((word >> 8) & 0xff); -} - -#define wd_writew(val, addr) (writew(flip_word(val), addr)) -#define wd_readw(addr) (flip_word(readw(addr))) -#define wd_writeb(val, addr) (writeb(val, addr)) -#define wd_readb(addr) (readb(addr)) - - -/* CP1400s seem to have broken PLD implementations-- - * the interrupt_mask register cannot be written, so - * no timer interrupts can be masked within the PLD. - */ -static inline int wd_isbroken(void) -{ - /* we could test this by read/write/read/restore - * on the interrupt mask register only if OBP - * 'watchdog-enable?' == FALSE, but it seems - * ubiquitous on CP1400s - */ - char val[32]; - prom_getproperty(prom_root_node, "model", val, sizeof(val)); - return((!strcmp(val, WD_BADMODEL)) ? 1 : 0); -} - -/* Retrieve watchdog-enable? option from OBP - * Returns 0 if false, 1 if true - */ -static inline int wd_opt_enable(void) -{ - int opt_node; - - opt_node = prom_getchild(prom_root_node); - opt_node = prom_searchsiblings(opt_node, "options"); - return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1); -} - -/* Retrieve watchdog-reboot? option from OBP - * Returns 0 if false, 1 if true - */ -static inline int wd_opt_reboot(void) -{ - int opt_node; - - opt_node = prom_getchild(prom_root_node); - opt_node = prom_searchsiblings(opt_node, "options"); - return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1); -} - -/* Retrieve watchdog-timeout option from OBP - * Returns OBP value, or 0 if not located - */ -static inline int wd_opt_timeout(void) -{ - int opt_node; - char value[32]; - char *p = value; - - opt_node = prom_getchild(prom_root_node); - opt_node = prom_searchsiblings(opt_node, "options"); - opt_node = prom_getproperty(opt_node, - "watchdog-timeout", - value, - sizeof(value)); - if(-1 != opt_node) { - /* atoi implementation */ - for(opt_node = 0; /* nop */; p++) { - if(*p >= '0' && *p <= '9') { - opt_node = (10*opt_node)+(*p-'0'); - } - else { - break; - } - } - } - return((-1 == opt_node) ? (0) : (opt_node)); -} - -static int wd_open(struct inode *inode, struct file *f) -{ - lock_kernel(); - switch(iminor(inode)) - { - case WD0_MINOR: - f->private_data = &wd_dev.watchdog[WD0_ID]; - break; - case WD1_MINOR: - f->private_data = &wd_dev.watchdog[WD1_ID]; - break; - case WD2_MINOR: - f->private_data = &wd_dev.watchdog[WD2_ID]; - break; - default: - unlock_kernel(); - return(-ENODEV); - } - - /* Register IRQ on first open of device */ - if(0 == wd_dev.initialized) - { - if (request_irq(wd_dev.irq, - &wd_interrupt, - IRQF_SHARED, - WD_OBPNAME, - (void *)wd_dev.regs)) { - printk("%s: Cannot register IRQ %d\n", - WD_OBPNAME, wd_dev.irq); - unlock_kernel(); - return(-EBUSY); - } - wd_dev.initialized = 1; - } - - unlock_kernel(); - return(nonseekable_open(inode, f)); -} - -static int wd_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static int wd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int setopt = 0; - struct wd_timer* pTimer = (struct wd_timer*)file->private_data; - void __user *argp = (void __user *)arg; - struct watchdog_info info = { - 0, - 0, - "Altera EPF8820ATC144-4" - }; - - if(NULL == pTimer) { - return(-EINVAL); - } - - switch(cmd) - { - /* Generic Linux IOCTLs */ - case WDIOC_GETSUPPORT: - if(copy_to_user(argp, &info, sizeof(struct watchdog_info))) { - return(-EFAULT); - } - break; - case WDIOC_GETSTATUS: - case WDIOC_GETBOOTSTATUS: - if (put_user(0, (int __user *)argp)) - return -EFAULT; - break; - case WDIOC_KEEPALIVE: - wd_pingtimer(pTimer); - break; - case WDIOC_SETOPTIONS: - if(copy_from_user(&setopt, argp, sizeof(unsigned int))) { - return -EFAULT; - } - if(setopt & WDIOS_DISABLECARD) { - if(wd_dev.opt_enable) { - printk( - "%s: cannot disable watchdog in ENABLED mode\n", - WD_OBPNAME); - return(-EINVAL); - } - wd_stoptimer(pTimer); - } - else if(setopt & WDIOS_ENABLECARD) { - wd_starttimer(pTimer); - } - else { - return(-EINVAL); - } - break; - /* Solaris-compatible IOCTLs */ - case WIOCGSTAT: - setopt = wd_getstatus(pTimer); - if(copy_to_user(argp, &setopt, sizeof(unsigned int))) { - return(-EFAULT); - } - break; - case WIOCSTART: - wd_starttimer(pTimer); - break; - case WIOCSTOP: - if(wd_dev.opt_enable) { - printk("%s: cannot disable watchdog in ENABLED mode\n", - WD_OBPNAME); - return(-EINVAL); - } - wd_stoptimer(pTimer); - break; - default: - return(-EINVAL); - } - return(0); -} - -static long wd_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - int rval = -ENOIOCTLCMD; - - switch (cmd) { - /* solaris ioctls are specific to this driver */ - case WIOCSTART: - case WIOCSTOP: - case WIOCGSTAT: - lock_kernel(); - rval = wd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - unlock_kernel(); - break; - /* everything else is handled by the generic compat layer */ - default: - break; - } - - return rval; -} - -static ssize_t wd_write(struct file *file, - const char __user *buf, - size_t count, - loff_t *ppos) -{ - struct wd_timer* pTimer = (struct wd_timer*)file->private_data; - - if(NULL == pTimer) { - return(-EINVAL); - } - - if (count) { - wd_pingtimer(pTimer); - return 1; - } - return 0; -} - -static ssize_t wd_read(struct file * file, char __user *buffer, - size_t count, loff_t *ppos) -{ -#ifdef WD_DEBUG - wd_dumpregs(); - return(0); -#else - return(-EINVAL); -#endif /* ifdef WD_DEBUG */ -} - -static irqreturn_t wd_interrupt(int irq, void *dev_id) -{ - /* Only WD0 will interrupt-- others are NMI and we won't - * see them here.... - */ - spin_lock_irq(&wd_dev.lock); - if((unsigned long)wd_dev.regs == (unsigned long)dev_id) - { - wd_stoptimer(&wd_dev.watchdog[WD0_ID]); - wd_dev.watchdog[WD0_ID].runstatus |= WD_STAT_SVCD; - } - spin_unlock_irq(&wd_dev.lock); - return IRQ_HANDLED; -} - -static const struct file_operations wd_fops = { - .owner = THIS_MODULE, - .ioctl = wd_ioctl, - .compat_ioctl = wd_compat_ioctl, - .open = wd_open, - .write = wd_write, - .read = wd_read, - .release = wd_release, -}; - -static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops }; -static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops }; -static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops }; - -#ifdef WD_DEBUG -static void wd_dumpregs(void) -{ - /* Reading from downcounters initiates watchdog countdown-- - * Example is included below for illustration purposes. - */ - int i; - printk("%s: dumping register values\n", WD_OBPNAME); - for(i = WD0_ID; i < WD_NUMDEVS; ++i) { - /* printk("\t%s%i: dcntr at 0x%lx: 0x%x\n", - * WD_OBPNAME, - * i, - * (unsigned long)(&wd_dev.watchdog[i].regs->dcntr), - * readw(&wd_dev.watchdog[i].regs->dcntr)); - */ - printk("\t%s%i: limit at 0x%lx: 0x%x\n", - WD_OBPNAME, - i, - (unsigned long)(&wd_dev.watchdog[i].regs->limit), - readw(&wd_dev.watchdog[i].regs->limit)); - printk("\t%s%i: status at 0x%lx: 0x%x\n", - WD_OBPNAME, - i, - (unsigned long)(&wd_dev.watchdog[i].regs->status), - readb(&wd_dev.watchdog[i].regs->status)); - printk("\t%s%i: driver status: 0x%x\n", - WD_OBPNAME, - i, - wd_getstatus(&wd_dev.watchdog[i])); - } - printk("\tintr_mask at %p: 0x%x\n", - wd_dev.regs + PLD_IMASK, - readb(wd_dev.regs + PLD_IMASK)); - printk("\tpld_status at %p: 0x%x\n", - wd_dev.regs + PLD_STATUS, - readb(wd_dev.regs + PLD_STATUS)); -} -#endif - -/* Enable or disable watchdog interrupts - * Because of the CP1400 defect this should only be - * called during initialzation or by wd_[start|stop]timer() - * - * pTimer - pointer to timer device, or NULL to indicate all timers - * enable - non-zero to enable interrupts, zero to disable - */ -static void wd_toggleintr(struct wd_timer* pTimer, int enable) -{ - unsigned char curregs = wd_readb(wd_dev.regs + PLD_IMASK); - unsigned char setregs = - (NULL == pTimer) ? - (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : - (pTimer->intr_mask); - - (WD_INTR_ON == enable) ? - (curregs &= ~setregs): - (curregs |= setregs); - - wd_writeb(curregs, wd_dev.regs + PLD_IMASK); - return; -} - -/* Reset countdown timer with 'limit' value and continue countdown. - * This will not start a stopped timer. - * - * pTimer - pointer to timer device - */ -static void wd_pingtimer(struct wd_timer* pTimer) -{ - if (wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) { - wd_readw(pTimer->regs + WD_DCNTR); - } -} - -/* Stop a running watchdog timer-- the timer actually keeps - * running, but the interrupt is masked so that no action is - * taken upon expiration. - * - * pTimer - pointer to timer device - */ -static void wd_stoptimer(struct wd_timer* pTimer) -{ - if(wd_readb(pTimer->regs + WD_STATUS) & WD_S_RUNNING) { - wd_toggleintr(pTimer, WD_INTR_OFF); - - if(wd_dev.isbaddoggie) { - pTimer->runstatus |= WD_STAT_BSTOP; - wd_brokentimer((unsigned long)&wd_dev); - } - } -} - -/* Start a watchdog timer with the specified limit value - * If the watchdog is running, it will be restarted with - * the provided limit value. - * - * This function will enable interrupts on the specified - * watchdog. - * - * pTimer - pointer to timer device - * limit - limit (countdown) value in 1/10th seconds - */ -static void wd_starttimer(struct wd_timer* pTimer) -{ - if(wd_dev.isbaddoggie) { - pTimer->runstatus &= ~WD_STAT_BSTOP; - } - pTimer->runstatus &= ~WD_STAT_SVCD; - - wd_writew(pTimer->timeout, pTimer->regs + WD_LIMIT); - wd_toggleintr(pTimer, WD_INTR_ON); -} - -/* Restarts timer with maximum limit value and - * does not unset 'brokenstop' value. - */ -static void wd_resetbrokentimer(struct wd_timer* pTimer) -{ - wd_toggleintr(pTimer, WD_INTR_ON); - wd_writew(WD_BLIMIT, pTimer->regs + WD_LIMIT); -} - -/* Timer device initialization helper. - * Returns 0 on success, other on failure - */ -static int wd_inittimer(int whichdog) -{ - struct miscdevice *whichmisc; - void __iomem *whichregs; - char whichident[8]; - int whichmask; - __u16 whichlimit; - - switch(whichdog) - { - case WD0_ID: - whichmisc = &wd0_miscdev; - strcpy(whichident, "RIC"); - whichregs = wd_dev.regs + WD0_OFF; - whichmask = WD0_INTR_MASK; - whichlimit= (0 == wd0_timeout) ? - (wd_dev.opt_timeout): - (wd0_timeout); - break; - case WD1_ID: - whichmisc = &wd1_miscdev; - strcpy(whichident, "XIR"); - whichregs = wd_dev.regs + WD1_OFF; - whichmask = WD1_INTR_MASK; - whichlimit= (0 == wd1_timeout) ? - (wd_dev.opt_timeout): - (wd1_timeout); - break; - case WD2_ID: - whichmisc = &wd2_miscdev; - strcpy(whichident, "POR"); - whichregs = wd_dev.regs + WD2_OFF; - whichmask = WD2_INTR_MASK; - whichlimit= (0 == wd2_timeout) ? - (wd_dev.opt_timeout): - (wd2_timeout); - break; - default: - printk("%s: %s: invalid watchdog id: %i\n", - WD_OBPNAME, __func__, whichdog); - return(1); - } - if(0 != misc_register(whichmisc)) - { - return(1); - } - wd_dev.watchdog[whichdog].regs = whichregs; - wd_dev.watchdog[whichdog].timeout = whichlimit; - wd_dev.watchdog[whichdog].intr_mask = whichmask; - wd_dev.watchdog[whichdog].runstatus &= ~WD_STAT_BSTOP; - wd_dev.watchdog[whichdog].runstatus |= WD_STAT_INIT; - - printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n", - WD_OBPNAME, - whichdog, - whichident, - wd_dev.watchdog[whichdog].timeout / 10, - wd_dev.watchdog[whichdog].timeout % 10, - (0 != wd_dev.opt_enable) ? "in ENABLED mode" : ""); - return(0); -} - -/* Timer method called to reset stopped watchdogs-- - * because of the PLD bug on CP1400, we cannot mask - * interrupts within the PLD so me must continually - * reset the timers ad infinitum. - */ -static void wd_brokentimer(unsigned long data) -{ - struct wd_device* pDev = (struct wd_device*)data; - int id, tripped = 0; - - /* kill a running timer instance, in case we - * were called directly instead of by kernel timer - */ - if(timer_pending(&wd_timer)) { - del_timer(&wd_timer); - } - - for(id = WD0_ID; id < WD_NUMDEVS; ++id) { - if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) { - ++tripped; - wd_resetbrokentimer(&pDev->watchdog[id]); - } - } - - if(tripped) { - /* there is at least one timer brokenstopped-- reschedule */ - init_timer(&wd_timer); - wd_timer.expires = WD_BTIMEOUT; - add_timer(&wd_timer); - } -} - -static int wd_getstatus(struct wd_timer* pTimer) -{ - unsigned char stat = wd_readb(pTimer->regs + WD_STATUS); - unsigned char intr = wd_readb(wd_dev.regs + PLD_IMASK); - unsigned char ret = WD_STOPPED; - - /* determine STOPPED */ - if(0 == stat ) { - return(ret); - } - /* determine EXPIRED vs FREERUN vs RUNNING */ - else if(WD_S_EXPIRED & stat) { - ret = WD_EXPIRED; - } - else if(WD_S_RUNNING & stat) { - if(intr & pTimer->intr_mask) { - ret = WD_FREERUN; - } - else { - /* Fudge WD_EXPIRED status for defective CP1400-- - * IF timer is running - * AND brokenstop is set - * AND an interrupt has been serviced - * we are WD_EXPIRED. - * - * IF timer is running - * AND brokenstop is set - * AND no interrupt has been serviced - * we are WD_FREERUN. - */ - if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) { - if(pTimer->runstatus & WD_STAT_SVCD) { - ret = WD_EXPIRED; - } - else { - /* we could as well pretend we are expired */ - ret = WD_FREERUN; - } - } - else { - ret = WD_RUNNING; - } - } - } - - /* determine SERVICED */ - if(pTimer->runstatus & WD_STAT_SVCD) { - ret |= WD_SERVICED; - } - - return(ret); -} - -static int __init wd_init(void) -{ - int id; - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->ofdev.node->name, WD_OBPNAME)) - goto ebus_done; - } - } - -ebus_done: - if(!edev) { - printk("%s: unable to locate device\n", WD_OBPNAME); - return -ENODEV; - } - - wd_dev.regs = - ioremap(edev->resource[0].start, 4 * WD_TIMER_REGSZ); /* ? */ - - if(NULL == wd_dev.regs) { - printk("%s: unable to map registers\n", WD_OBPNAME); - return(-ENODEV); - } - - /* initialize device structure from OBP parameters */ - wd_dev.irq = edev->irqs[0]; - wd_dev.opt_enable = wd_opt_enable(); - wd_dev.opt_reboot = wd_opt_reboot(); - wd_dev.opt_timeout = wd_opt_timeout(); - wd_dev.isbaddoggie = wd_isbroken(); - - /* disable all interrupts unless watchdog-enabled? == true */ - if(! wd_dev.opt_enable) { - wd_toggleintr(NULL, WD_INTR_OFF); - } - - /* register miscellaneous devices */ - for(id = WD0_ID; id < WD_NUMDEVS; ++id) { - if(0 != wd_inittimer(id)) { - printk("%s%i: unable to initialize\n", WD_OBPNAME, id); - } - } - - /* warn about possible defective PLD */ - if(wd_dev.isbaddoggie) { - init_timer(&wd_timer); - wd_timer.function = wd_brokentimer; - wd_timer.data = (unsigned long)&wd_dev; - wd_timer.expires = WD_BTIMEOUT; - - printk("%s: PLD defect workaround enabled for model %s\n", - WD_OBPNAME, WD_BADMODEL); - } - return(0); -} - -static void __exit wd_cleanup(void) -{ - int id; - - /* if 'watchdog-enable?' == TRUE, timers are not stopped - * when module is unloaded. All brokenstopped timers will - * also now eventually trip. - */ - for(id = WD0_ID; id < WD_NUMDEVS; ++id) { - if(WD_S_RUNNING == wd_readb(wd_dev.watchdog[id].regs + WD_STATUS)) { - if(wd_dev.opt_enable) { - printk(KERN_WARNING "%s%i: timer not stopped at release\n", - WD_OBPNAME, id); - } - else { - wd_stoptimer(&wd_dev.watchdog[id]); - if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) { - wd_resetbrokentimer(&wd_dev.watchdog[id]); - printk(KERN_WARNING - "%s%i: defect workaround disabled at release, "\ - "timer expires in ~%01i sec\n", - WD_OBPNAME, id, - wd_readw(wd_dev.watchdog[id].regs + WD_LIMIT) / 10); - } - } - } - } - - if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) { - del_timer(&wd_timer); - } - if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) { - misc_deregister(&wd0_miscdev); - } - if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) { - misc_deregister(&wd1_miscdev); - } - if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) { - misc_deregister(&wd2_miscdev); - } - if(0 != wd_dev.initialized) { - free_irq(wd_dev.irq, (void *)wd_dev.regs); - } - iounmap(wd_dev.regs); -} - -module_init(wd_init); -module_exit(wd_cleanup); diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index d8f5c0ca236..2550af4ae43 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -1,10 +1,7 @@ -/* $Id: display7seg.c,v 1.6 2002/01/08 16:00:16 davem Exp $ - * - * display7seg - Driver implementation for the 7-segment display - * present on Sun Microsystems CP1400 and CP1500 +/* display7seg.c - Driver implementation for the 7-segment display + * present on Sun Microsystems CP1400 and CP1500 * * Copyright (c) 2000 Eric Brower (ebrower@usa.net) - * */ #include <linux/kernel.h> @@ -16,22 +13,20 @@ #include <linux/miscdevice.h> #include <linux/ioport.h> /* request_region */ #include <linux/smp_lock.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/atomic.h> -#include <asm/ebus.h> /* EBus device */ -#include <asm/oplib.h> /* OpenProm Library */ #include <asm/uaccess.h> /* put_/get_user */ #include <asm/io.h> #include <asm/display7seg.h> #define D7S_MINOR 193 -#define D7S_OBPNAME "display7seg" -#define D7S_DEVNAME "d7s" +#define DRIVER_NAME "d7s" +#define PFX DRIVER_NAME ": " static int sol_compat = 0; /* Solaris compatibility mode */ -#ifdef MODULE - /* Solaris compatibility flag - * The Solaris implementation omits support for several * documented driver features (ref Sun doc 806-0180-03). @@ -46,20 +41,20 @@ static int sol_compat = 0; /* Solaris compatibility mode */ * If you wish the device to operate as under Solaris, * omitting above features, set this parameter to non-zero. */ -module_param - (sol_compat, int, 0); -MODULE_PARM_DESC - (sol_compat, - "Disables documented functionality omitted from Solaris driver"); - -MODULE_AUTHOR - ("Eric Brower <ebrower@usa.net>"); -MODULE_DESCRIPTION - ("7-Segment Display driver for Sun Microsystems CP1400/1500"); +module_param(sol_compat, int, 0); +MODULE_PARM_DESC(sol_compat, + "Disables documented functionality omitted from Solaris driver"); + +MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); +MODULE_DESCRIPTION("7-Segment Display driver for Sun Microsystems CP1400/1500"); MODULE_LICENSE("GPL"); -MODULE_SUPPORTED_DEVICE - ("d7s"); -#endif /* ifdef MODULE */ +MODULE_SUPPORTED_DEVICE("d7s"); + +struct d7s { + void __iomem *regs; + bool flipped; +}; +struct d7s *d7s_device; /* * Register block address- see header for details @@ -72,22 +67,6 @@ MODULE_SUPPORTED_DEVICE * FLIP - Inverts display for upside-down mounted board * bits 0-4 - 7-segment display contents */ -static void __iomem* d7s_regs; - -static inline void d7s_free(void) -{ - iounmap(d7s_regs); -} - -static inline int d7s_obpflipped(void) -{ - int opt_node; - - opt_node = prom_getchild(prom_root_node); - opt_node = prom_searchsiblings(opt_node, "options"); - return ((-1 != prom_getintdefault(opt_node, "d7s-flipped?", -1)) ? 0 : 1); -} - static atomic_t d7s_users = ATOMIC_INIT(0); static int d7s_open(struct inode *inode, struct file *f) @@ -106,12 +85,15 @@ static int d7s_release(struct inode *inode, struct file *f) * are not operating in solaris-compat mode */ if (atomic_dec_and_test(&d7s_users) && !sol_compat) { - int regval = 0; - - regval = readb(d7s_regs); - (0 == d7s_obpflipped()) ? - writeb(regval |= D7S_FLIP, d7s_regs): - writeb(regval &= ~D7S_FLIP, d7s_regs); + struct d7s *p = d7s_device; + u8 regval = 0; + + regval = readb(p->regs); + if (p->flipped) + regval |= D7S_FLIP; + else + regval &= ~D7S_FLIP; + writeb(regval, p->regs); } return 0; @@ -119,9 +101,10 @@ static int d7s_release(struct inode *inode, struct file *f) static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - __u8 regs = readb(d7s_regs); - __u8 ireg = 0; + struct d7s *p = d7s_device; + u8 regs = readb(p->regs); int error = 0; + u8 ireg = 0; if (D7S_MINOR != iminor(file->f_path.dentry->d_inode)) return -ENODEV; @@ -129,18 +112,20 @@ static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg) lock_kernel(); switch (cmd) { case D7SIOCWR: - /* assign device register values - * we mask-out D7S_FLIP if in sol_compat mode + /* assign device register values we mask-out D7S_FLIP + * if in sol_compat mode */ if (get_user(ireg, (int __user *) arg)) { error = -EFAULT; break; } - if (0 != sol_compat) { - (regs & D7S_FLIP) ? - (ireg |= D7S_FLIP) : (ireg &= ~D7S_FLIP); + if (sol_compat) { + if (regs & D7S_FLIP) + ireg |= D7S_FLIP; + else + ireg &= ~D7S_FLIP; } - writeb(ireg, d7s_regs); + writeb(ireg, p->regs); break; case D7SIOCRD: @@ -158,9 +143,11 @@ static long d7s_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case D7SIOCTM: /* toggle device mode-- flip display orientation */ - (regs & D7S_FLIP) ? - (regs &= ~D7S_FLIP) : (regs |= D7S_FLIP); - writeb(regs, d7s_regs); + if (regs & D7S_FLIP) + regs &= ~D7S_FLIP; + else + regs |= D7S_FLIP; + writeb(regs, p->regs); break; }; unlock_kernel(); @@ -176,69 +163,123 @@ static const struct file_operations d7s_fops = { .release = d7s_release, }; -static struct miscdevice d7s_miscdev = { D7S_MINOR, D7S_DEVNAME, &d7s_fops }; +static struct miscdevice d7s_miscdev = { + .minor = D7S_MINOR, + .name = DRIVER_NAME, + .fops = &d7s_fops +}; -static int __init d7s_init(void) +static int __devinit d7s_probe(struct of_device *op, + const struct of_device_id *match) { - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; - int iTmp = 0, regs = 0; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, D7S_OBPNAME)) - goto ebus_done; - } + struct device_node *opts; + int err = -EINVAL; + struct d7s *p; + u8 regs; + + if (d7s_device) + goto out; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + err = -ENOMEM; + if (!p) + goto out; + + p->regs = of_ioremap(&op->resource[0], 0, sizeof(u8), "d7s"); + if (!p->regs) { + printk(KERN_ERR PFX "Cannot map chip registers\n"); + goto out_free; } -ebus_done: - if(!edev) { - printk("%s: unable to locate device\n", D7S_DEVNAME); - return -ENODEV; + err = misc_register(&d7s_miscdev); + if (err) { + printk(KERN_ERR PFX "Unable to acquire miscdevice minor %i\n", + D7S_MINOR); + goto out_iounmap; } - d7s_regs = ioremap(edev->resource[0].start, sizeof(__u8)); - - iTmp = misc_register(&d7s_miscdev); - if (0 != iTmp) { - printk("%s: unable to acquire miscdevice minor %i\n", - D7S_DEVNAME, D7S_MINOR); - iounmap(d7s_regs); - return iTmp; - } - - /* OBP option "d7s-flipped?" is honored as default - * for the device, and reset default when detached + /* OBP option "d7s-flipped?" is honored as default for the + * device, and reset default when detached */ - regs = readb(d7s_regs); - iTmp = d7s_obpflipped(); - (0 == iTmp) ? - writeb(regs |= D7S_FLIP, d7s_regs): - writeb(regs &= ~D7S_FLIP, d7s_regs); - - printk("%s: 7-Segment Display%s at 0x%lx %s\n", - D7S_DEVNAME, - (0 == iTmp) ? (" (FLIPPED)") : (""), - edev->resource[0].start, - (0 != sol_compat) ? ("in sol_compat mode") : ("")); - - return 0; + regs = readb(p->regs); + opts = of_find_node_by_path("/options"); + if (opts && + of_get_property(opts, "d7s-flipped?", NULL)) + p->flipped = true; + + if (p->flipped) + regs |= D7S_FLIP; + else + regs &= ~D7S_FLIP; + + writeb(regs, p->regs); + + printk(KERN_INFO PFX "7-Segment Display%s at [%s:0x%lx] %s\n", + op->node->full_name, + (regs & D7S_FLIP) ? " (FLIPPED)" : "", + op->resource[0].start, + sol_compat ? "in sol_compat mode" : ""); + + dev_set_drvdata(&op->dev, p); + d7s_device = p; + err = 0; + +out: + return err; + +out_iounmap: + of_iounmap(&op->resource[0], p->regs, sizeof(u8)); + +out_free: + kfree(p); + goto out; } -static void __exit d7s_cleanup(void) +static int __devexit d7s_remove(struct of_device *op) { - int regs = readb(d7s_regs); + struct d7s *p = dev_get_drvdata(&op->dev); + u8 regs = readb(p->regs); /* Honor OBP d7s-flipped? unless operating in solaris-compat mode */ - if (0 == sol_compat) { - (0 == d7s_obpflipped()) ? - writeb(regs |= D7S_FLIP, d7s_regs): - writeb(regs &= ~D7S_FLIP, d7s_regs); + if (sol_compat) { + if (p->flipped) + regs |= D7S_FLIP; + else + regs &= ~D7S_FLIP; + writeb(regs, p->regs); } misc_deregister(&d7s_miscdev); - d7s_free(); + of_iounmap(&op->resource[0], p->regs, sizeof(u8)); + kfree(p); + + return 0; +} + +static const struct of_device_id d7s_match[] = { + { + .name = "display7seg", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, d7s_match); + +static struct of_platform_driver d7s_driver = { + .name = DRIVER_NAME, + .match_table = d7s_match, + .probe = d7s_probe, + .remove = __devexit_p(d7s_remove), +}; + +static int __init d7s_init(void) +{ + return of_register_driver(&d7s_driver, &of_bus_type); +} + +static void __exit d7s_exit(void) +{ + of_unregister_driver(&d7s_driver); } module_init(d7s_init); -module_exit(d7s_cleanup); +module_exit(d7s_exit); diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index a408402426f..58e583b61e6 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -1,5 +1,4 @@ -/* $Id: envctrl.c,v 1.25 2002/01/15 09:01:26 davem Exp $ - * envctrl.c: Temperature and Fan monitoring on Machines providing it. +/* envctrl.c: Temperature and Fan monitoring on Machines providing it. * * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2000 Vinh Truong (vinh.truong@eng.sun.com) @@ -28,12 +27,16 @@ #include <linux/kmod.h> #include <linux/reboot.h> #include <linux/smp_lock.h> +#include <linux/of.h> +#include <linux/of_device.h> -#include <asm/ebus.h> #include <asm/uaccess.h> #include <asm/envctrl.h> #include <asm/io.h> +#define DRIVER_NAME "envctrl" +#define PFX DRIVER_NAME ": " + #define ENVCTRL_MINOR 162 #define PCF8584_ADDRESS 0x55 @@ -193,7 +196,7 @@ static void envtrl_i2c_test_pin(void) } if (limit <= 0) - printk(KERN_INFO "envctrl: Pin status will not clear.\n"); + printk(KERN_INFO PFX "Pin status will not clear.\n"); } /* Function Description: Test busy bit. @@ -211,7 +214,7 @@ static void envctrl_i2c_test_bb(void) } if (limit <= 0) - printk(KERN_INFO "envctrl: Busy bit will not clear.\n"); + printk(KERN_INFO PFX "Busy bit will not clear.\n"); } /* Function Description: Send the address for a read access. @@ -858,11 +861,10 @@ static void envctrl_init_voltage_status(struct i2c_child_t *pchild) /* Function Description: Initialize i2c child device. * Return: None. */ -static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child, +static void envctrl_init_i2c_child(struct device_node *dp, struct i2c_child_t *pchild) { int len, i, tbls_size = 0; - struct device_node *dp = edev_child->prom_node; const void *pval; /* Get device address. */ @@ -882,12 +884,12 @@ static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child, pchild->tables = kmalloc(tbls_size, GFP_KERNEL); if (pchild->tables == NULL){ - printk("envctrl: Failed to allocate table.\n"); + printk(KERN_ERR PFX "Failed to allocate table.\n"); return; } pval = of_get_property(dp, "tables", &len); if (!pval || len <= 0) { - printk("envctrl: Failed to get table.\n"); + printk(KERN_ERR PFX "Failed to get table.\n"); return; } memcpy(pchild->tables, pval, len); @@ -993,14 +995,14 @@ static int kenvctrld(void *__unused) struct i2c_child_t *cputemp; if (NULL == (cputemp = envctrl_get_i2c_child(ENVCTRL_CPUTEMP_MON))) { - printk(KERN_ERR - "envctrl: kenvctrld unable to monitor CPU temp-- exiting\n"); + printk(KERN_ERR PFX + "kenvctrld unable to monitor CPU temp-- exiting\n"); return -ENODEV; } poll_interval = 5000; /* TODO env_mon_interval */ - printk(KERN_INFO "envctrl: %s starting...\n", current->comm); + printk(KERN_INFO PFX "%s starting...\n", current->comm); for (;;) { msleep_interruptible(poll_interval); @@ -1022,54 +1024,35 @@ static int kenvctrld(void *__unused) } } } - printk(KERN_INFO "envctrl: %s exiting...\n", current->comm); + printk(KERN_INFO PFX "%s exiting...\n", current->comm); return 0; } -static int __init envctrl_init(void) +static int __devinit envctrl_probe(struct of_device *op, + const struct of_device_id *match) { - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; - struct linux_ebus_child *edev_child = NULL; - int err, i = 0; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "bbc")) { - /* If we find a boot-bus controller node, - * then this envctrl driver is not for us. - */ - return -ENODEV; - } - } - } + struct device_node *dp; + int index, err; - /* Traverse through ebus and ebus device list for i2c device and - * adc and gpio nodes. - */ - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "i2c")) { - i2c = ioremap(edev->resource[0].start, 0x2); - for_each_edevchild(edev, edev_child) { - if (!strcmp("gpio", edev_child->prom_node->name)) { - i2c_childlist[i].i2ctype = I2C_GPIO; - envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++])); - } - if (!strcmp("adc", edev_child->prom_node->name)) { - i2c_childlist[i].i2ctype = I2C_ADC; - envctrl_init_i2c_child(edev_child, &(i2c_childlist[i++])); - } - } - goto done; - } + if (i2c) + return -EINVAL; + + i2c = of_ioremap(&op->resource[0], 0, 0x2, DRIVER_NAME); + if (!i2c) + return -ENOMEM; + + index = 0; + dp = op->node->child; + while (dp) { + if (!strcmp(dp->name, "gpio")) { + i2c_childlist[index].i2ctype = I2C_GPIO; + envctrl_init_i2c_child(dp, &(i2c_childlist[index++])); + } else if (!strcmp(dp->name, "adc")) { + i2c_childlist[index].i2ctype = I2C_ADC; + envctrl_init_i2c_child(dp, &(i2c_childlist[index++])); } - } -done: - if (!edev) { - printk("envctrl: I2C device not found.\n"); - return -ENODEV; + dp = dp->sibling; } /* Set device address. */ @@ -1087,7 +1070,7 @@ done: /* Register the device as a minor miscellaneous device. */ err = misc_register(&envctrl_dev); if (err) { - printk("envctrl: Unable to get misc minor %d\n", + printk(KERN_ERR PFX "Unable to get misc minor %d\n", envctrl_dev.minor); goto out_iounmap; } @@ -1096,12 +1079,12 @@ done: * a next child device, so we decrement before reverse-traversal of * child devices. */ - printk("envctrl: initialized "); - for (--i; i >= 0; --i) { + printk(KERN_INFO PFX "Initialized "); + for (--index; index >= 0; --index) { printk("[%s 0x%lx]%s", - (I2C_ADC == i2c_childlist[i].i2ctype) ? ("adc") : - ((I2C_GPIO == i2c_childlist[i].i2ctype) ? ("gpio") : ("unknown")), - i2c_childlist[i].addr, (0 == i) ? ("\n") : (" ")); + (I2C_ADC == i2c_childlist[index].i2ctype) ? "adc" : + ((I2C_GPIO == i2c_childlist[index].i2ctype) ? "gpio" : "unknown"), + i2c_childlist[index].addr, (0 == index) ? "\n" : " "); } kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld"); @@ -1115,26 +1098,54 @@ done: out_deregister: misc_deregister(&envctrl_dev); out_iounmap: - iounmap(i2c); - for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) - kfree(i2c_childlist[i].tables); + of_iounmap(&op->resource[0], i2c, 0x2); + for (index = 0; index < ENVCTRL_MAX_CPU * 2; index++) + kfree(i2c_childlist[index].tables); return err; } -static void __exit envctrl_cleanup(void) +static int __devexit envctrl_remove(struct of_device *op) { - int i; + int index; kthread_stop(kenvctrld_task); - iounmap(i2c); + of_iounmap(&op->resource[0], i2c, 0x2); misc_deregister(&envctrl_dev); - for (i = 0; i < ENVCTRL_MAX_CPU * 2; i++) - kfree(i2c_childlist[i].tables); + for (index = 0; index < ENVCTRL_MAX_CPU * 2; index++) + kfree(i2c_childlist[index].tables); + + return 0; +} + +static const struct of_device_id envctrl_match[] = { + { + .name = "i2c", + .compatible = "i2cpcf,8584", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, envctrl_match); + +static struct of_platform_driver envctrl_driver = { + .name = DRIVER_NAME, + .match_table = envctrl_match, + .probe = envctrl_probe, + .remove = __devexit_p(envctrl_remove), +}; + +static int __init envctrl_init(void) +{ + return of_register_driver(&envctrl_driver, &of_bus_type); +} + +static void __exit envctrl_exit(void) +{ + of_unregister_driver(&envctrl_driver); } module_init(envctrl_init); -module_exit(envctrl_cleanup); +module_exit(envctrl_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 7d95e151513..41083472ff4 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -1,5 +1,4 @@ -/* $Id: flash.c,v 1.25 2001/12/21 04:56:16 davem Exp $ - * flash.c: Allow mmap access to the OBP Flash, for OBP updates. +/* flash.c: Allow mmap access to the OBP Flash, for OBP updates. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) */ @@ -15,13 +14,13 @@ #include <linux/smp_lock.h> #include <linux/spinlock.h> #include <linux/mm.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/system.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <asm/io.h> -#include <asm/sbus.h> -#include <asm/ebus.h> #include <asm/upa.h> static DEFINE_SPINLOCK(flash_lock); @@ -161,97 +160,68 @@ static const struct file_operations flash_fops = { static struct miscdevice flash_dev = { FLASH_MINOR, "flash", &flash_fops }; -static int __init flash_init(void) +static int __devinit flash_probe(struct of_device *op, + const struct of_device_id *match) { - struct sbus_bus *sbus; - struct sbus_dev *sdev = NULL; -#ifdef CONFIG_PCI - struct linux_ebus *ebus; - struct linux_ebus_device *edev = NULL; - struct linux_prom_registers regs[2]; - int len, nregs; -#endif - int err; - - for_all_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, "flashprom")) { - if (sdev->reg_addrs[0].phys_addr == sdev->reg_addrs[1].phys_addr) { - flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | - (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); - flash.read_size = sdev->reg_addrs[0].reg_size; - flash.write_base = flash.read_base; - flash.write_size = flash.read_size; - } else { - flash.read_base = ((unsigned long)sdev->reg_addrs[0].phys_addr) | - (((unsigned long)sdev->reg_addrs[0].which_io)<<32UL); - flash.read_size = sdev->reg_addrs[0].reg_size; - flash.write_base = ((unsigned long)sdev->reg_addrs[1].phys_addr) | - (((unsigned long)sdev->reg_addrs[1].which_io)<<32UL); - flash.write_size = sdev->reg_addrs[1].reg_size; - } - flash.busy = 0; - break; - } - } - if (!sdev) { -#ifdef CONFIG_PCI - const struct linux_prom_registers *ebus_regs; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->prom_node->name, "flashprom")) - goto ebus_done; - } - } - ebus_done: - if (!edev) - return -ENODEV; - - ebus_regs = of_get_property(edev->prom_node, "reg", &len); - if (!ebus_regs || (len % sizeof(regs[0])) != 0) { - printk("flash: Strange reg property size %d\n", len); - return -ENODEV; - } - - nregs = len / sizeof(ebus_regs[0]); + struct device_node *dp = op->node; + struct device_node *parent; - flash.read_base = edev->resource[0].start; - flash.read_size = ebus_regs[0].reg_size; + parent = dp->parent; - if (nregs == 1) { - flash.write_base = edev->resource[0].start; - flash.write_size = ebus_regs[0].reg_size; - } else if (nregs == 2) { - flash.write_base = edev->resource[1].start; - flash.write_size = ebus_regs[1].reg_size; - } else { - printk("flash: Strange number of regs %d\n", nregs); - return -ENODEV; - } - - flash.busy = 0; - -#else + if (strcmp(parent->name, "sbus") && + strcmp(parent->name, "sbi") && + strcmp(parent->name, "ebus")) return -ENODEV; -#endif + + flash.read_base = op->resource[0].start; + flash.read_size = resource_size(&op->resource[0]); + if (op->resource[1].flags) { + flash.write_base = op->resource[1].start; + flash.write_size = resource_size(&op->resource[1]); + } else { + flash.write_base = op->resource[0].start; + flash.write_size = resource_size(&op->resource[0]); } + flash.busy = 0; - printk("OBP Flash: RD %lx[%lx] WR %lx[%lx]\n", + printk(KERN_INFO "%s: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n", + op->node->full_name, flash.read_base, flash.read_size, flash.write_base, flash.write_size); - err = misc_register(&flash_dev); - if (err) { - printk(KERN_ERR "flash: unable to get misc minor\n"); - return err; - } + return misc_register(&flash_dev); +} + +static int __devexit flash_remove(struct of_device *op) +{ + misc_deregister(&flash_dev); return 0; } +static const struct of_device_id flash_match[] = { + { + .name = "flashprom", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, flash_match); + +static struct of_platform_driver flash_driver = { + .name = "flash", + .match_table = flash_match, + .probe = flash_probe, + .remove = __devexit_p(flash_remove), +}; + +static int __init flash_init(void) +{ + return of_register_driver(&flash_driver, &of_bus_type); +} + static void __exit flash_cleanup(void) { - misc_deregister(&flash_dev); + of_unregister_driver(&flash_driver); } module_init(flash_init); diff --git a/drivers/sbus/char/rtc.c b/drivers/sbus/char/rtc.c deleted file mode 100644 index b0429917154..00000000000 --- a/drivers/sbus/char/rtc.c +++ /dev/null @@ -1,275 +0,0 @@ -/* $Id: rtc.c,v 1.28 2001/10/08 22:19:51 davem Exp $ - * - * Linux/SPARC Real Time Clock Driver - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - * - * This is a little driver that lets a user-level program access - * the SPARC Mostek real time clock chip. It is no use unless you - * use the modified clock utility. - * - * Get the modified clock utility from: - * ftp://vger.kernel.org/pub/linux/Sparc/userland/clock.c - */ - -#include <linux/module.h> -#include <linux/smp_lock.h> -#include <linux/types.h> -#include <linux/errno.h> -#include <linux/miscdevice.h> -#include <linux/slab.h> -#include <linux/fcntl.h> -#include <linux/poll.h> -#include <linux/init.h> -#include <asm/io.h> -#include <asm/mostek.h> -#include <asm/system.h> -#include <asm/uaccess.h> -#include <asm/rtc.h> - -static int rtc_busy = 0; - -/* This is the structure layout used by drivers/char/rtc.c, we - * support that driver's ioctls so that things are less messy in - * userspace. - */ -struct rtc_time_generic { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; -}; -#define RTC_AIE_ON _IO('p', 0x01) /* Alarm int. enable on */ -#define RTC_AIE_OFF _IO('p', 0x02) /* ... off */ -#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ -#define RTC_UIE_OFF _IO('p', 0x04) /* ... off */ -#define RTC_PIE_ON _IO('p', 0x05) /* Periodic int. enable on */ -#define RTC_PIE_OFF _IO('p', 0x06) /* ... off */ -#define RTC_WIE_ON _IO('p', 0x0f) /* Watchdog int. enable on */ -#define RTC_WIE_OFF _IO('p', 0x10) /* ... off */ -#define RTC_RD_TIME _IOR('p', 0x09, struct rtc_time_generic) /* Read RTC time */ -#define RTC_SET_TIME _IOW('p', 0x0a, struct rtc_time_generic) /* Set RTC time */ -#define RTC_ALM_SET _IOW('p', 0x07, struct rtc_time) /* Set alarm time */ -#define RTC_ALM_READ _IOR('p', 0x08, struct rtc_time) /* Read alarm time */ -#define RTC_IRQP_READ _IOR('p', 0x0b, unsigned long) /* Read IRQ rate */ -#define RTC_IRQP_SET _IOW('p', 0x0c, unsigned long) /* Set IRQ rate */ -#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ -#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ -#define RTC_WKALM_SET _IOW('p', 0x0f, struct rtc_wkalrm)/* Set wakeup alarm*/ -#define RTC_WKALM_RD _IOR('p', 0x10, struct rtc_wkalrm)/* Get wakeup alarm*/ -#define RTC_PLL_GET _IOR('p', 0x11, struct rtc_pll_info) /* Get PLL correction */ -#define RTC_PLL_SET _IOW('p', 0x12, struct rtc_pll_info) /* Set PLL correction */ - -/* Retrieve the current date and time from the real time clock. */ -static void get_rtc_time(struct rtc_time *t) -{ - void __iomem *regs = mstk48t02_regs; - u8 tmp; - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); - - t->sec = MSTK_REG_SEC(regs); - t->min = MSTK_REG_MIN(regs); - t->hour = MSTK_REG_HOUR(regs); - t->dow = MSTK_REG_DOW(regs); - t->dom = MSTK_REG_DOM(regs); - t->month = MSTK_REG_MONTH(regs); - t->year = MSTK_CVT_YEAR( MSTK_REG_YEAR(regs) ); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_READ; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); -} - -/* Set the current date and time inthe real time clock. */ -void set_rtc_time(struct rtc_time *t) -{ - void __iomem *regs = mstk48t02_regs; - u8 tmp; - - spin_lock_irq(&mostek_lock); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp |= MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - MSTK_SET_REG_SEC(regs,t->sec); - MSTK_SET_REG_MIN(regs,t->min); - MSTK_SET_REG_HOUR(regs,t->hour); - MSTK_SET_REG_DOW(regs,t->dow); - MSTK_SET_REG_DOM(regs,t->dom); - MSTK_SET_REG_MONTH(regs,t->month); - MSTK_SET_REG_YEAR(regs,t->year - MSTK_YEAR_ZERO); - - tmp = mostek_read(regs + MOSTEK_CREG); - tmp &= ~MSTK_CREG_WRITE; - mostek_write(regs + MOSTEK_CREG, tmp); - - spin_unlock_irq(&mostek_lock); -} - -static int put_rtc_time_generic(void __user *argp, struct rtc_time *tm) -{ - struct rtc_time_generic __user *utm = argp; - - if (__put_user(tm->sec, &utm->tm_sec) || - __put_user(tm->min, &utm->tm_min) || - __put_user(tm->hour, &utm->tm_hour) || - __put_user(tm->dom, &utm->tm_mday) || - __put_user(tm->month, &utm->tm_mon) || - __put_user(tm->year, &utm->tm_year) || - __put_user(tm->dow, &utm->tm_wday) || - __put_user(0, &utm->tm_yday) || - __put_user(0, &utm->tm_isdst)) - return -EFAULT; - - return 0; -} - -static int get_rtc_time_generic(struct rtc_time *tm, void __user *argp) -{ - struct rtc_time_generic __user *utm = argp; - - if (__get_user(tm->sec, &utm->tm_sec) || - __get_user(tm->min, &utm->tm_min) || - __get_user(tm->hour, &utm->tm_hour) || - __get_user(tm->dom, &utm->tm_mday) || - __get_user(tm->month, &utm->tm_mon) || - __get_user(tm->year, &utm->tm_year) || - __get_user(tm->dow, &utm->tm_wday)) - return -EFAULT; - - return 0; -} - -static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct rtc_time rtc_tm; - void __user *argp = (void __user *)arg; - - switch (cmd) { - /* No interrupt support, return an error - * compatible with drivers/char/rtc.c - */ - case RTC_AIE_OFF: - case RTC_AIE_ON: - case RTC_PIE_OFF: - case RTC_PIE_ON: - case RTC_UIE_OFF: - case RTC_UIE_ON: - case RTC_IRQP_READ: - case RTC_IRQP_SET: - case RTC_EPOCH_SET: - case RTC_EPOCH_READ: - return -EINVAL; - - case RTCGET: - case RTC_RD_TIME: - memset(&rtc_tm, 0, sizeof(struct rtc_time)); - get_rtc_time(&rtc_tm); - - if (cmd == RTCGET) { - if (copy_to_user(argp, &rtc_tm, - sizeof(struct rtc_time))) - return -EFAULT; - } else if (put_rtc_time_generic(argp, &rtc_tm)) - return -EFAULT; - - return 0; - - - case RTCSET: - case RTC_SET_TIME: - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (cmd == RTCSET) { - if (copy_from_user(&rtc_tm, argp, - sizeof(struct rtc_time))) - return -EFAULT; - } else if (get_rtc_time_generic(&rtc_tm, argp)) - return -EFAULT; - - set_rtc_time(&rtc_tm); - - return 0; - - default: - return -EINVAL; - } -} - -static int rtc_open(struct inode *inode, struct file *file) -{ - int ret; - - lock_kernel(); - spin_lock_irq(&mostek_lock); - if (rtc_busy) { - ret = -EBUSY; - } else { - rtc_busy = 1; - ret = 0; - } - spin_unlock_irq(&mostek_lock); - unlock_kernel(); - - return ret; -} - -static int rtc_release(struct inode *inode, struct file *file) -{ - rtc_busy = 0; - - return 0; -} - -static const struct file_operations rtc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = rtc_ioctl, - .open = rtc_open, - .release = rtc_release, -}; - -static struct miscdevice rtc_dev = { RTC_MINOR, "rtc", &rtc_fops }; - -static int __init rtc_sun_init(void) -{ - int error; - - /* It is possible we are being driven by some other RTC chip - * and thus another RTC driver is handling things. - */ - if (!mstk48t02_regs) - return -ENODEV; - - error = misc_register(&rtc_dev); - if (error) { - printk(KERN_ERR "rtc: unable to get misc minor for Mostek\n"); - return error; - } - printk("rtc_sun_init: Registered Mostek RTC driver.\n"); - - return 0; -} - -static void __exit rtc_sun_cleanup(void) -{ - misc_deregister(&rtc_dev); -} - -module_init(rtc_sun_init); -module_exit(rtc_sun_cleanup); -MODULE_LICENSE("GPL"); diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 777637594ac..27993c37775 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -1,7 +1,7 @@ -/* $Id: uctrl.c,v 1.12 2001/10/08 22:19:51 davem Exp $ - * uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 +/* uctrl.c: TS102 Microcontroller interface on Tadpole Sparcbook 3 * * Copyright 1999 Derrick J Brashear (shadow@dementia.org) + * Copyright 2008 David S. Miller (davem@davemloft.net) */ #include <linux/module.h> @@ -14,6 +14,8 @@ #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/mm.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/openprom.h> #include <asm/oplib.h> @@ -21,7 +23,6 @@ #include <asm/irq.h> #include <asm/io.h> #include <asm/pgtable.h> -#include <asm/sbus.h> #define UCTRL_MINOR 174 @@ -33,26 +34,26 @@ #endif struct uctrl_regs { - volatile u32 uctrl_intr; - volatile u32 uctrl_data; - volatile u32 uctrl_stat; - volatile u32 uctrl_xxx[5]; + u32 uctrl_intr; + u32 uctrl_data; + u32 uctrl_stat; + u32 uctrl_xxx[5]; }; struct ts102_regs { - volatile u32 card_a_intr; - volatile u32 card_a_stat; - volatile u32 card_a_ctrl; - volatile u32 card_a_xxx; - volatile u32 card_b_intr; - volatile u32 card_b_stat; - volatile u32 card_b_ctrl; - volatile u32 card_b_xxx; - volatile u32 uctrl_intr; - volatile u32 uctrl_data; - volatile u32 uctrl_stat; - volatile u32 uctrl_xxx; - volatile u32 ts102_xxx[4]; + u32 card_a_intr; + u32 card_a_stat; + u32 card_a_ctrl; + u32 card_a_xxx; + u32 card_b_intr; + u32 card_b_stat; + u32 card_b_ctrl; + u32 card_b_xxx; + u32 uctrl_intr; + u32 uctrl_data; + u32 uctrl_stat; + u32 uctrl_xxx; + u32 ts102_xxx[4]; }; /* Bits for uctrl_intr register */ @@ -186,17 +187,15 @@ enum uctrl_opcode { POWER_RESTART=0x83, }; -struct uctrl_driver { - struct uctrl_regs *regs; +static struct uctrl_driver { + struct uctrl_regs __iomem *regs; int irq; int pending; struct uctrl_status status; -}; - -static struct uctrl_driver drv; +} *global_driver; -static void uctrl_get_event_status(void); -static void uctrl_get_external_status(void); +static void uctrl_get_event_status(struct uctrl_driver *); +static void uctrl_get_external_status(struct uctrl_driver *); static int uctrl_ioctl(struct inode *inode, struct file *file, @@ -213,16 +212,14 @@ static int uctrl_open(struct inode *inode, struct file *file) { lock_kernel(); - uctrl_get_event_status(); - uctrl_get_external_status(); + uctrl_get_event_status(global_driver); + uctrl_get_external_status(global_driver); unlock_kernel(); return 0; } static irqreturn_t uctrl_interrupt(int irq, void *dev_id) { - struct uctrl_driver *driver = (struct uctrl_driver *)dev_id; - printk("in uctrl_interrupt\n"); return IRQ_HANDLED; } @@ -244,11 +241,11 @@ static struct miscdevice uctrl_dev = { { \ unsigned int i; \ for (i = 0; i < 10000; i++) { \ - if (UCTRL_STAT_TXNF_STA & driver->regs->uctrl_stat) \ + if (UCTRL_STAT_TXNF_STA & sbus_readl(&driver->regs->uctrl_stat)) \ break; \ } \ dprintk(("write data 0x%02x\n", value)); \ - driver->regs->uctrl_data = value; \ + sbus_writel(value, &driver->regs->uctrl_data); \ } /* Wait for something to read, read it, then clear the bit */ @@ -257,24 +254,23 @@ static struct miscdevice uctrl_dev = { unsigned int i; \ value = 0; \ for (i = 0; i < 10000; i++) { \ - if ((UCTRL_STAT_RXNE_STA & driver->regs->uctrl_stat) == 0) \ + if ((UCTRL_STAT_RXNE_STA & sbus_readl(&driver->regs->uctrl_stat)) == 0) \ break; \ udelay(1); \ } \ - value = driver->regs->uctrl_data; \ + value = sbus_readl(&driver->regs->uctrl_data); \ dprintk(("read data 0x%02x\n", value)); \ - driver->regs->uctrl_stat = UCTRL_STAT_RXNE_STA; \ + sbus_writel(UCTRL_STAT_RXNE_STA, &driver->regs->uctrl_stat); \ } -static void uctrl_do_txn(struct uctrl_txn *txn) +static void uctrl_do_txn(struct uctrl_driver *driver, struct uctrl_txn *txn) { - struct uctrl_driver *driver = &drv; int stat, incnt, outcnt, bytecnt, intr; u32 byte; - stat = driver->regs->uctrl_stat; - intr = driver->regs->uctrl_intr; - driver->regs->uctrl_stat = stat; + stat = sbus_readl(&driver->regs->uctrl_stat); + intr = sbus_readl(&driver->regs->uctrl_intr); + sbus_writel(stat, &driver->regs->uctrl_stat); dprintk(("interrupt stat 0x%x int 0x%x\n", stat, intr)); @@ -305,9 +301,8 @@ static void uctrl_do_txn(struct uctrl_txn *txn) } } -static void uctrl_get_event_status(void) +static void uctrl_get_event_status(struct uctrl_driver *driver) { - struct uctrl_driver *driver = &drv; struct uctrl_txn txn; u8 outbits[2]; @@ -317,7 +312,7 @@ static void uctrl_get_event_status(void) txn.inbuf = NULL; txn.outbuf = outbits; - uctrl_do_txn(&txn); + uctrl_do_txn(driver, &txn); dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); driver->status.event_status = @@ -325,9 +320,8 @@ static void uctrl_get_event_status(void) dprintk(("ev is %x\n", driver->status.event_status)); } -static void uctrl_get_external_status(void) +static void uctrl_get_external_status(struct uctrl_driver *driver) { - struct uctrl_driver *driver = &drv; struct uctrl_txn txn; u8 outbits[2]; int i, v; @@ -338,7 +332,7 @@ static void uctrl_get_external_status(void) txn.inbuf = NULL; txn.outbuf = outbits; - uctrl_do_txn(&txn); + uctrl_do_txn(driver, &txn); dprintk(("bytes %x %x\n", (outbits[0] & 0xff), (outbits[1] & 0xff))); driver->status.external_status = @@ -354,71 +348,101 @@ static void uctrl_get_external_status(void) } -static int __init ts102_uctrl_init(void) +static int __devinit uctrl_probe(struct of_device *op, + const struct of_device_id *match) { - struct uctrl_driver *driver = &drv; - int len; - struct linux_prom_irqs tmp_irq[2]; - unsigned int vaddr[2] = { 0, 0 }; - int tmpnode, uctrlnode = prom_getchild(prom_root_node); - int err; + struct uctrl_driver *p; + int err = -ENOMEM; - tmpnode = prom_searchsiblings(uctrlnode, "obio"); + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) { + printk(KERN_ERR "uctrl: Unable to allocate device struct.\n"); + goto out; + } - if (tmpnode) - uctrlnode = prom_getchild(tmpnode); + p->regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "uctrl"); + if (!p->regs) { + printk(KERN_ERR "uctrl: Unable to map registers.\n"); + goto out_free; + } - uctrlnode = prom_searchsiblings(uctrlnode, "uctrl"); + p->irq = op->irqs[0]; + err = request_irq(p->irq, uctrl_interrupt, 0, "uctrl", p); + if (err) { + printk(KERN_ERR "uctrl: Unable to register irq.\n"); + goto out_iounmap; + } - if (!uctrlnode) - return -ENODEV; + err = misc_register(&uctrl_dev); + if (err) { + printk(KERN_ERR "uctrl: Unable to register misc device.\n"); + goto out_free_irq; + } - /* the prom mapped it for us */ - len = prom_getproperty(uctrlnode, "address", (void *) vaddr, - sizeof(vaddr)); - driver->regs = (struct uctrl_regs *)vaddr[0]; + sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr); + printk(KERN_INFO "%s: uctrl regs[0x%p] (irq %d)\n", + op->node->full_name, p->regs, p->irq); + uctrl_get_event_status(p); + uctrl_get_external_status(p); - len = prom_getproperty(uctrlnode, "intr", (char *) tmp_irq, - sizeof(tmp_irq)); + dev_set_drvdata(&op->dev, p); + global_driver = p; - /* Flush device */ - READUCTLDATA(len); +out: + return err; - if(!driver->irq) - driver->irq = tmp_irq[0].pri; +out_free_irq: + free_irq(p->irq, p); - err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver); - if (err) { - printk("%s: unable to register irq %d\n", - __func__, driver->irq); - return err; - } +out_iounmap: + of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); - if (misc_register(&uctrl_dev)) { - printk("%s: unable to get misc minor %d\n", - __func__, uctrl_dev.minor); - free_irq(driver->irq, driver); - return -ENODEV; - } +out_free: + kfree(p); + goto out; +} - driver->regs->uctrl_intr = UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK; - printk("uctrl: 0x%p (irq %d)\n", driver->regs, driver->irq); - uctrl_get_event_status(); - uctrl_get_external_status(); - return 0; +static int __devexit uctrl_remove(struct of_device *op) +{ + struct uctrl_driver *p = dev_get_drvdata(&op->dev); + + if (p) { + misc_deregister(&uctrl_dev); + free_irq(p->irq, p); + of_iounmap(&op->resource[0], p->regs, resource_size(&op->resource[0])); + kfree(p); + } + return 0; } -static void __exit ts102_uctrl_cleanup(void) +static const struct of_device_id uctrl_match[] = { + { + .name = "uctrl", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, uctrl_match); + +static struct of_platform_driver uctrl_driver = { + .name = "uctrl", + .match_table = uctrl_match, + .probe = uctrl_probe, + .remove = __devexit_p(uctrl_remove), +}; + + +static int __init uctrl_init(void) { - struct uctrl_driver *driver = &drv; + return of_register_driver(&uctrl_driver, &of_bus_type); +} - misc_deregister(&uctrl_dev); - if (driver->irq) - free_irq(driver->irq, driver); - if (driver->regs) - driver->regs = NULL; +static void __exit uctrl_exit(void) +{ + of_unregister_driver(&uctrl_driver); } -module_init(ts102_uctrl_init); -module_exit(ts102_uctrl_cleanup); +module_init(uctrl_init); +module_exit(uctrl_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/sbus/char/vfc.h b/drivers/sbus/char/vfc.h deleted file mode 100644 index a5240c52aa0..00000000000 --- a/drivers/sbus/char/vfc.h +++ /dev/null @@ -1,171 +0,0 @@ -#ifndef _LINUX_VFC_H_ -#define _LINUX_VFC_H_ - -/* - * The control register for the vfc is at offset 0x4000 - * The first field ram bank is located at offset 0x5000 - * The second field ram bank is at offset 0x7000 - * i2c_reg address the Phillips PCF8584(see notes in vfc_i2c.c) - * data and transmit register. - * i2c_s1 controls register s1 of the PCF8584 - * i2c_write seems to be similar to i2c_write but I am not - * quite sure why sun uses it - * - * I am also not sure whether or not you can read the fram bank as a - * whole or whether you must read each word individually from offset - * 0x5000 as soon as I figure it out I will update this file */ - -struct vfc_regs { - char pad1[0x4000]; - unsigned int control; /* Offset 0x4000 */ - char pad2[0xffb]; /* from offset 0x4004 to 0x5000 */ - unsigned int fram_bank1; /* Offset 0x5000 */ - char pad3[0xffb]; /* from offset 0x5004 to 0x6000 */ - unsigned int i2c_reg; /* Offset 0x6000 */ - unsigned int i2c_magic2; /* Offset 0x6004 */ - unsigned int i2c_s1; /* Offset 0x6008 */ - unsigned int i2c_write; /* Offset 0x600c */ - char pad4[0xff0]; /* from offset 0x6010 to 0x7000 */ - unsigned int fram_bank2; /* Offset 0x7000 */ - char pad5[0x1000]; -}; - -#define VFC_SAA9051_NR (13) -#define VFC_SAA9051_ADDR (0x8a) - /* The saa9051 returns the following for its status - * bit 0 - 0 - * bit 1 - SECAM color detected (1=found,0=not found) - * bit 2 - COLOR detected (1=found,0=not found) - * bit 3 - 0 - * bit 4 - Field frequency bit (1=60Hz (NTSC), 0=50Hz (PAL)) - * bit 5 - 1 - * bit 6 - horizontal frequency lock (1=transmitter found, - * 0=no transmitter) - * bit 7 - Power on reset bit (1=reset,0=at least one successful - * read of the status byte) - */ - -#define VFC_SAA9051_PONRES (0x80) -#define VFC_SAA9051_HLOCK (0x40) -#define VFC_SAA9051_FD (0x10) -#define VFC_SAA9051_CD (0x04) -#define VFC_SAA9051_CS (0x02) - - -/* The various saa9051 sub addresses */ - -#define VFC_SAA9051_IDEL (0) -#define VFC_SAA9051_HSY_START (1) -#define VFC_SAA9051_HSY_STOP (2) -#define VFC_SAA9051_HC_START (3) -#define VFC_SAA9051_HC_STOP (4) -#define VFC_SAA9051_HS_START (5) -#define VFC_SAA9051_HORIZ_PEAK (6) -#define VFC_SAA9051_HUE (7) -#define VFC_SAA9051_C1 (8) -#define VFC_SAA9051_C2 (9) -#define VFC_SAA9051_C3 (0xa) -#define VFC_SAA9051_SECAM_DELAY (0xb) - - -/* Bit settings for saa9051 sub address 0x06 */ - -#define VFC_SAA9051_AP1 (0x01) -#define VFC_SAA9051_AP2 (0x02) -#define VFC_SAA9051_COR1 (0x04) -#define VFC_SAA9051_COR2 (0x08) -#define VFC_SAA9051_BP1 (0x10) -#define VFC_SAA9051_BP2 (0x20) -#define VFC_SAA9051_PF (0x40) -#define VFC_SAA9051_BY (0x80) - - -/* Bit settings for saa9051 sub address 0x08 */ - -#define VFC_SAA9051_CCFR0 (0x01) -#define VFC_SAA9051_CCFR1 (0x02) -#define VFC_SAA9051_YPN (0x04) -#define VFC_SAA9051_ALT (0x08) -#define VFC_SAA9051_CO (0x10) -#define VFC_SAA9051_VTR (0x20) -#define VFC_SAA9051_FS (0x40) -#define VFC_SAA9051_HPLL (0x80) - - -/* Bit settings for saa9051 sub address 9 */ - -#define VFC_SAA9051_SS0 (0x01) -#define VFC_SAA9051_SS1 (0x02) -#define VFC_SAA9051_AFCC (0x04) -#define VFC_SAA9051_CI (0x08) -#define VFC_SAA9051_SA9D4 (0x10) /* Don't care bit */ -#define VFC_SAA9051_OEC (0x20) -#define VFC_SAA9051_OEY (0x40) -#define VFC_SAA9051_VNL (0x80) - - -/* Bit settings for saa9051 sub address 0x0A */ - -#define VFC_SAA9051_YDL0 (0x01) -#define VFC_SAA9051_YDL1 (0x02) -#define VFC_SAA9051_YDL2 (0x04) -#define VFC_SAA9051_SS2 (0x08) -#define VFC_SAA9051_SS3 (0x10) -#define VFC_SAA9051_YC (0x20) -#define VFC_SAA9051_CT (0x40) -#define VFC_SAA9051_SYC (0x80) - - -#define VFC_SAA9051_SA(a,b) ((a)->saa9051_state_array[(b)+1]) -#define vfc_update_saa9051(a) (vfc_i2c_sendbuf((a),VFC_SAA9051_ADDR,\ - (a)->saa9051_state_array,\ - VFC_SAA9051_NR)) - - -struct vfc_dev { - volatile struct vfc_regs __iomem *regs; - struct vfc_regs *phys_regs; - unsigned int control_reg; - struct mutex device_lock_mtx; - int instance; - int busy; - unsigned long which_io; - unsigned char saa9051_state_array[VFC_SAA9051_NR]; -}; - -void captstat_reset(struct vfc_dev *); -void memptr_reset(struct vfc_dev *); - -int vfc_pcf8584_init(struct vfc_dev *); -void vfc_i2c_delay_no_busy(struct vfc_dev *, unsigned long); -void vfc_i2c_delay(struct vfc_dev *); -int vfc_i2c_sendbuf(struct vfc_dev *, unsigned char, char *, int) ; -int vfc_i2c_recvbuf(struct vfc_dev *, unsigned char, char *, int) ; -int vfc_i2c_reset_bus(struct vfc_dev *); -int vfc_init_i2c_bus(struct vfc_dev *); - -#define VFC_CONTROL_DIAGMODE 0x10000000 -#define VFC_CONTROL_MEMPTR 0x20000000 -#define VFC_CONTROL_CAPTURE 0x02000000 -#define VFC_CONTROL_CAPTRESET 0x04000000 - -#define VFC_STATUS_CAPTURE 0x08000000 - -#ifdef VFC_IOCTL_DEBUG -#define VFC_IOCTL_DEBUG_PRINTK(a) printk a -#else -#define VFC_IOCTL_DEBUG_PRINTK(a) -#endif - -#ifdef VFC_I2C_DEBUG -#define VFC_I2C_DEBUG_PRINTK(a) printk a -#else -#define VFC_I2C_DEBUG_PRINTK(a) -#endif - -#endif /* _LINUX_VFC_H_ */ - - - - - diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c deleted file mode 100644 index 25181bb7d62..00000000000 --- a/drivers/sbus/char/vfc_dev.c +++ /dev/null @@ -1,736 +0,0 @@ -/* - * drivers/sbus/char/vfc_dev.c - * - * Driver for the Videopix Frame Grabber. - * - * In order to use the VFC you need to program the video controller - * chip. This chip is the Phillips SAA9051. You need to call their - * documentation ordering line to get the docs. - * - * There is very little documentation on the VFC itself. There is - * some useful info that can be found in the manuals that come with - * the card. I will hopefully write some better docs at a later date. - * - * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) - * */ - -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/delay.h> -#include <linux/spinlock.h> -#include <linux/mutex.h> -#include <linux/mm.h> -#include <linux/smp_lock.h> - -#include <asm/openprom.h> -#include <asm/oplib.h> -#include <asm/io.h> -#include <asm/system.h> -#include <asm/sbus.h> -#include <asm/page.h> -#include <asm/pgtable.h> -#include <asm/uaccess.h> - -#define VFC_MAJOR (60) - -#if 0 -#define VFC_IOCTL_DEBUG -#endif - -#include "vfc.h" -#include <asm/vfc_ioctls.h> - -static const struct file_operations vfc_fops; -static struct vfc_dev **vfc_dev_lst; -static char vfcstr[]="vfc"; -static unsigned char saa9051_init_array[VFC_SAA9051_NR] = { - 0x00, 0x64, 0x72, 0x52, - 0x36, 0x18, 0xff, 0x20, - 0xfc, 0x77, 0xe3, 0x50, - 0x3e -}; - -static void vfc_lock_device(struct vfc_dev *dev) -{ - mutex_lock(&dev->device_lock_mtx); -} - -static void vfc_unlock_device(struct vfc_dev *dev) -{ - mutex_unlock(&dev->device_lock_mtx); -} - - -static void vfc_captstat_reset(struct vfc_dev *dev) -{ - dev->control_reg |= VFC_CONTROL_CAPTRESET; - sbus_writel(dev->control_reg, &dev->regs->control); - dev->control_reg &= ~VFC_CONTROL_CAPTRESET; - sbus_writel(dev->control_reg, &dev->regs->control); - dev->control_reg |= VFC_CONTROL_CAPTRESET; - sbus_writel(dev->control_reg, &dev->regs->control); -} - -static void vfc_memptr_reset(struct vfc_dev *dev) -{ - dev->control_reg |= VFC_CONTROL_MEMPTR; - sbus_writel(dev->control_reg, &dev->regs->control); - dev->control_reg &= ~VFC_CONTROL_MEMPTR; - sbus_writel(dev->control_reg, &dev->regs->control); - dev->control_reg |= VFC_CONTROL_MEMPTR; - sbus_writel(dev->control_reg, &dev->regs->control); -} - -static int vfc_csr_init(struct vfc_dev *dev) -{ - dev->control_reg = 0x80000000; - sbus_writel(dev->control_reg, &dev->regs->control); - udelay(200); - dev->control_reg &= ~0x80000000; - sbus_writel(dev->control_reg, &dev->regs->control); - udelay(100); - sbus_writel(0x0f000000, &dev->regs->i2c_magic2); - - vfc_memptr_reset(dev); - - dev->control_reg &= ~VFC_CONTROL_DIAGMODE; - dev->control_reg &= ~VFC_CONTROL_CAPTURE; - dev->control_reg |= 0x40000000; - sbus_writel(dev->control_reg, &dev->regs->control); - - vfc_captstat_reset(dev); - - return 0; -} - -static int vfc_saa9051_init(struct vfc_dev *dev) -{ - int i; - - for (i = 0; i < VFC_SAA9051_NR; i++) - dev->saa9051_state_array[i] = saa9051_init_array[i]; - - vfc_i2c_sendbuf(dev,VFC_SAA9051_ADDR, - dev->saa9051_state_array, VFC_SAA9051_NR); - return 0; -} - -static int init_vfc_hw(struct vfc_dev *dev) -{ - vfc_lock_device(dev); - vfc_csr_init(dev); - - vfc_pcf8584_init(dev); - vfc_init_i2c_bus(dev); /* hopefully this doesn't undo the magic - sun code above*/ - vfc_saa9051_init(dev); - vfc_unlock_device(dev); - return 0; -} - -static int init_vfc_devstruct(struct vfc_dev *dev, int instance) -{ - dev->instance=instance; - mutex_init(&dev->device_lock_mtx); - dev->control_reg=0; - dev->busy=0; - return 0; -} - -static int init_vfc_device(struct sbus_dev *sdev,struct vfc_dev *dev, - int instance) -{ - if(dev == NULL) { - printk(KERN_ERR "VFC: Bogus pointer passed\n"); - return -ENOMEM; - } - printk("Initializing vfc%d\n",instance); - dev->regs = NULL; - dev->regs = (volatile struct vfc_regs __iomem *) - sbus_ioremap(&sdev->resource[0], 0, - sizeof(struct vfc_regs), vfcstr); - dev->which_io = sdev->reg_addrs[0].which_io; - dev->phys_regs = (struct vfc_regs *) sdev->reg_addrs[0].phys_addr; - if (dev->regs == NULL) - return -EIO; - - printk("vfc%d: registers mapped at phys_addr: 0x%lx\n virt_addr: 0x%lx\n", - instance,(unsigned long)sdev->reg_addrs[0].phys_addr,(unsigned long)dev->regs); - - if (init_vfc_devstruct(dev, instance)) - return -EINVAL; - if (init_vfc_hw(dev)) - return -EIO; - return 0; -} - - -static struct vfc_dev *vfc_get_dev_ptr(int instance) -{ - return vfc_dev_lst[instance]; -} - -static DEFINE_SPINLOCK(vfc_dev_lock); - -static int vfc_open(struct inode *inode, struct file *file) -{ - struct vfc_dev *dev; - - lock_kernel(); - spin_lock(&vfc_dev_lock); - dev = vfc_get_dev_ptr(iminor(inode)); - if (dev == NULL) { - spin_unlock(&vfc_dev_lock); - unlock_kernel(); - return -ENODEV; - } - if (dev->busy) { - spin_unlock(&vfc_dev_lock); - unlock_kernel(); - return -EBUSY; - } - - dev->busy = 1; - spin_unlock(&vfc_dev_lock); - - vfc_lock_device(dev); - - vfc_csr_init(dev); - vfc_pcf8584_init(dev); - vfc_init_i2c_bus(dev); - vfc_saa9051_init(dev); - vfc_memptr_reset(dev); - vfc_captstat_reset(dev); - - vfc_unlock_device(dev); - unlock_kernel(); - return 0; -} - -static int vfc_release(struct inode *inode,struct file *file) -{ - struct vfc_dev *dev; - - spin_lock(&vfc_dev_lock); - dev = vfc_get_dev_ptr(iminor(inode)); - if (!dev || !dev->busy) { - spin_unlock(&vfc_dev_lock); - return -EINVAL; - } - dev->busy = 0; - spin_unlock(&vfc_dev_lock); - return 0; -} - -static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp) -{ - struct vfc_debug_inout inout; - unsigned char *buffer; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - switch(cmd) { - case VFC_I2C_SEND: - if(copy_from_user(&inout, argp, sizeof(inout))) - return -EFAULT; - - buffer = kmalloc(inout.len, GFP_KERNEL); - if (buffer == NULL) - return -ENOMEM; - - if(copy_from_user(buffer, inout.buffer, inout.len)) { - kfree(buffer); - return -EFAULT; - } - - - vfc_lock_device(dev); - inout.ret= - vfc_i2c_sendbuf(dev,inout.addr & 0xff, - buffer,inout.len); - - if (copy_to_user(argp,&inout,sizeof(inout))) { - vfc_unlock_device(dev); - kfree(buffer); - return -EFAULT; - } - vfc_unlock_device(dev); - - break; - case VFC_I2C_RECV: - if (copy_from_user(&inout, argp, sizeof(inout))) - return -EFAULT; - - buffer = kzalloc(inout.len, GFP_KERNEL); - if (buffer == NULL) - return -ENOMEM; - - vfc_lock_device(dev); - inout.ret= - vfc_i2c_recvbuf(dev,inout.addr & 0xff - ,buffer,inout.len); - vfc_unlock_device(dev); - - if (copy_to_user(inout.buffer, buffer, inout.len)) { - kfree(buffer); - return -EFAULT; - } - if (copy_to_user(argp,&inout,sizeof(inout))) { - kfree(buffer); - return -EFAULT; - } - kfree(buffer); - break; - default: - return -EINVAL; - }; - - return 0; -} - -static int vfc_capture_start(struct vfc_dev *dev) -{ - vfc_captstat_reset(dev); - dev->control_reg = sbus_readl(&dev->regs->control); - if((dev->control_reg & VFC_STATUS_CAPTURE)) { - printk(KERN_ERR "vfc%d: vfc capture status not reset\n", - dev->instance); - return -EIO; - } - - vfc_lock_device(dev); - dev->control_reg &= ~VFC_CONTROL_CAPTURE; - sbus_writel(dev->control_reg, &dev->regs->control); - dev->control_reg |= VFC_CONTROL_CAPTURE; - sbus_writel(dev->control_reg, &dev->regs->control); - dev->control_reg &= ~VFC_CONTROL_CAPTURE; - sbus_writel(dev->control_reg, &dev->regs->control); - vfc_unlock_device(dev); - - return 0; -} - -static int vfc_capture_poll(struct vfc_dev *dev) -{ - int timeout = 1000; - - while (!timeout--) { - if (sbus_readl(&dev->regs->control) & VFC_STATUS_CAPTURE) - break; - vfc_i2c_delay_no_busy(dev, 100); - } - if(!timeout) { - printk(KERN_WARNING "vfc%d: capture timed out\n", - dev->instance); - return -ETIMEDOUT; - } - return 0; -} - - - -static int vfc_set_control_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) -{ - int setcmd, ret = 0; - - if (copy_from_user(&setcmd,(void __user *)arg,sizeof(unsigned int))) - return -EFAULT; - - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSCTRL) arg=0x%x\n", - dev->instance,setcmd)); - - switch(setcmd) { - case MEMPRST: - vfc_lock_device(dev); - vfc_memptr_reset(dev); - vfc_unlock_device(dev); - ret=0; - break; - case CAPTRCMD: - vfc_capture_start(dev); - vfc_capture_poll(dev); - break; - case DIAGMODE: - if(capable(CAP_SYS_ADMIN)) { - vfc_lock_device(dev); - dev->control_reg |= VFC_CONTROL_DIAGMODE; - sbus_writel(dev->control_reg, &dev->regs->control); - vfc_unlock_device(dev); - ret = 0; - } else { - ret = -EPERM; - } - break; - case NORMMODE: - vfc_lock_device(dev); - dev->control_reg &= ~VFC_CONTROL_DIAGMODE; - sbus_writel(dev->control_reg, &dev->regs->control); - vfc_unlock_device(dev); - ret = 0; - break; - case CAPTRSTR: - vfc_capture_start(dev); - ret = 0; - break; - case CAPTRWAIT: - vfc_capture_poll(dev); - ret = 0; - break; - default: - ret = -EINVAL; - break; - }; - - return ret; -} - - -static int vfc_port_change_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) -{ - int ret = 0; - int cmd; - - if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) { - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " - "vfc_port_change_ioctl\n", - dev->instance)); - return -EFAULT; - } - - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCPORTCHG) arg=0x%x\n", - dev->instance, cmd)); - - switch(cmd) { - case 1: - case 2: - VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x72; - VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x52; - VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0x36; - VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0x18; - VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = VFC_SAA9051_BP2; - VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_CT | VFC_SAA9051_SS3; - VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0x3e; - break; - case 3: - VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_START) = 0x3a; - VFC_SAA9051_SA(dev,VFC_SAA9051_HSY_STOP) = 0x17; - VFC_SAA9051_SA(dev,VFC_SAA9051_HC_START) = 0xfa; - VFC_SAA9051_SA(dev,VFC_SAA9051_HC_STOP) = 0xde; - VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) = - VFC_SAA9051_BY | VFC_SAA9051_PF | VFC_SAA9051_BP2; - VFC_SAA9051_SA(dev,VFC_SAA9051_C3) = VFC_SAA9051_YC; - VFC_SAA9051_SA(dev,VFC_SAA9051_SECAM_DELAY) = 0; - VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= - ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); - break; - default: - ret = -EINVAL; - return ret; - break; - } - - switch(cmd) { - case 1: - VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= - (VFC_SAA9051_SS0 | VFC_SAA9051_SS1); - break; - case 2: - VFC_SAA9051_SA(dev,VFC_SAA9051_C2) &= - ~(VFC_SAA9051_SS0 | VFC_SAA9051_SS1); - VFC_SAA9051_SA(dev,VFC_SAA9051_C2) |= VFC_SAA9051_SS0; - break; - case 3: - break; - default: - ret = -EINVAL; - return ret; - break; - } - VFC_SAA9051_SA(dev,VFC_SAA9051_C3) &= ~(VFC_SAA9051_SS2); - ret=vfc_update_saa9051(dev); - udelay(500); - VFC_SAA9051_SA(dev,VFC_SAA9051_C3) |= (VFC_SAA9051_SS2); - ret=vfc_update_saa9051(dev); - return ret; -} - -static int vfc_set_video_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) -{ - int ret = 0; - int cmd; - - if(copy_from_user(&cmd, (void __user *)arg, sizeof(unsigned int))) { - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " - "vfc_set_video_ioctl\n", - dev->instance)); - return ret; - } - - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCSVID) arg=0x%x\n", - dev->instance, cmd)); - switch(cmd) { - case STD_NTSC: - VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~VFC_SAA9051_ALT; - VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_YPN | - VFC_SAA9051_CCFR0 | VFC_SAA9051_CCFR1 | VFC_SAA9051_FS; - ret = vfc_update_saa9051(dev); - break; - case STD_PAL: - VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_YPN | - VFC_SAA9051_CCFR1 | - VFC_SAA9051_CCFR0 | - VFC_SAA9051_FS); - VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_ALT; - ret = vfc_update_saa9051(dev); - break; - - case COLOR_ON: - VFC_SAA9051_SA(dev,VFC_SAA9051_C1) |= VFC_SAA9051_CO; - VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) &= - ~(VFC_SAA9051_BY | VFC_SAA9051_PF); - ret = vfc_update_saa9051(dev); - break; - case MONO: - VFC_SAA9051_SA(dev,VFC_SAA9051_C1) &= ~(VFC_SAA9051_CO); - VFC_SAA9051_SA(dev,VFC_SAA9051_HORIZ_PEAK) |= - (VFC_SAA9051_BY | VFC_SAA9051_PF); - ret = vfc_update_saa9051(dev); - break; - default: - ret = -EINVAL; - break; - }; - - return ret; -} - -static int vfc_get_video_ioctl(struct inode *inode, struct file *file, - struct vfc_dev *dev, unsigned long arg) -{ - int ret = 0; - unsigned int status = NO_LOCK; - unsigned char buf[1]; - - if(vfc_i2c_recvbuf(dev, VFC_SAA9051_ADDR, buf, 1)) { - printk(KERN_ERR "vfc%d: Unable to get status\n", - dev->instance); - return -EIO; - } - - if(buf[0] & VFC_SAA9051_HLOCK) { - status = NO_LOCK; - } else if(buf[0] & VFC_SAA9051_FD) { - if(buf[0] & VFC_SAA9051_CD) - status = NTSC_COLOR; - else - status = NTSC_NOCOLOR; - } else { - if(buf[0] & VFC_SAA9051_CD) - status = PAL_COLOR; - else - status = PAL_NOCOLOR; - } - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGVID) returning status 0x%x; " - "buf[0]=%x\n", dev->instance, status, buf[0])); - - if (copy_to_user((void __user *)arg,&status,sizeof(unsigned int))) { - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer to " - "vfc_get_video_ioctl\n", - dev->instance)); - return ret; - } - return ret; -} - -static int vfc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int ret = 0; - unsigned int tmp; - struct vfc_dev *dev; - void __user *argp = (void __user *)arg; - - dev = vfc_get_dev_ptr(iminor(inode)); - if(dev == NULL) - return -ENODEV; - - switch(cmd & 0x0000ffff) { - case VFCGCTRL: -#if 0 - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCGCTRL)\n", dev->instance)); -#endif - tmp = sbus_readl(&dev->regs->control); - if(copy_to_user(argp, &tmp, sizeof(unsigned int))) { - ret = -EFAULT; - break; - } - ret = 0; - break; - case VFCSCTRL: - ret = vfc_set_control_ioctl(inode, file, dev, arg); - break; - case VFCGVID: - ret = vfc_get_video_ioctl(inode, file, dev, arg); - break; - case VFCSVID: - ret = vfc_set_video_ioctl(inode, file, dev, arg); - break; - case VFCHUE: - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCHUE)\n", dev->instance)); - if(copy_from_user(&tmp,argp,sizeof(unsigned int))) { - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: User passed bogus pointer " - "to IOCTL(VFCHUE)", dev->instance)); - ret = -EFAULT; - } else { - VFC_SAA9051_SA(dev,VFC_SAA9051_HUE) = tmp; - vfc_update_saa9051(dev); - ret = 0; - } - break; - case VFCPORTCHG: - ret = vfc_port_change_ioctl(inode, file, dev, arg); - break; - case VFCRDINFO: - ret = -EINVAL; - VFC_IOCTL_DEBUG_PRINTK(("vfc%d: IOCTL(VFCRDINFO)\n", dev->instance)); - break; - default: - ret = vfc_debug(vfc_get_dev_ptr(iminor(inode)), cmd, argp); - break; - }; - - return ret; -} - -static int vfc_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned int map_size, ret, map_offset; - struct vfc_dev *dev; - - dev = vfc_get_dev_ptr(iminor(file->f_path.dentry->d_inode)); - if(dev == NULL) - return -ENODEV; - - map_size = vma->vm_end - vma->vm_start; - if(map_size > sizeof(struct vfc_regs)) - map_size = sizeof(struct vfc_regs); - - vma->vm_flags |= - (VM_MAYREAD | VM_MAYWRITE | VM_MAYSHARE); - map_offset = (unsigned int) (long)dev->phys_regs; - ret = io_remap_pfn_range(vma, vma->vm_start, - MK_IOSPACE_PFN(dev->which_io, - map_offset >> PAGE_SHIFT), - map_size, vma->vm_page_prot); - - if(ret) - return -EAGAIN; - - return 0; -} - - -static const struct file_operations vfc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .ioctl = vfc_ioctl, - .mmap = vfc_mmap, - .open = vfc_open, - .release = vfc_release, -}; - -static int vfc_probe(void) -{ - struct sbus_bus *sbus; - struct sbus_dev *sdev = NULL; - int ret; - int instance = 0, cards = 0; - - for_all_sbusdev(sdev, sbus) { - if (strcmp(sdev->prom_name, "vfc") == 0) { - cards++; - continue; - } - } - - if (!cards) - return -ENODEV; - - vfc_dev_lst = kcalloc(cards + 1, sizeof(struct vfc_dev*), GFP_KERNEL); - if (vfc_dev_lst == NULL) - return -ENOMEM; - vfc_dev_lst[cards] = NULL; - - ret = register_chrdev(VFC_MAJOR, vfcstr, &vfc_fops); - if(ret) { - printk(KERN_ERR "Unable to get major number %d\n", VFC_MAJOR); - kfree(vfc_dev_lst); - return -EIO; - } - instance = 0; - for_all_sbusdev(sdev, sbus) { - if (strcmp(sdev->prom_name, "vfc") == 0) { - vfc_dev_lst[instance]=(struct vfc_dev *) - kmalloc(sizeof(struct vfc_dev), GFP_KERNEL); - if (vfc_dev_lst[instance] == NULL) - return -ENOMEM; - ret = init_vfc_device(sdev, - vfc_dev_lst[instance], - instance); - if(ret) { - printk(KERN_ERR "Unable to initialize" - " vfc%d device\n", - instance); - } else { - } - - instance++; - continue; - } - } - - return 0; -} - -#ifdef MODULE -int init_module(void) -#else -int vfc_init(void) -#endif -{ - return vfc_probe(); -} - -#ifdef MODULE -static void deinit_vfc_device(struct vfc_dev *dev) -{ - if(dev == NULL) - return; - sbus_iounmap(dev->regs, sizeof(struct vfc_regs)); - kfree(dev); -} - -void cleanup_module(void) -{ - struct vfc_dev **devp; - - unregister_chrdev(VFC_MAJOR,vfcstr); - - for (devp = vfc_dev_lst; *devp; devp++) - deinit_vfc_device(*devp); - - kfree(vfc_dev_lst); - return; -} -#endif - -MODULE_LICENSE("GPL"); - diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c deleted file mode 100644 index 32b986e0ed7..00000000000 --- a/drivers/sbus/char/vfc_i2c.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * drivers/sbus/char/vfc_i2c.c - * - * Driver for the Videopix Frame Grabber. - * - * Functions that support the Phillips i2c(I squared C) bus on the vfc - * Documentation for the Phillips I2C bus can be found on the - * phillips home page - * - * Copyright (C) 1996 Manish Vachharajani (mvachhar@noc.rutgers.edu) - * - */ - -/* NOTE: It seems to me that the documentation regarding the -pcd8584t/pcf8584 does not show the correct way to address the i2c bus. -Based on the information on the I2C bus itself and the remainder of -the Phillips docs the following algorithms appear to be correct. I am -fairly certain that the flowcharts in the phillips docs are wrong. */ - - -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/sched.h> -#include <linux/wait.h> -#include <linux/delay.h> -#include <asm/openprom.h> -#include <asm/oplib.h> -#include <asm/io.h> -#include <asm/system.h> -#include <asm/sbus.h> - -#if 0 -#define VFC_I2C_DEBUG -#endif - -#include "vfc.h" -#include "vfc_i2c.h" - -#define WRITE_S1(__val) \ - sbus_writel(__val, &dev->regs->i2c_s1) -#define WRITE_REG(__val) \ - sbus_writel(__val, &dev->regs->i2c_reg) - -#define VFC_I2C_READ (0x1) -#define VFC_I2C_WRITE (0x0) - -/****** - The i2c bus controller chip on the VFC is a pcd8584t, but - phillips claims it doesn't exist. As far as I can tell it is - identical to the PCF8584 so I treat it like it is the pcf8584. - - NOTE: The pcf8584 only cares - about the msb of the word you feed it -*****/ - -int vfc_pcf8584_init(struct vfc_dev *dev) -{ - /* This will also choose register S0_OWN so we can set it. */ - WRITE_S1(RESET); - - /* The pcf8584 shifts this value left one bit and uses - * it as its i2c bus address. - */ - WRITE_REG(0x55000000); - - /* This will set the i2c bus at the same speed sun uses, - * and set another magic bit. - */ - WRITE_S1(SELECT(S2)); - WRITE_REG(0x14000000); - - /* Enable the serial port, idle the i2c bus and set - * the data reg to s0. - */ - WRITE_S1(CLEAR_I2C_BUS); - udelay(100); - return 0; -} - -void vfc_i2c_delay_no_busy(struct vfc_dev *dev, unsigned long usecs) -{ - schedule_timeout_uninterruptible(usecs_to_jiffies(usecs)); -} - -void inline vfc_i2c_delay(struct vfc_dev *dev) -{ - vfc_i2c_delay_no_busy(dev, 100); -} - -int vfc_init_i2c_bus(struct vfc_dev *dev) -{ - WRITE_S1(ENABLE_SERIAL | SELECT(S0) | ACK); - vfc_i2c_reset_bus(dev); - return 0; -} - -int vfc_i2c_reset_bus(struct vfc_dev *dev) -{ - VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: Resetting the i2c bus\n", - dev->instance)); - if(dev == NULL) - return -EINVAL; - if(dev->regs == NULL) - return -EINVAL; - WRITE_S1(SEND_I2C_STOP); - WRITE_S1(SEND_I2C_STOP | ACK); - vfc_i2c_delay(dev); - WRITE_S1(CLEAR_I2C_BUS); - VFC_I2C_DEBUG_PRINTK((KERN_DEBUG "vfc%d: I2C status %x\n", - dev->instance, - sbus_readl(&dev->regs->i2c_s1))); - return 0; -} - -static int vfc_i2c_wait_for_bus(struct vfc_dev *dev) -{ - int timeout = 1000; - - while(!(sbus_readl(&dev->regs->i2c_s1) & BB)) { - if(!(timeout--)) - return -ETIMEDOUT; - vfc_i2c_delay(dev); - } - return 0; -} - -static int vfc_i2c_wait_for_pin(struct vfc_dev *dev, int ack) -{ - int timeout = 1000; - int s1; - - while ((s1 = sbus_readl(&dev->regs->i2c_s1)) & PIN) { - if (!(timeout--)) - return -ETIMEDOUT; - vfc_i2c_delay(dev); - } - if (ack == VFC_I2C_ACK_CHECK) { - if(s1 & LRB) - return -EIO; - } - return 0; -} - -#define SHIFT(a) ((a) << 24) -static int vfc_i2c_xmit_addr(struct vfc_dev *dev, unsigned char addr, - char mode) -{ - int ret, raddr; -#if 1 - WRITE_S1(SEND_I2C_STOP | ACK); - WRITE_S1(SELECT(S0) | ENABLE_SERIAL); - vfc_i2c_delay(dev); -#endif - - switch(mode) { - case VFC_I2C_READ: - raddr = SHIFT(((unsigned int)addr | 0x1)); - WRITE_REG(raddr); - VFC_I2C_DEBUG_PRINTK(("vfc%d: receiving from i2c addr 0x%x\n", - dev->instance, addr | 0x1)); - break; - case VFC_I2C_WRITE: - raddr = SHIFT((unsigned int)addr & ~0x1); - WRITE_REG(raddr); - VFC_I2C_DEBUG_PRINTK(("vfc%d: sending to i2c addr 0x%x\n", - dev->instance, addr & ~0x1)); - break; - default: - return -EINVAL; - }; - - WRITE_S1(SEND_I2C_START); - vfc_i2c_delay(dev); - ret = vfc_i2c_wait_for_pin(dev,VFC_I2C_ACK_CHECK); /* We wait - for the - i2c send - to finish - here but - Sun - doesn't, - hmm */ - if (ret) { - printk(KERN_ERR "vfc%d: VFC xmit addr timed out or no ack\n", - dev->instance); - return ret; - } else if (mode == VFC_I2C_READ) { - if ((ret = sbus_readl(&dev->regs->i2c_reg) & 0xff000000) != raddr) { - printk(KERN_WARNING - "vfc%d: returned slave address " - "mismatch(%x,%x)\n", - dev->instance, raddr, ret); - } - } - return 0; -} - -static int vfc_i2c_xmit_byte(struct vfc_dev *dev,unsigned char *byte) -{ - int ret; - u32 val = SHIFT((unsigned int)*byte); - - WRITE_REG(val); - - ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_ACK_CHECK); - switch(ret) { - case -ETIMEDOUT: - printk(KERN_ERR "vfc%d: VFC xmit byte timed out or no ack\n", - dev->instance); - break; - case -EIO: - ret = XMIT_LAST_BYTE; - break; - default: - break; - }; - - return ret; -} - -static int vfc_i2c_recv_byte(struct vfc_dev *dev, unsigned char *byte, - int last) -{ - int ret; - - if (last) { - WRITE_REG(NEGATIVE_ACK); - VFC_I2C_DEBUG_PRINTK(("vfc%d: sending negative ack\n", - dev->instance)); - } else { - WRITE_S1(ACK); - } - - ret = vfc_i2c_wait_for_pin(dev, VFC_I2C_NO_ACK_CHECK); - if(ret) { - printk(KERN_ERR "vfc%d: " - "VFC recv byte timed out\n", - dev->instance); - } - *byte = (sbus_readl(&dev->regs->i2c_reg)) >> 24; - return ret; -} - -int vfc_i2c_recvbuf(struct vfc_dev *dev, unsigned char addr, - char *buf, int count) -{ - int ret, last; - - if(!(count && buf && dev && dev->regs) ) - return -EINVAL; - - if ((ret = vfc_i2c_wait_for_bus(dev))) { - printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance); - return ret; - } - - if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_READ))) { - WRITE_S1(SEND_I2C_STOP); - vfc_i2c_delay(dev); - return ret; - } - - last = 0; - while (count--) { - if (!count) - last = 1; - if ((ret = vfc_i2c_recv_byte(dev, buf, last))) { - printk(KERN_ERR "vfc%d: " - "VFC error while receiving byte\n", - dev->instance); - WRITE_S1(SEND_I2C_STOP); - ret = -EINVAL; - } - buf++; - } - WRITE_S1(SEND_I2C_STOP | ACK); - vfc_i2c_delay(dev); - return ret; -} - -int vfc_i2c_sendbuf(struct vfc_dev *dev, unsigned char addr, - char *buf, int count) -{ - int ret; - - if (!(buf && dev && dev->regs)) - return -EINVAL; - - if ((ret = vfc_i2c_wait_for_bus(dev))) { - printk(KERN_ERR "vfc%d: VFC I2C bus busy\n", dev->instance); - return ret; - } - - if ((ret = vfc_i2c_xmit_addr(dev, addr, VFC_I2C_WRITE))) { - WRITE_S1(SEND_I2C_STOP); - vfc_i2c_delay(dev); - return ret; - } - - while(count--) { - ret = vfc_i2c_xmit_byte(dev, buf); - switch(ret) { - case XMIT_LAST_BYTE: - VFC_I2C_DEBUG_PRINTK(("vfc%d: " - "Receiver ended transmission with " - " %d bytes remaining\n", - dev->instance, count)); - ret = 0; - goto done; - break; - case 0: - break; - default: - printk(KERN_ERR "vfc%d: " - "VFC error while sending byte\n", dev->instance); - break; - }; - - buf++; - } -done: - WRITE_S1(SEND_I2C_STOP | ACK); - vfc_i2c_delay(dev); - return ret; -} - - - - - - - - - diff --git a/drivers/sbus/char/vfc_i2c.h b/drivers/sbus/char/vfc_i2c.h deleted file mode 100644 index a2e6973209d..00000000000 --- a/drivers/sbus/char/vfc_i2c.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef _LINUX_VFC_I2C_H_ -#define _LINUX_VFC_I2C_H_ - -/* control bits */ -#define PIN (0x80000000) -#define ESO (0x40000000) -#define ES1 (0x20000000) -#define ES2 (0x10000000) -#define ENI (0x08000000) -#define STA (0x04000000) -#define STO (0x02000000) -#define ACK (0x01000000) - -/* status bits */ -#define STS (0x20000000) -#define BER (0x10000000) -#define LRB (0x08000000) -#define AAS (0x04000000) -#define LAB (0x02000000) -#define BB (0x01000000) - -#define SEND_I2C_START (PIN | ESO | STA) -#define SEND_I2C_STOP (PIN | ESO | STO) -#define CLEAR_I2C_BUS (PIN | ESO | ACK) -#define NEGATIVE_ACK ((ESO) & ~ACK) - -#define SELECT(a) (a) -#define S0 (PIN | ESO | ES1) -#define S0_OWN (PIN) -#define S2 (PIN | ES1) -#define S3 (PIN | ES2) - -#define ENABLE_SERIAL (PIN | ESO) -#define DISABLE_SERIAL (PIN) -#define RESET (PIN) - -#define XMIT_LAST_BYTE (1) -#define VFC_I2C_ACK_CHECK (1) -#define VFC_I2C_NO_ACK_CHECK (0) - -#endif /* _LINUX_VFC_I2C_H_ */ - - - diff --git a/drivers/sbus/dvma.c b/drivers/sbus/dvma.c deleted file mode 100644 index ab0d2de3324..00000000000 --- a/drivers/sbus/dvma.c +++ /dev/null @@ -1,136 +0,0 @@ -/* dvma.c: Routines that are used to access DMA on the Sparc SBus. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ - -#include <linux/string.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/delay.h> - -#include <asm/oplib.h> -#include <asm/io.h> -#include <asm/dma.h> -#include <asm/sbus.h> - -struct sbus_dma *dma_chain; - -static void __init init_one_dvma(struct sbus_dma *dma, int num_dma) -{ - printk("dma%d: ", num_dma); - - dma->next = NULL; - dma->running = 0; /* No transfers going on as of yet */ - dma->allocated = 0; /* No one has allocated us yet */ - switch(sbus_readl(dma->regs + DMA_CSR)&DMA_DEVICE_ID) { - case DMA_VERS0: - dma->revision = dvmarev0; - printk("Revision 0 "); - break; - case DMA_ESCV1: - dma->revision = dvmaesc1; - printk("ESC Revision 1 "); - break; - case DMA_VERS1: - dma->revision = dvmarev1; - printk("Revision 1 "); - break; - case DMA_VERS2: - dma->revision = dvmarev2; - printk("Revision 2 "); - break; - case DMA_VERHME: - dma->revision = dvmahme; - printk("HME DVMA gate array "); - break; - case DMA_VERSPLUS: - dma->revision = dvmarevplus; - printk("Revision 1 PLUS "); - break; - default: - printk("unknown dma version %08x", - sbus_readl(dma->regs + DMA_CSR) & DMA_DEVICE_ID); - dma->allocated = 1; - break; - } - printk("\n"); -} - -/* Probe this SBus DMA module(s) */ -void __init dvma_init(struct sbus_bus *sbus) -{ - struct sbus_dev *this_dev; - struct sbus_dma *dma; - struct sbus_dma *dchain; - static int num_dma = 0; - - for_each_sbusdev(this_dev, sbus) { - char *name = this_dev->prom_name; - int hme = 0; - - if(!strcmp(name, "SUNW,fas")) - hme = 1; - else if(strcmp(name, "dma") && - strcmp(name, "ledma") && - strcmp(name, "espdma")) - continue; - - /* Found one... */ - dma = kmalloc(sizeof(struct sbus_dma), GFP_ATOMIC); - - dma->sdev = this_dev; - - /* Put at end of dma chain */ - dchain = dma_chain; - if(dchain) { - while(dchain->next) - dchain = dchain->next; - dchain->next = dma; - } else { - /* We're the first in line */ - dma_chain = dma; - } - - dma->regs = sbus_ioremap(&dma->sdev->resource[0], 0, - dma->sdev->resource[0].end - dma->sdev->resource[0].start + 1, - "dma"); - - dma->node = dma->sdev->prom_node; - - init_one_dvma(dma, num_dma++); - } -} - -#ifdef CONFIG_SUN4 - -#include <asm/sun4paddr.h> - -void __init sun4_dvma_init(void) -{ - struct sbus_dma *dma; - struct resource r; - - if(sun4_dma_physaddr) { - dma = kmalloc(sizeof(struct sbus_dma), GFP_ATOMIC); - - /* No SBUS */ - dma->sdev = NULL; - - /* Only one DMA device */ - dma_chain = dma; - - memset(&r, 0, sizeof(r)); - r.start = sun4_dma_physaddr; - dma->regs = sbus_ioremap(&r, 0, PAGE_SIZE, "dma"); - - /* No prom node */ - dma->node = 0x0; - - init_one_dvma(dma, 0); - } else { - dma_chain = NULL; - } -} - -#endif diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c deleted file mode 100644 index 9c129248466..00000000000 --- a/drivers/sbus/sbus.c +++ /dev/null @@ -1,316 +0,0 @@ -/* sbus.c: SBus support routines. - * - * Copyright (C) 1995, 2006 David S. Miller (davem@davemloft.net) - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/device.h> -#include <linux/of_device.h> - -#include <asm/system.h> -#include <asm/sbus.h> -#include <asm/dma.h> -#include <asm/oplib.h> -#include <asm/prom.h> -#include <asm/bpp.h> -#include <asm/irq.h> - -static ssize_t -show_sbusobppath_attr(struct device * dev, struct device_attribute * attr, char * buf) -{ - struct sbus_dev *sbus; - - sbus = to_sbus_device(dev); - - return snprintf (buf, PAGE_SIZE, "%s\n", sbus->ofdev.node->full_name); -} - -static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH, show_sbusobppath_attr, NULL); - -struct sbus_bus *sbus_root; - -static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev) -{ - struct dev_archdata *sd; - unsigned long base; - const void *pval; - int len, err; - - sdev->prom_node = dp->node; - strcpy(sdev->prom_name, dp->name); - - pval = of_get_property(dp, "reg", &len); - sdev->num_registers = 0; - if (pval) { - memcpy(sdev->reg_addrs, pval, len); - - sdev->num_registers = - len / sizeof(struct linux_prom_registers); - - base = (unsigned long) sdev->reg_addrs[0].phys_addr; - - /* Compute the slot number. */ - if (base >= SUN_SBUS_BVADDR && sparc_cpu_model == sun4m) - sdev->slot = sbus_dev_slot(base); - else - sdev->slot = sdev->reg_addrs[0].which_io; - } - - pval = of_get_property(dp, "ranges", &len); - sdev->num_device_ranges = 0; - if (pval) { - memcpy(sdev->device_ranges, pval, len); - sdev->num_device_ranges = - len / sizeof(struct linux_prom_ranges); - } - - sbus_fill_device_irq(sdev); - - sd = &sdev->ofdev.dev.archdata; - sd->prom_node = dp; - sd->op = &sdev->ofdev; - - sdev->ofdev.node = dp; - if (sdev->parent) - sdev->ofdev.dev.parent = &sdev->parent->ofdev.dev; - else - sdev->ofdev.dev.parent = &sdev->bus->ofdev.dev; - sdev->ofdev.dev.bus = &sbus_bus_type; - dev_set_name(&sdev->ofdev.dev, "sbus[%08x]", dp->node); - - if (of_device_register(&sdev->ofdev) != 0) - printk(KERN_DEBUG "sbus: device registration error for %s!\n", - dp->path_component_name); - - /* WE HAVE BEEN INVADED BY ALIENS! */ - err = sysfs_create_file(&sdev->ofdev.dev.kobj, &dev_attr_obppath.attr); -} - -static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus) -{ - const void *pval; - int len; - - pval = of_get_property(dp, "ranges", &len); - sbus->num_sbus_ranges = 0; - if (pval) { - memcpy(sbus->sbus_ranges, pval, len); - sbus->num_sbus_ranges = - len / sizeof(struct linux_prom_ranges); - - sbus_arch_bus_ranges_init(dp->parent, sbus); - } -} - -static void __init __apply_ranges_to_regs(struct linux_prom_ranges *ranges, - int num_ranges, - struct linux_prom_registers *regs, - int num_regs) -{ - if (num_ranges) { - int regnum; - - for (regnum = 0; regnum < num_regs; regnum++) { - int rngnum; - - for (rngnum = 0; rngnum < num_ranges; rngnum++) { - if (regs[regnum].which_io == ranges[rngnum].ot_child_space) - break; - } - if (rngnum == num_ranges) { - /* We used to flag this as an error. Actually - * some devices do not report the regs as we expect. - * For example, see SUNW,pln device. In that case - * the reg property is in a format internal to that - * node, ie. it is not in the SBUS register space - * per se. -DaveM - */ - return; - } - regs[regnum].which_io = ranges[rngnum].ot_parent_space; - regs[regnum].phys_addr -= ranges[rngnum].ot_child_base; - regs[regnum].phys_addr += ranges[rngnum].ot_parent_base; - } - } -} - -static void __init __fixup_regs_sdev(struct sbus_dev *sdev) -{ - if (sdev->num_registers != 0) { - struct sbus_dev *parent = sdev->parent; - int i; - - while (parent != NULL) { - __apply_ranges_to_regs(parent->device_ranges, - parent->num_device_ranges, - sdev->reg_addrs, - sdev->num_registers); - - parent = parent->parent; - } - - __apply_ranges_to_regs(sdev->bus->sbus_ranges, - sdev->bus->num_sbus_ranges, - sdev->reg_addrs, - sdev->num_registers); - - for (i = 0; i < sdev->num_registers; i++) { - struct resource *res = &sdev->resource[i]; - - res->start = sdev->reg_addrs[i].phys_addr; - res->end = (res->start + - (unsigned long)sdev->reg_addrs[i].reg_size - 1UL); - res->flags = IORESOURCE_IO | - (sdev->reg_addrs[i].which_io & 0xff); - } - } -} - -static void __init sbus_fixup_all_regs(struct sbus_dev *first_sdev) -{ - struct sbus_dev *sdev; - - for (sdev = first_sdev; sdev; sdev = sdev->next) { - if (sdev->child) - sbus_fixup_all_regs(sdev->child); - __fixup_regs_sdev(sdev); - } -} - -/* We preserve the "probe order" of these bus and device lists to give - * the same ordering as the old code. - */ -static void __init sbus_insert(struct sbus_bus *sbus, struct sbus_bus **root) -{ - while (*root) - root = &(*root)->next; - *root = sbus; - sbus->next = NULL; -} - -static void __init sdev_insert(struct sbus_dev *sdev, struct sbus_dev **root) -{ - while (*root) - root = &(*root)->next; - *root = sdev; - sdev->next = NULL; -} - -static void __init walk_children(struct device_node *dp, struct sbus_dev *parent, struct sbus_bus *sbus) -{ - dp = dp->child; - while (dp) { - struct sbus_dev *sdev; - - sdev = kzalloc(sizeof(struct sbus_dev), GFP_ATOMIC); - if (sdev) { - sdev_insert(sdev, &parent->child); - - sdev->bus = sbus; - sdev->parent = parent; - sdev->ofdev.dev.archdata.iommu = - sbus->ofdev.dev.archdata.iommu; - sdev->ofdev.dev.archdata.stc = - sbus->ofdev.dev.archdata.stc; - - fill_sbus_device(dp, sdev); - - walk_children(dp, sdev, sbus); - } - dp = dp->sibling; - } -} - -static void __init build_one_sbus(struct device_node *dp, int num_sbus) -{ - struct sbus_bus *sbus; - unsigned int sbus_clock; - struct device_node *dev_dp; - - sbus = kzalloc(sizeof(struct sbus_bus), GFP_ATOMIC); - if (!sbus) - return; - - sbus_insert(sbus, &sbus_root); - sbus->prom_node = dp->node; - - sbus_setup_iommu(sbus, dp); - - printk("sbus%d: ", num_sbus); - - sbus_clock = of_getintprop_default(dp, "clock-frequency", - (25*1000*1000)); - sbus->clock_freq = sbus_clock; - - printk("Clock %d.%d MHz\n", (int) ((sbus_clock/1000)/1000), - (int) (((sbus_clock/1000)%1000 != 0) ? - (((sbus_clock/1000)%1000) + 1000) : 0)); - - strcpy(sbus->prom_name, dp->name); - - sbus_setup_arch_props(sbus, dp); - - sbus_bus_ranges_init(dp, sbus); - - sbus->ofdev.node = dp; - sbus->ofdev.dev.parent = NULL; - sbus->ofdev.dev.bus = &sbus_bus_type; - dev_set_name(&sbus->ofdev.dev, "sbus%d", num_sbus); - - if (of_device_register(&sbus->ofdev) != 0) - printk(KERN_DEBUG "sbus: device registration error for %s!\n", - dev_name(&sbus->ofdev.dev)); - - dev_dp = dp->child; - while (dev_dp) { - struct sbus_dev *sdev; - - sdev = kzalloc(sizeof(struct sbus_dev), GFP_ATOMIC); - if (sdev) { - sdev_insert(sdev, &sbus->devices); - - sdev->bus = sbus; - sdev->parent = NULL; - sdev->ofdev.dev.archdata.iommu = - sbus->ofdev.dev.archdata.iommu; - sdev->ofdev.dev.archdata.stc = - sbus->ofdev.dev.archdata.stc; - - fill_sbus_device(dev_dp, sdev); - - walk_children(dev_dp, sdev, sbus); - } - dev_dp = dev_dp->sibling; - } - - sbus_fixup_all_regs(sbus->devices); - - dvma_init(sbus); -} - -static int __init sbus_init(void) -{ - struct device_node *dp; - const char *sbus_name = "sbus"; - int num_sbus = 0; - - if (sbus_arch_preinit()) - return 0; - - if (sparc_cpu_model == sun4d) - sbus_name = "sbi"; - - for_each_node_by_name(dp, sbus_name) { - build_one_sbus(dp, num_sbus); - num_sbus++; - - } - - sbus_arch_postinit(); - - return 0; -} - -subsys_initcall(sbus_init); diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h index bb43a138818..28e22acf87e 100644 --- a/drivers/scsi/esp_scsi.h +++ b/drivers/scsi/esp_scsi.h @@ -521,7 +521,8 @@ struct esp { struct completion *eh_reset; - struct sbus_dma *dma; + void *dma; + int dmarev; }; /* A front-end driver for the ESP chip should do the following in diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 90535089672..69d6ad862b6 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1,6 +1,6 @@ /* qlogicpti.c: Performance Technologies QlogicISP sbus card driver. * - * Copyright (C) 1996, 2006 David S. Miller (davem@davemloft.net) + * Copyright (C) 1996, 2006, 2008 David S. Miller (davem@davemloft.net) * * A lot of this driver was directly stolen from Erik H. Moe's PCI * Qlogic ISP driver. Mucho kudos to him for this code. @@ -25,12 +25,14 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/jiffies.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/byteorder.h> #include "qlogicpti.h" -#include <asm/sbus.h> #include <asm/dma.h> #include <asm/system.h> #include <asm/ptrace.h> @@ -157,7 +159,7 @@ static inline void set_sbus_cfg1(struct qlogicpti *qpti) * is a nop and the chip ends up using the smallest burst * size. -DaveM */ - if (sbus_can_burst64(qpti->sdev) && (bursts & DMA_BURST64)) { + if (sbus_can_burst64() && (bursts & DMA_BURST64)) { val = (SBUS_CFG1_BENAB | SBUS_CFG1_B64); } else #endif @@ -684,19 +686,19 @@ static void __devexit qpti_chain_del(struct qlogicpti *qpti) static int __devinit qpti_map_regs(struct qlogicpti *qpti) { - struct sbus_dev *sdev = qpti->sdev; + struct of_device *op = qpti->op; - qpti->qregs = sbus_ioremap(&sdev->resource[0], 0, - sdev->reg_addrs[0].reg_size, - "PTI Qlogic/ISP"); + qpti->qregs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), + "PTI Qlogic/ISP"); if (!qpti->qregs) { printk("PTI: Qlogic/ISP registers are unmappable\n"); return -1; } if (qpti->is_pti) { - qpti->sreg = sbus_ioremap(&sdev->resource[0], (16 * 4096), - sizeof(unsigned char), - "PTI Qlogic/ISP statreg"); + qpti->sreg = of_ioremap(&op->resource[0], (16 * 4096), + sizeof(unsigned char), + "PTI Qlogic/ISP statreg"); if (!qpti->sreg) { printk("PTI: Qlogic/ISP status register is unmappable\n"); return -1; @@ -707,9 +709,9 @@ static int __devinit qpti_map_regs(struct qlogicpti *qpti) static int __devinit qpti_register_irq(struct qlogicpti *qpti) { - struct sbus_dev *sdev = qpti->sdev; + struct of_device *op = qpti->op; - qpti->qhost->irq = qpti->irq = sdev->irqs[0]; + qpti->qhost->irq = qpti->irq = op->irqs[0]; /* We used to try various overly-clever things to * reduce the interrupt processing overhead on @@ -732,17 +734,19 @@ fail: static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti) { - qpti->scsi_id = prom_getintdefault(qpti->prom_node, - "initiator-id", - -1); + struct of_device *op = qpti->op; + struct device_node *dp; + + dp = op->node; + + qpti->scsi_id = of_getintprop_default(dp, "initiator-id", -1); if (qpti->scsi_id == -1) - qpti->scsi_id = prom_getintdefault(qpti->prom_node, - "scsi-initiator-id", - -1); + qpti->scsi_id = of_getintprop_default(dp, "scsi-initiator-id", + -1); if (qpti->scsi_id == -1) qpti->scsi_id = - prom_getintdefault(qpti->sdev->bus->prom_node, - "scsi-initiator-id", 7); + of_getintprop_default(dp->parent, + "scsi-initiator-id", 7); qpti->qhost->this_id = qpti->scsi_id; qpti->qhost->max_sectors = 64; @@ -751,12 +755,11 @@ static void __devinit qpti_get_scsi_id(struct qlogicpti *qpti) static void qpti_get_bursts(struct qlogicpti *qpti) { - struct sbus_dev *sdev = qpti->sdev; + struct of_device *op = qpti->op; u8 bursts, bmask; - bursts = prom_getintdefault(qpti->prom_node, "burst-sizes", 0xff); - bmask = prom_getintdefault(sdev->bus->prom_node, - "burst-sizes", 0xff); + bursts = of_getintprop_default(op->node, "burst-sizes", 0xff); + bmask = of_getintprop_default(op->node->parent, "burst-sizes", 0xff); if (bmask != 0xff) bursts &= bmask; if (bursts == 0xff || @@ -785,25 +788,25 @@ static void qpti_get_clock(struct qlogicpti *qpti) */ static int __devinit qpti_map_queues(struct qlogicpti *qpti) { - struct sbus_dev *sdev = qpti->sdev; + struct of_device *op = qpti->op; #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) - qpti->res_cpu = sbus_alloc_consistent(sdev, - QSIZE(RES_QUEUE_LEN), - &qpti->res_dvma); + qpti->res_cpu = dma_alloc_coherent(&op->dev, + QSIZE(RES_QUEUE_LEN), + &qpti->res_dvma, GFP_ATOMIC); if (qpti->res_cpu == NULL || qpti->res_dvma == 0) { printk("QPTI: Cannot map response queue.\n"); return -1; } - qpti->req_cpu = sbus_alloc_consistent(sdev, - QSIZE(QLOGICPTI_REQ_QUEUE_LEN), - &qpti->req_dvma); + qpti->req_cpu = dma_alloc_coherent(&op->dev, + QSIZE(QLOGICPTI_REQ_QUEUE_LEN), + &qpti->req_dvma, GFP_ATOMIC); if (qpti->req_cpu == NULL || qpti->req_dvma == 0) { - sbus_free_consistent(sdev, QSIZE(RES_QUEUE_LEN), - qpti->res_cpu, qpti->res_dvma); + dma_free_coherent(&op->dev, QSIZE(RES_QUEUE_LEN), + qpti->res_cpu, qpti->res_dvma); printk("QPTI: Cannot map request queue.\n"); return -1; } @@ -875,8 +878,9 @@ static inline int load_cmd(struct scsi_cmnd *Cmnd, struct Command_Entry *cmd, int sg_count; sg = scsi_sglist(Cmnd); - sg_count = sbus_map_sg(qpti->sdev, sg, scsi_sg_count(Cmnd), - Cmnd->sc_data_direction); + sg_count = dma_map_sg(&qpti->op->dev, sg, + scsi_sg_count(Cmnd), + Cmnd->sc_data_direction); ds = cmd->dataseg; cmd->segment_cnt = sg_count; @@ -1152,9 +1156,9 @@ static struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti) Cmnd->result = DID_ERROR << 16; if (scsi_bufflen(Cmnd)) - sbus_unmap_sg(qpti->sdev, - scsi_sglist(Cmnd), scsi_sg_count(Cmnd), - Cmnd->sc_data_direction); + dma_unmap_sg(&qpti->op->dev, + scsi_sglist(Cmnd), scsi_sg_count(Cmnd), + Cmnd->sc_data_direction); qpti->cmd_count[Cmnd->device->id]--; sbus_writew(out_ptr, qpti->qregs + MBOX5); @@ -1268,34 +1272,32 @@ static struct scsi_host_template qpti_template = { .use_clustering = ENABLE_CLUSTERING, }; -static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit qpti_sbus_probe(struct of_device *op, const struct of_device_id *match) { - static int nqptis; - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct device_node *dp = dev->node; struct scsi_host_template *tpnt = match->data; + struct device_node *dp = op->node; struct Scsi_Host *host; struct qlogicpti *qpti; + static int nqptis; const char *fcode; /* Sometimes Antares cards come up not completely * setup, and we get a report of a zero IRQ. */ - if (sdev->irqs[0] == 0) + if (op->irqs[0] == 0) return -ENODEV; host = scsi_host_alloc(tpnt, sizeof(struct qlogicpti)); if (!host) return -ENOMEM; - qpti = (struct qlogicpti *) host->hostdata; + qpti = shost_priv(host); host->max_id = MAX_TARGETS; qpti->qhost = host; - qpti->sdev = sdev; + qpti->op = op; qpti->qpti_id = nqptis; - qpti->prom_node = sdev->prom_node; - strcpy(qpti->prom_name, sdev->ofdev.node->name); + strcpy(qpti->prom_name, op->node->name); qpti->is_pti = strcmp(qpti->prom_name, "QLGC,isp"); if (qpti_map_regs(qpti) < 0) @@ -1341,12 +1343,12 @@ static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_devi (qpti->ultra ? "Ultra" : "Fast"), (qpti->differential ? "differential" : "single ended")); - if (scsi_add_host(host, &dev->dev)) { + if (scsi_add_host(host, &op->dev)) { printk("qlogicpti%d: Failed scsi_add_host\n", qpti->qpti_id); goto fail_unmap_queues; } - dev_set_drvdata(&sdev->ofdev.dev, qpti); + dev_set_drvdata(&op->dev, qpti); qpti_chain_add(qpti); @@ -1357,19 +1359,20 @@ static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_devi fail_unmap_queues: #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) - sbus_free_consistent(qpti->sdev, - QSIZE(RES_QUEUE_LEN), - qpti->res_cpu, qpti->res_dvma); - sbus_free_consistent(qpti->sdev, - QSIZE(QLOGICPTI_REQ_QUEUE_LEN), - qpti->req_cpu, qpti->req_dvma); + dma_free_coherent(&op->dev, + QSIZE(RES_QUEUE_LEN), + qpti->res_cpu, qpti->res_dvma); + dma_free_coherent(&op->dev, + QSIZE(QLOGICPTI_REQ_QUEUE_LEN), + qpti->req_cpu, qpti->req_dvma); #undef QSIZE fail_unmap_regs: - sbus_iounmap(qpti->qregs, - qpti->sdev->reg_addrs[0].reg_size); + of_iounmap(&op->resource[0], qpti->qregs, + resource_size(&op->resource[0])); if (qpti->is_pti) - sbus_iounmap(qpti->sreg, sizeof(unsigned char)); + of_iounmap(&op->resource[0], qpti->sreg, + sizeof(unsigned char)); fail_free_irq: free_irq(qpti->irq, qpti); @@ -1380,9 +1383,9 @@ fail_unlink: return -ENODEV; } -static int __devexit qpti_sbus_remove(struct of_device *dev) +static int __devexit qpti_sbus_remove(struct of_device *op) { - struct qlogicpti *qpti = dev_get_drvdata(&dev->dev); + struct qlogicpti *qpti = dev_get_drvdata(&op->dev); qpti_chain_del(qpti); @@ -1395,24 +1398,25 @@ static int __devexit qpti_sbus_remove(struct of_device *dev) free_irq(qpti->irq, qpti); #define QSIZE(entries) (((entries) + 1) * QUEUE_ENTRY_LEN) - sbus_free_consistent(qpti->sdev, - QSIZE(RES_QUEUE_LEN), - qpti->res_cpu, qpti->res_dvma); - sbus_free_consistent(qpti->sdev, - QSIZE(QLOGICPTI_REQ_QUEUE_LEN), - qpti->req_cpu, qpti->req_dvma); + dma_free_coherent(&op->dev, + QSIZE(RES_QUEUE_LEN), + qpti->res_cpu, qpti->res_dvma); + dma_free_coherent(&op->dev, + QSIZE(QLOGICPTI_REQ_QUEUE_LEN), + qpti->req_cpu, qpti->req_dvma); #undef QSIZE - sbus_iounmap(qpti->qregs, qpti->sdev->reg_addrs[0].reg_size); + of_iounmap(&op->resource[0], qpti->qregs, + resource_size(&op->resource[0])); if (qpti->is_pti) - sbus_iounmap(qpti->sreg, sizeof(unsigned char)); + of_iounmap(&op->resource[0], qpti->sreg, sizeof(unsigned char)); scsi_host_put(qpti->qhost); return 0; } -static struct of_device_id qpti_match[] = { +static const struct of_device_id qpti_match[] = { { .name = "ptisp", .data = &qpti_template, @@ -1442,7 +1446,7 @@ static struct of_platform_driver qpti_sbus_driver = { static int __init qpti_init(void) { - return of_register_driver(&qpti_sbus_driver, &sbus_bus_type); + return of_register_driver(&qpti_sbus_driver, &of_bus_type); } static void __exit qpti_exit(void) @@ -1453,7 +1457,7 @@ static void __exit qpti_exit(void) MODULE_DESCRIPTION("QlogicISP SBUS driver"); MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); MODULE_LICENSE("GPL"); -MODULE_VERSION("2.0"); +MODULE_VERSION("2.1"); module_init(qpti_init); module_exit(qpti_exit); diff --git a/drivers/scsi/qlogicpti.h b/drivers/scsi/qlogicpti.h index ef6da2df584..9c053bbaa87 100644 --- a/drivers/scsi/qlogicpti.h +++ b/drivers/scsi/qlogicpti.h @@ -342,7 +342,7 @@ struct qlogicpti { u_int req_in_ptr; /* index of next request slot */ u_int res_out_ptr; /* index of next result slot */ long send_marker; /* must we send a marker? */ - struct sbus_dev *sdev; + struct of_device *op; unsigned long __pad; int cmd_count[MAX_TARGETS]; diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c index f9cf7015136..3d73aad4bc8 100644 --- a/drivers/scsi/sun_esp.c +++ b/drivers/scsi/sun_esp.c @@ -1,6 +1,6 @@ /* sun_esp.c: ESP front-end for Sparc SBUS systems. * - * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + * Copyright (C) 2007, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> @@ -9,60 +9,70 @@ #include <linux/module.h> #include <linux/mm.h> #include <linux/init.h> +#include <linux/dma-mapping.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/dma.h> -#include <asm/sbus.h> - #include <scsi/scsi_host.h> #include "esp_scsi.h" #define DRV_MODULE_NAME "sun_esp" #define PFX DRV_MODULE_NAME ": " -#define DRV_VERSION "1.000" -#define DRV_MODULE_RELDATE "April 19, 2007" +#define DRV_VERSION "1.100" +#define DRV_MODULE_RELDATE "August 27, 2008" #define dma_read32(REG) \ sbus_readl(esp->dma_regs + (REG)) #define dma_write32(VAL, REG) \ sbus_writel((VAL), esp->dma_regs + (REG)) -static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sdev) -{ - struct sbus_dev *sdev = esp->dev; - struct sbus_dma *dma; +/* DVMA chip revisions */ +enum dvma_rev { + dvmarev0, + dvmaesc1, + dvmarev1, + dvmarev2, + dvmarev3, + dvmarevplus, + dvmahme +}; - if (dma_sdev != NULL) { - for_each_dvma(dma) { - if (dma->sdev == dma_sdev) - break; - } - } else { - for_each_dvma(dma) { - if (dma->sdev == NULL) - break; +static int __devinit esp_sbus_setup_dma(struct esp *esp, + struct of_device *dma_of) +{ + esp->dma = dma_of; - /* If bus + slot are the same and it has the - * correct OBP name, it's ours. - */ - if (sdev->bus == dma->sdev->bus && - sdev->slot == dma->sdev->slot && - (!strcmp(dma->sdev->prom_name, "dma") || - !strcmp(dma->sdev->prom_name, "espdma"))) - break; - } - } + esp->dma_regs = of_ioremap(&dma_of->resource[0], 0, + resource_size(&dma_of->resource[0]), + "espdma"); + if (!esp->dma_regs) + return -ENOMEM; - if (dma == NULL) { - printk(KERN_ERR PFX "[%s] Cannot find dma.\n", - sdev->ofdev.node->full_name); - return -ENODEV; + switch (dma_read32(DMA_CSR) & DMA_DEVICE_ID) { + case DMA_VERS0: + esp->dmarev = dvmarev0; + break; + case DMA_ESCV1: + esp->dmarev = dvmaesc1; + break; + case DMA_VERS1: + esp->dmarev = dvmarev1; + break; + case DMA_VERS2: + esp->dmarev = dvmarev2; + break; + case DMA_VERHME: + esp->dmarev = dvmahme; + break; + case DMA_VERSPLUS: + esp->dmarev = dvmarevplus; + break; } - esp->dma = dma; - esp->dma_regs = dma->regs; return 0; @@ -70,18 +80,18 @@ static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sde static int __devinit esp_sbus_map_regs(struct esp *esp, int hme) { - struct sbus_dev *sdev = esp->dev; + struct of_device *op = esp->dev; struct resource *res; /* On HME, two reg sets exist, first is DVMA, * second is ESP registers. */ if (hme) - res = &sdev->resource[1]; + res = &op->resource[1]; else - res = &sdev->resource[0]; + res = &op->resource[0]; - esp->regs = sbus_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP"); + esp->regs = of_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP"); if (!esp->regs) return -ENOMEM; @@ -90,10 +100,11 @@ static int __devinit esp_sbus_map_regs(struct esp *esp, int hme) static int __devinit esp_sbus_map_command_block(struct esp *esp) { - struct sbus_dev *sdev = esp->dev; + struct of_device *op = esp->dev; - esp->command_block = sbus_alloc_consistent(sdev, 16, - &esp->command_block_dma); + esp->command_block = dma_alloc_coherent(&op->dev, 16, + &esp->command_block_dma, + GFP_ATOMIC); if (!esp->command_block) return -ENOMEM; return 0; @@ -102,17 +113,18 @@ static int __devinit esp_sbus_map_command_block(struct esp *esp) static int __devinit esp_sbus_register_irq(struct esp *esp) { struct Scsi_Host *host = esp->host; - struct sbus_dev *sdev = esp->dev; + struct of_device *op = esp->dev; - host->irq = sdev->irqs[0]; + host->irq = op->irqs[0]; return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp); } -static void __devinit esp_get_scsi_id(struct esp *esp) +static void __devinit esp_get_scsi_id(struct esp *esp, struct of_device *espdma) { - struct sbus_dev *sdev = esp->dev; - struct device_node *dp = sdev->ofdev.node; + struct of_device *op = esp->dev; + struct device_node *dp; + dp = op->node; esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff); if (esp->scsi_id != 0xff) goto done; @@ -121,13 +133,7 @@ static void __devinit esp_get_scsi_id(struct esp *esp) if (esp->scsi_id != 0xff) goto done; - if (!sdev->bus) { - /* SUN4 */ - esp->scsi_id = 7; - goto done; - } - - esp->scsi_id = of_getintprop_default(sdev->bus->ofdev.node, + esp->scsi_id = of_getintprop_default(espdma->node, "scsi-initiator-id", 7); done: @@ -137,9 +143,10 @@ done: static void __devinit esp_get_differential(struct esp *esp) { - struct sbus_dev *sdev = esp->dev; - struct device_node *dp = sdev->ofdev.node; + struct of_device *op = esp->dev; + struct device_node *dp; + dp = op->node; if (of_find_property(dp, "differential", NULL)) esp->flags |= ESP_FLAG_DIFFERENTIAL; else @@ -148,43 +155,36 @@ static void __devinit esp_get_differential(struct esp *esp) static void __devinit esp_get_clock_params(struct esp *esp) { - struct sbus_dev *sdev = esp->dev; - struct device_node *dp = sdev->ofdev.node; - struct device_node *bus_dp; + struct of_device *op = esp->dev; + struct device_node *bus_dp, *dp; int fmhz; - bus_dp = NULL; - if (sdev != NULL && sdev->bus != NULL) - bus_dp = sdev->bus->ofdev.node; + dp = op->node; + bus_dp = dp->parent; fmhz = of_getintprop_default(dp, "clock-frequency", 0); if (fmhz == 0) - fmhz = (!bus_dp) ? 0 : - of_getintprop_default(bus_dp, "clock-frequency", 0); + fmhz = of_getintprop_default(bus_dp, "clock-frequency", 0); esp->cfreq = fmhz; } -static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma) +static void __devinit esp_get_bursts(struct esp *esp, struct of_device *dma_of) { - struct sbus_dev *sdev = esp->dev; - struct device_node *dp = sdev->ofdev.node; - u8 bursts; + struct device_node *dma_dp = dma_of->node; + struct of_device *op = esp->dev; + struct device_node *dp; + u8 bursts, val; + dp = op->node; bursts = of_getintprop_default(dp, "burst-sizes", 0xff); - if (dma) { - struct device_node *dma_dp = dma->ofdev.node; - u8 val = of_getintprop_default(dma_dp, "burst-sizes", 0xff); - if (val != 0xff) - bursts &= val; - } + val = of_getintprop_default(dma_dp, "burst-sizes", 0xff); + if (val != 0xff) + bursts &= val; - if (sdev->bus) { - u8 val = of_getintprop_default(sdev->bus->ofdev.node, - "burst-sizes", 0xff); - if (val != 0xff) - bursts &= val; - } + val = of_getintprop_default(dma_dp->parent, "burst-sizes", 0xff); + if (val != 0xff) + bursts &= val; if (bursts == 0xff || (bursts & DMA_BURST16) == 0 || @@ -194,9 +194,9 @@ static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma) esp->bursts = bursts; } -static void __devinit esp_sbus_get_props(struct esp *esp, struct sbus_dev *espdma) +static void __devinit esp_sbus_get_props(struct esp *esp, struct of_device *espdma) { - esp_get_scsi_id(esp); + esp_get_scsi_id(esp, espdma); esp_get_differential(esp); esp_get_clock_params(esp); esp_get_bursts(esp, espdma); @@ -215,25 +215,33 @@ static u8 sbus_esp_read8(struct esp *esp, unsigned long reg) static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf, size_t sz, int dir) { - return sbus_map_single(esp->dev, buf, sz, dir); + struct of_device *op = esp->dev; + + return dma_map_single(&op->dev, buf, sz, dir); } static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { - return sbus_map_sg(esp->dev, sg, num_sg, dir); + struct of_device *op = esp->dev; + + return dma_map_sg(&op->dev, sg, num_sg, dir); } static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr, size_t sz, int dir) { - sbus_unmap_single(esp->dev, addr, sz, dir); + struct of_device *op = esp->dev; + + dma_unmap_single(&op->dev, addr, sz, dir); } static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, int num_sg, int dir) { - sbus_unmap_sg(esp->dev, sg, num_sg, dir); + struct of_device *op = esp->dev; + + dma_unmap_sg(&op->dev, sg, num_sg, dir); } static int sbus_esp_irq_pending(struct esp *esp) @@ -247,24 +255,26 @@ static void sbus_esp_reset_dma(struct esp *esp) { int can_do_burst16, can_do_burst32, can_do_burst64; int can_do_sbus64, lim; + struct of_device *op; u32 val; can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; can_do_burst32 = (esp->bursts & DMA_BURST32) != 0; can_do_burst64 = 0; can_do_sbus64 = 0; - if (sbus_can_dma_64bit(esp->dev)) + op = esp->dev; + if (sbus_can_dma_64bit()) can_do_sbus64 = 1; - if (sbus_can_burst64(esp->sdev)) + if (sbus_can_burst64()) can_do_burst64 = (esp->bursts & DMA_BURST64) != 0; /* Put the DVMA into a known state. */ - if (esp->dma->revision != dvmahme) { + if (esp->dmarev != dvmahme) { val = dma_read32(DMA_CSR); dma_write32(val | DMA_RST_SCSI, DMA_CSR); dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); } - switch (esp->dma->revision) { + switch (esp->dmarev) { case dvmahme: dma_write32(DMA_RESET_FAS366, DMA_CSR); dma_write32(DMA_RST_SCSI, DMA_CSR); @@ -282,7 +292,7 @@ static void sbus_esp_reset_dma(struct esp *esp) if (can_do_sbus64) { esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64; - sbus_set_sbus64(esp->dev, esp->bursts); + sbus_set_sbus64(&op->dev, esp->bursts); } lim = 1000; @@ -346,14 +356,14 @@ static void sbus_esp_dma_drain(struct esp *esp) u32 csr; int lim; - if (esp->dma->revision == dvmahme) + if (esp->dmarev == dvmahme) return; csr = dma_read32(DMA_CSR); if (!(csr & DMA_FIFO_ISDRAIN)) return; - if (esp->dma->revision != dvmarev3 && esp->dma->revision != dvmaesc1) + if (esp->dmarev != dvmarev3 && esp->dmarev != dvmaesc1) dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); lim = 1000; @@ -369,7 +379,7 @@ static void sbus_esp_dma_drain(struct esp *esp) static void sbus_esp_dma_invalidate(struct esp *esp) { - if (esp->dma->revision == dvmahme) { + if (esp->dmarev == dvmahme) { dma_write32(DMA_RST_SCSI, DMA_CSR); esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | @@ -440,7 +450,7 @@ static void sbus_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, else csr &= ~DMA_ST_WRITE; dma_write32(csr, DMA_CSR); - if (esp->dma->revision == dvmaesc1) { + if (esp->dmarev == dvmaesc1) { u32 end = PAGE_ALIGN(addr + dma_count + 16U); dma_write32(end - addr, DMA_COUNT); } @@ -476,10 +486,8 @@ static const struct esp_driver_ops sbus_esp_ops = { .dma_error = sbus_esp_dma_error, }; -static int __devinit esp_sbus_probe_one(struct device *dev, - struct sbus_dev *esp_dev, - struct sbus_dev *espdma, - struct sbus_bus *sbus, +static int __devinit esp_sbus_probe_one(struct of_device *op, + struct of_device *espdma, int hme) { struct scsi_host_template *tpnt = &scsi_esp_template; @@ -497,13 +505,13 @@ static int __devinit esp_sbus_probe_one(struct device *dev, esp = shost_priv(host); esp->host = host; - esp->dev = esp_dev; + esp->dev = op; esp->ops = &sbus_esp_ops; if (hme) esp->flags |= ESP_FLAG_WIDE_CAPABLE; - err = esp_sbus_find_dma(esp, espdma); + err = esp_sbus_setup_dma(esp, espdma); if (err < 0) goto fail_unlink; @@ -525,15 +533,15 @@ static int __devinit esp_sbus_probe_one(struct device *dev, * come up with the reset bit set, so make sure that * is clear first. */ - if (esp->dma->revision == dvmaesc1) { + if (esp->dmarev == dvmaesc1) { u32 val = dma_read32(DMA_CSR); dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); } - dev_set_drvdata(&esp_dev->ofdev.dev, esp); + dev_set_drvdata(&op->dev, esp); - err = scsi_esp_register(esp, dev); + err = scsi_esp_register(esp, &op->dev); if (err) goto fail_free_irq; @@ -542,41 +550,46 @@ static int __devinit esp_sbus_probe_one(struct device *dev, fail_free_irq: free_irq(host->irq, esp); fail_unmap_command_block: - sbus_free_consistent(esp->dev, 16, - esp->command_block, - esp->command_block_dma); + dma_free_coherent(&op->dev, 16, + esp->command_block, + esp->command_block_dma); fail_unmap_regs: - sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE); + of_iounmap(&op->resource[(hme ? 1 : 0)], esp->regs, SBUS_ESP_REG_SIZE); fail_unlink: scsi_host_put(host); fail: return err; } -static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit esp_sbus_probe(struct of_device *op, const struct of_device_id *match) { - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct device_node *dp = dev->node; - struct sbus_dev *dma_sdev = NULL; + struct device_node *dma_node = NULL; + struct device_node *dp = op->node; + struct of_device *dma_of = NULL; int hme = 0; if (dp->parent && (!strcmp(dp->parent->name, "espdma") || !strcmp(dp->parent->name, "dma"))) - dma_sdev = sdev->parent; + dma_node = dp->parent; else if (!strcmp(dp->name, "SUNW,fas")) { - dma_sdev = sdev; + dma_node = op->node; hme = 1; } + if (dma_node) + dma_of = of_find_device_by_node(dma_node); + if (!dma_of) + return -ENODEV; - return esp_sbus_probe_one(&dev->dev, sdev, dma_sdev, - sdev->bus, hme); + return esp_sbus_probe_one(op, dma_of, hme); } -static int __devexit esp_sbus_remove(struct of_device *dev) +static int __devexit esp_sbus_remove(struct of_device *op) { - struct esp *esp = dev_get_drvdata(&dev->dev); + struct esp *esp = dev_get_drvdata(&op->dev); + struct of_device *dma_of = esp->dma; unsigned int irq = esp->host->irq; + bool is_hme; u32 val; scsi_esp_unregister(esp); @@ -586,17 +599,25 @@ static int __devexit esp_sbus_remove(struct of_device *dev) dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); free_irq(irq, esp); - sbus_free_consistent(esp->dev, 16, - esp->command_block, - esp->command_block_dma); - sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE); + + is_hme = (esp->dmarev == dvmahme); + + dma_free_coherent(&op->dev, 16, + esp->command_block, + esp->command_block_dma); + of_iounmap(&op->resource[(is_hme ? 1 : 0)], esp->regs, + SBUS_ESP_REG_SIZE); + of_iounmap(&dma_of->resource[0], esp->dma_regs, + resource_size(&dma_of->resource[0])); scsi_host_put(esp->host); + dev_set_drvdata(&op->dev, NULL); + return 0; } -static struct of_device_id esp_match[] = { +static const struct of_device_id esp_match[] = { { .name = "SUNW,esp", }, @@ -619,7 +640,7 @@ static struct of_platform_driver esp_sbus_driver = { static int __init sunesp_init(void) { - return of_register_driver(&esp_sbus_driver, &sbus_bus_type); + return of_register_driver(&esp_sbus_driver, &of_bus_type); } static void __exit sunesp_exit(void) diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 9ccc563d873..d4104a3bbe8 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -44,6 +44,10 @@ #include "8250.h" +#ifdef CONFIG_SPARC +#include "suncore.h" +#endif + /* * Configuration: * share_irqs - whether we pass IRQF_SHARED to request_irq(). This option @@ -53,6 +57,13 @@ static unsigned int share_irqs = SERIAL8250_SHARE_IRQS; static unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS; +static struct uart_driver serial8250_reg; + +static int serial_index(struct uart_port *port) +{ + return (serial8250_reg.minor - 64) + port->line; +} + /* * Debugging. */ @@ -536,7 +547,7 @@ static unsigned int serial_icr_read(struct uart_8250_port *up, int offset) /* * FIFO support. */ -static inline void serial8250_clear_fifos(struct uart_8250_port *p) +static void serial8250_clear_fifos(struct uart_8250_port *p) { if (p->capabilities & UART_CAP_FIFO) { serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO); @@ -551,7 +562,7 @@ static inline void serial8250_clear_fifos(struct uart_8250_port *p) * capability" bit enabled. Note that on XR16C850s, we need to * reset LCR to write to IER. */ -static inline void serial8250_set_sleep(struct uart_8250_port *p, int sleep) +static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { @@ -993,7 +1004,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) return; DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", - up->port.line, up->port.iobase, up->port.membase); + serial_index(&up->port), up->port.iobase, up->port.membase); /* * We really do need global IRQs disabled here - we're going to @@ -1128,8 +1139,8 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags) if (up->capabilities != uart_config[up->port.type].flags) { printk(KERN_WARNING "ttyS%d: detected caps %08x should be %08x\n", - up->port.line, up->capabilities, - uart_config[up->port.type].flags); + serial_index(&up->port), up->capabilities, + uart_config[up->port.type].flags); } up->port.fifosize = uart_config[up->port.type].fifo_size; @@ -1424,8 +1435,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) /* * This handles the interrupt from one port. */ -static inline void -serial8250_handle_port(struct uart_8250_port *up) +static void serial8250_handle_port(struct uart_8250_port *up) { unsigned int status; unsigned long flags; @@ -1719,7 +1729,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) /* * Wait for transmitter & holding register to empty */ -static inline void wait_for_xmitr(struct uart_8250_port *up, int bits) +static void wait_for_xmitr(struct uart_8250_port *up, int bits) { unsigned int status, tmout = 10000; @@ -1854,7 +1864,8 @@ static int serial8250_startup(struct uart_port *port) */ if (!(up->port.flags & UPF_BUGGY_UART) && (serial_inp(up, UART_LSR) == 0xff)) { - printk("ttyS%d: LSR safety check engaged!\n", up->port.line); + printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n", + serial_index(&up->port)); return -ENODEV; } @@ -1909,7 +1920,8 @@ static int serial8250_startup(struct uart_port *port) */ if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) { up->bugs |= UART_BUG_THRE; - pr_debug("ttyS%d - using backup timer\n", port->line); + pr_debug("ttyS%d - using backup timer\n", + serial_index(port)); } } @@ -1969,7 +1981,7 @@ static int serial8250_startup(struct uart_port *port) if (!(up->bugs & UART_BUG_TXEN)) { up->bugs |= UART_BUG_TXEN; pr_debug("ttyS%d - enabling bad tx status workarounds\n", - port->line); + serial_index(port)); } } else { up->bugs &= ~UART_BUG_TXEN; @@ -2630,7 +2642,6 @@ static int serial8250_console_early_setup(void) return serial8250_find_port_for_earlycon(); } -static struct uart_driver serial8250_reg; static struct console serial8250_console = { .name = "ttyS", .write = serial8250_console_write, @@ -2677,7 +2688,6 @@ static struct uart_driver serial8250_reg = { .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, - .nr = UART_NR, .cons = SERIAL8250_CONSOLE, }; @@ -2959,10 +2969,12 @@ static int __init serial8250_init(void) "%d ports, IRQ sharing %sabled\n", nr_uarts, share_irqs ? "en" : "dis"); - for (i = 0; i < NR_IRQS; i++) - spin_lock_init(&irq_lists[i].lock); - +#ifdef CONFIG_SPARC + ret = sunserial_register_minors(&serial8250_reg, UART_NR); +#else + serial8250_reg.nr = UART_NR; ret = uart_register_driver(&serial8250_reg); +#endif if (ret) goto out; @@ -2987,7 +2999,11 @@ static int __init serial8250_init(void) put_dev: platform_device_put(serial8250_isa_devs); unreg_uart_drv: +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else uart_unregister_driver(&serial8250_reg); +#endif out: return ret; } @@ -3006,7 +3022,11 @@ static void __exit serial8250_exit(void) platform_driver_unregister(&serial8250_isa_driver); platform_device_unregister(isa_dev); +#ifdef CONFIG_SPARC + sunserial_unregister_minors(&serial8250_reg, UART_NR); +#else uart_unregister_driver(&serial8250_reg); +#endif } module_init(serial8250_init); diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index c2f23933155..c014ffb110e 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -2041,9 +2041,9 @@ static int pciserial_resume_one(struct pci_dev *dev) * The device may have been disabled. Re-enable it. */ err = pci_enable_device(dev); + /* FIXME: We cannot simply error out here */ if (err) - return err; - + printk(KERN_ERR "pciserial: Unable to re-enable ports, trying to continue.\n"); pciserial_resume_ports(priv); } return 0; diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 77cb34270fc..31786b3b0a6 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -9,7 +9,6 @@ menu "Serial drivers" # The new 8250/16550 serial drivers config SERIAL_8250 tristate "8250/16550 and compatible serial support" - depends on (BROKEN || !SPARC) select SERIAL_CORE ---help--- This selects whether you want to include the driver for the standard @@ -994,24 +993,12 @@ config SERIAL_68328_RTS_CTS bool "Support RTS/CTS on 68328 serial port" depends on SERIAL_68328 -config SERIAL_COLDFIRE - bool "ColdFire serial support (DEPRECATED)" - depends on COLDFIRE - help - This driver supports the built-in serial ports of the Motorola ColdFire - family of CPUs. - This driver is deprecated because it supports only the old interface - for serial drivers and features like magic keys are not working. - Please switch to the new style driver because this driver will be - removed soon. - config SERIAL_MCF - bool "Coldfire serial support (new style driver)" + bool "Coldfire serial support" depends on COLDFIRE select SERIAL_CORE help - This new serial driver supports the Freescale Coldfire serial ports - using the new serial driver subsystem. + This serial driver supports the Freescale Coldfire serial ports. config SERIAL_MCF_BAUDRATE int "Default baudrate for Coldfire serial ports" diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 7e7383e890d..0c17c8ddb19 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -4,6 +4,16 @@ obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o + +# These Sparc drivers have to appear before others such as 8250 +# which share ttySx minor node space. Otherwise console device +# names change and other unplesantries. +obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o +obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o +obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o +obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o +obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o + obj-$(CONFIG_SERIAL_8250) += 8250.o obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o @@ -31,16 +41,10 @@ obj-$(CONFIG_SERIAL_S3C2400) += s3c2400.o obj-$(CONFIG_SERIAL_S3C2410) += s3c2410.o obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o -obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o -obj-$(CONFIG_SERIAL_SUNHV) += sunhv.o -obj-$(CONFIG_SERIAL_SUNZILOG) += sunzilog.o obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o -obj-$(CONFIG_SERIAL_SUNSU) += sunsu.o -obj-$(CONFIG_SERIAL_SUNSAB) += sunsab.o obj-$(CONFIG_SERIAL_MUX) += mux.o obj-$(CONFIG_SERIAL_68328) += 68328serial.o obj-$(CONFIG_SERIAL_68360) += 68360serial.o -obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o obj-$(CONFIG_SERIAL_MCF) += mcf.o obj-$(CONFIG_SERIAL_PMACZILOG) += pmac_zilog.o obj-$(CONFIG_SERIAL_LH7A40X) += serial_lh7a40x.o diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index 4a0d30bed9f..569f0e2476c 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -1,7 +1,7 @@ /* * Blackfin On-Chip Serial Driver * - * Copyright 2006-2007 Analog Devices Inc. + * Copyright 2006-2008 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -42,6 +42,9 @@ #define BFIN_SERIAL_MAJOR 204 #define BFIN_SERIAL_MINOR 64 +static struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS]; +static int nr_active_ports = ARRAY_SIZE(bfin_serial_resource); + /* * Setup for console. Argument comes from the menuconfig */ @@ -126,13 +129,13 @@ static int kgdb_entry_state; void kgdb_put_debug_char(int chr) { struct bfin_serial_port *uart; - + if (CONFIG_KGDB_UART_PORT < 0 || CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS) uart = &bfin_serial_ports[0]; else uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT]; - + while (!(UART_GET_LSR(uart) & THRE)) { SSYNC(); } @@ -152,7 +155,7 @@ int kgdb_get_debug_char(void) uart = &bfin_serial_ports[0]; else uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT]; - + while(!(UART_GET_LSR(uart) & DR)) { SSYNC(); } @@ -298,7 +301,11 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) bfin_serial_mctrl_check(uart); if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) { - bfin_serial_stop_tx(&uart->port); +#ifdef CONFIG_BF54x + /* Clear TFI bit */ + UART_PUT_LSR(uart, TFI); +#endif + UART_CLEAR_IER(uart, ETBEI); return; } @@ -317,9 +324,6 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(&uart->port); - - if (uart_circ_empty(xmit)) - bfin_serial_stop_tx(&uart->port); } static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id) @@ -645,6 +649,42 @@ static int bfin_serial_startup(struct uart_port *port) free_irq(uart->port.irq, uart); return -EBUSY; } + +# ifdef CONFIG_BF54x + { + unsigned uart_dma_ch_rx, uart_dma_ch_tx; + + switch (uart->port.irq) { + case IRQ_UART3_RX: + uart_dma_ch_rx = CH_UART3_RX; + uart_dma_ch_tx = CH_UART3_TX; + break; + case IRQ_UART2_RX: + uart_dma_ch_rx = CH_UART2_RX; + uart_dma_ch_tx = CH_UART2_TX; + break; + default: + uart_dma_ch_rx = uart_dma_ch_tx = 0; + break; + }; + + if (uart_dma_ch_rx && + request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) { + printk(KERN_NOTICE"Fail to attach UART interrupt\n"); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, uart); + return -EBUSY; + } + if (uart_dma_ch_tx && + request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) { + printk(KERN_NOTICE "Fail to attach UART interrupt\n"); + free_dma(uart_dma_ch_rx); + free_irq(uart->port.irq, uart); + free_irq(uart->port.irq + 1, uart); + return -EBUSY; + } + } +# endif #endif UART_SET_IER(uart, ERBFI); return 0; @@ -662,6 +702,20 @@ static void bfin_serial_shutdown(struct uart_port *port) del_timer(&(uart->rx_dma_timer)); dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0); #else +#ifdef CONFIG_BF54x + switch (uart->port.irq) { + case IRQ_UART3_RX: + free_dma(CH_UART3_RX); + free_dma(CH_UART3_TX); + break; + case IRQ_UART2_RX: + free_dma(CH_UART2_RX); + free_dma(CH_UART2_TX); + break; + default: + break; + }; +#endif #ifdef CONFIG_KGDB_UART if (uart->port.line != CONFIG_KGDB_UART_PORT) #endif @@ -757,6 +811,9 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios, val |= UCEN; UART_PUT_GCTL(uart, val); + /* Port speed changed, update the per-port timeout. */ + uart_update_timeout(port, termios->c_cflag, baud); + spin_unlock_irqrestore(&uart->port.lock, flags); } @@ -859,8 +916,9 @@ static void __init bfin_serial_init_ports(void) return; first = 0; - for (i = 0; i < nr_ports; i++) { + for (i = 0; i < nr_active_ports; i++) { bfin_serial_ports[i].port.uartclk = get_sclk(); + bfin_serial_ports[i].port.fifosize = BFIN_UART_TX_FIFO_SIZE; bfin_serial_ports[i].port.ops = &bfin_serial_pops; bfin_serial_ports[i].port.line = i; bfin_serial_ports[i].port.iotype = UPIO_MEM; @@ -961,7 +1019,7 @@ bfin_serial_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index == -1 || co->index >= nr_ports) + if (co->index == -1 || co->index >= nr_active_ports) co->index = 0; uart = &bfin_serial_ports[co->index]; @@ -1056,7 +1114,7 @@ static __init void early_serial_write(struct console *con, const char *s, } } -static struct __init console bfin_early_serial_console = { +static struct __initdata console bfin_early_serial_console = { .name = "early_BFuart", .write = early_serial_write, .device = uart_console_device, @@ -1072,7 +1130,7 @@ struct console __init *bfin_earlyserial_init(unsigned int port, struct bfin_serial_port *uart; struct ktermios t; - if (port == -1 || port >= nr_ports) + if (port == -1 || port >= nr_active_ports) port = 0; bfin_serial_init_ports(); bfin_early_serial_console.index = port; @@ -1100,20 +1158,26 @@ static struct uart_driver bfin_serial_reg = { static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state) { - struct bfin_serial_port *uart = platform_get_drvdata(dev); + int i; - if (uart) - uart_suspend_port(&bfin_serial_reg, &uart->port); + for (i = 0; i < nr_active_ports; i++) { + if (bfin_serial_ports[i].port.dev != &dev->dev) + continue; + uart_suspend_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + } return 0; } static int bfin_serial_resume(struct platform_device *dev) { - struct bfin_serial_port *uart = platform_get_drvdata(dev); + int i; - if (uart) - uart_resume_port(&bfin_serial_reg, &uart->port); + for (i = 0; i < nr_active_ports; i++) { + if (bfin_serial_ports[i].port.dev != &dev->dev) + continue; + uart_resume_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + } return 0; } @@ -1128,32 +1192,31 @@ static int bfin_serial_probe(struct platform_device *dev) break; if (i < dev->num_resources) { - for (i = 0; i < nr_ports; i++, res++) { + for (i = 0; i < nr_active_ports; i++, res++) { if (bfin_serial_ports[i].port.mapbase != res->start) continue; bfin_serial_ports[i].port.dev = &dev->dev; uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); - platform_set_drvdata(dev, &bfin_serial_ports[i]); } } return 0; } -static int bfin_serial_remove(struct platform_device *pdev) +static int bfin_serial_remove(struct platform_device *dev) { - struct bfin_serial_port *uart = platform_get_drvdata(pdev); - + int i; + for (i = 0; i < nr_active_ports; i++) { + if (bfin_serial_ports[i].port.dev != &dev->dev) + continue; + uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port); + bfin_serial_ports[i].port.dev = NULL; #ifdef CONFIG_SERIAL_BFIN_CTSRTS - gpio_free(uart->cts_pin); - gpio_free(uart->rts_pin); + gpio_free(bfin_serial_ports[i].cts_pin); + gpio_free(bfin_serial_ports[i].rts_pin); #endif - - platform_set_drvdata(pdev, NULL); - - if (uart) - uart_remove_one_port(&bfin_serial_reg, &uart->port); + } return 0; } diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c index bf94a770bb4..211c21797ce 100644 --- a/drivers/serial/crisv10.c +++ b/drivers/serial/crisv10.c @@ -457,7 +457,6 @@ static struct e100_serial rs_table[] = { #define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial)) static struct ktermios *serial_termios[NR_PORTS]; -static struct ktermios *serial_termios_locked[NR_PORTS]; #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER static struct fast_timer fast_timers[NR_PORTS]; #endif @@ -4419,6 +4418,7 @@ rs_init(void) rs485_pa_bit)) { printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); + put_tty_driver(driver); return -EBUSY; } #endif @@ -4427,6 +4427,7 @@ rs_init(void) rs485_port_g_bit)) { printk(KERN_CRIT "ETRAX100LX serial: Could not allocate " "RS485 pin\n"); + put_tty_driver(driver); return -EBUSY; } #endif @@ -4446,8 +4447,6 @@ rs_init(void) driver->init_termios.c_ispeed = 115200; driver->init_termios.c_ospeed = 115200; driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; - driver->termios = serial_termios; - driver->termios_locked = serial_termios_locked; tty_set_operations(driver, &rs_ops); serial_driver = driver; diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c deleted file mode 100644 index fbe3835f6b7..00000000000 --- a/drivers/serial/mcfserial.c +++ /dev/null @@ -1,1965 +0,0 @@ -#warning This driver is deprecated. Check Kconfig for details. -/* - * mcfserial.c -- serial driver for ColdFire internal UARTS. - * - * Copyright (C) 1999-2003 Greg Ungerer <gerg@snapgear.com> - * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com> - * Copyright (C) 2001-2002 SnapGear Inc. <www.snapgear.com> - * - * Based on code from 68332serial.c which was: - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 TSHG - * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org> - * - * Changes: - * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> - * some cleanups in mcfrs_write. - * - */ - -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/wait.h> -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/serial.h> -#include <linux/serialP.h> -#include <linux/console.h> -#include <linux/init.h> -#include <linux/bitops.h> -#include <linux/delay.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/system.h> -#include <asm/delay.h> -#include <asm/coldfire.h> -#include <asm/mcfsim.h> -#include <asm/mcfuart.h> -#include <asm/nettel.h> -#include <asm/uaccess.h> -#include "mcfserial.h" - -struct timer_list mcfrs_timer_struct; - -/* - * Default console baud rate, we use this as the default - * for all ports so init can just open /dev/console and - * keep going. Perhaps one day the cflag settings for the - * console can be used instead. - */ -#if defined(CONFIG_HW_FEITH) -#define CONSOLE_BAUD_RATE 38400 -#define DEFAULT_CBAUD B38400 -#elif defined(CONFIG_MOD5272) || defined(CONFIG_M5208EVB) || \ - defined(CONFIG_M5329EVB) || defined(CONFIG_GILBARCO) -#define CONSOLE_BAUD_RATE 115200 -#define DEFAULT_CBAUD B115200 -#elif defined(CONFIG_ARNEWSH) || defined(CONFIG_FREESCALE) || \ - defined(CONFIG_senTec) || defined(CONFIG_SNEHA) || defined(CONFIG_AVNET) -#define CONSOLE_BAUD_RATE 19200 -#define DEFAULT_CBAUD B19200 -#endif - -#ifndef CONSOLE_BAUD_RATE -#define CONSOLE_BAUD_RATE 9600 -#define DEFAULT_CBAUD B9600 -#endif - -int mcfrs_console_inited = 0; -int mcfrs_console_port = -1; -int mcfrs_console_baud = CONSOLE_BAUD_RATE; -int mcfrs_console_cbaud = DEFAULT_CBAUD; - -/* - * Driver data structures. - */ -static struct tty_driver *mcfrs_serial_driver; - -/* number of characters left in xmit buffer before we ask for more */ -#define WAKEUP_CHARS 256 - -/* Debugging... - */ -#undef SERIAL_DEBUG_OPEN -#undef SERIAL_DEBUG_FLOW - -#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ - defined(CONFIG_M520x) || defined(CONFIG_M532x) -#define IRQBASE (MCFINT_VECBASE+MCFINT_UART0) -#else -#define IRQBASE 73 -#endif - -/* - * Configuration table, UARTs to look for at startup. - */ -static struct mcf_serial mcfrs_table[] = { - { /* ttyS0 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE1), - .irq = IRQBASE, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#ifdef MCFUART_BASE2 - { /* ttyS1 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE2), - .irq = IRQBASE+1, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#endif -#ifdef MCFUART_BASE3 - { /* ttyS2 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE3), - .irq = IRQBASE+2, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#endif -#ifdef MCFUART_BASE4 - { /* ttyS3 */ - .magic = 0, - .addr = (volatile unsigned char *) (MCF_MBAR+MCFUART_BASE4), - .irq = IRQBASE+3, - .flags = ASYNC_BOOT_AUTOCONF, - }, -#endif -}; - - -#define NR_PORTS (sizeof(mcfrs_table) / sizeof(struct mcf_serial)) - -/* - * This is used to figure out the divisor speeds and the timeouts. - */ -static int mcfrs_baud_table[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 -}; -#define MCFRS_BAUD_TABLE_SIZE \ - (sizeof(mcfrs_baud_table)/sizeof(mcfrs_baud_table[0])) - - -#ifdef CONFIG_MAGIC_SYSRQ -/* - * Magic system request keys. Used for debugging... - */ -extern int magic_sysrq_key(int ch); -#endif - - -/* - * Forware declarations... - */ -static void mcfrs_change_speed(struct mcf_serial *info); -static void mcfrs_wait_until_sent(struct tty_struct *tty, int timeout); - - -static inline int serial_paranoia_check(struct mcf_serial *info, - char *name, const char *routine) -{ -#ifdef SERIAL_PARANOIA_CHECK - static const char badmagic[] = - "MCFRS(warning): bad magic number for serial struct %s in %s\n"; - static const char badinfo[] = - "MCFRS(warning): null mcf_serial for %s in %s\n"; - - if (!info) { - printk(badinfo, name, routine); - return 1; - } - if (info->magic != SERIAL_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * Sets or clears DTR and RTS on the requested line. - */ -static void mcfrs_setsignals(struct mcf_serial *info, int dtr, int rts) -{ - volatile unsigned char *uartp; - unsigned long flags; - -#if 0 - printk("%s(%d): mcfrs_setsignals(info=%x,dtr=%d,rts=%d)\n", - __FILE__, __LINE__, info, dtr, rts); -#endif - - local_irq_save(flags); - if (dtr >= 0) { -#ifdef MCFPP_DTR0 - if (info->line) - mcf_setppdata(MCFPP_DTR1, (dtr ? 0 : MCFPP_DTR1)); - else - mcf_setppdata(MCFPP_DTR0, (dtr ? 0 : MCFPP_DTR0)); -#endif - } - if (rts >= 0) { - uartp = info->addr; - if (rts) { - info->sigs |= TIOCM_RTS; - uartp[MCFUART_UOP1] = MCFUART_UOP_RTS; - } else { - info->sigs &= ~TIOCM_RTS; - uartp[MCFUART_UOP0] = MCFUART_UOP_RTS; - } - } - local_irq_restore(flags); - return; -} - -/* - * Gets values of serial signals. - */ -static int mcfrs_getsignals(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - unsigned long flags; - int sigs; -#if defined(CONFIG_NETtel) && defined(CONFIG_M5307) - unsigned short ppdata; -#endif - -#if 0 - printk("%s(%d): mcfrs_getsignals(info=%x)\n", __FILE__, __LINE__); -#endif - - local_irq_save(flags); - uartp = info->addr; - sigs = (uartp[MCFUART_UIPR] & MCFUART_UIPR_CTS) ? 0 : TIOCM_CTS; - sigs |= (info->sigs & TIOCM_RTS); - -#ifdef MCFPP_DCD0 -{ - unsigned int ppdata; - ppdata = mcf_getppdata(); - if (info->line == 0) { - sigs |= (ppdata & MCFPP_DCD0) ? 0 : TIOCM_CD; - sigs |= (ppdata & MCFPP_DTR0) ? 0 : TIOCM_DTR; - } else if (info->line == 1) { - sigs |= (ppdata & MCFPP_DCD1) ? 0 : TIOCM_CD; - sigs |= (ppdata & MCFPP_DTR1) ? 0 : TIOCM_DTR; - } -} -#endif - - local_irq_restore(flags); - return(sigs); -} - -/* - * ------------------------------------------------------------ - * mcfrs_stop() and mcfrs_start() - * - * This routines are called before setting or resetting tty->stopped. - * They enable or disable transmitter interrupts, as necessary. - * ------------------------------------------------------------ - */ -static void mcfrs_stop(struct tty_struct *tty) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_stop")) - return; - - local_irq_save(flags); - uartp = info->addr; - info->imr &= ~MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - local_irq_restore(flags); -} - -static void mcfrs_start(struct tty_struct *tty) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_start")) - return; - - local_irq_save(flags); - if (info->xmit_cnt && info->xmit_buf) { - uartp = info->addr; - info->imr |= MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - } - local_irq_restore(flags); -} - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * mcfrs_interrupt(). They were separated out for readability's sake. - * - * Note: mcfrs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * mcfrs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ - -static inline void receive_chars(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - struct tty_struct *tty = info->port.tty; - unsigned char status, ch, flag; - - if (!tty) - return; - - uartp = info->addr; - - while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) { - ch = uartp[MCFUART_URB]; - info->stats.rx++; - -#ifdef CONFIG_MAGIC_SYSRQ - if (mcfrs_console_inited && (info->line == mcfrs_console_port)) { - if (magic_sysrq_key(ch)) - continue; - } -#endif - - flag = TTY_NORMAL; - if (status & MCFUART_USR_RXERR) { - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETERR; - if (status & MCFUART_USR_RXBREAK) { - info->stats.rxbreak++; - flag = TTY_BREAK; - } else if (status & MCFUART_USR_RXPARITY) { - info->stats.rxparity++; - flag = TTY_PARITY; - } else if (status & MCFUART_USR_RXOVERRUN) { - info->stats.rxoverrun++; - flag = TTY_OVERRUN; - } else if (status & MCFUART_USR_RXFRAMING) { - info->stats.rxframing++; - flag = TTY_FRAME; - } - } - tty_insert_flip_char(tty, ch, flag); - } - tty_schedule_flip(tty); - return; -} - -static inline void transmit_chars(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - - uartp = info->addr; - - if (info->x_char) { - /* Send special char - probably flow control */ - uartp[MCFUART_UTB] = info->x_char; - info->x_char = 0; - info->stats.tx++; - } - - if ((info->xmit_cnt <= 0) || info->port.tty->stopped) { - info->imr &= ~MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - return; - } - - while (uartp[MCFUART_USR] & MCFUART_USR_TXREADY) { - uartp[MCFUART_UTB] = info->xmit_buf[info->xmit_tail++]; - info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - info->stats.tx++; - if (--info->xmit_cnt <= 0) - break; - } - - if (info->xmit_cnt < WAKEUP_CHARS) - schedule_work(&info->tqueue); - return; -} - -/* - * This is the serial driver's generic interrupt routine - */ -irqreturn_t mcfrs_interrupt(int irq, void *dev_id) -{ - struct mcf_serial *info; - unsigned char isr; - - info = &mcfrs_table[(irq - IRQBASE)]; - isr = info->addr[MCFUART_UISR] & info->imr; - - if (isr & MCFUART_UIR_RXREADY) - receive_chars(info); - if (isr & MCFUART_UIR_TXREADY) - transmit_chars(info); - return IRQ_HANDLED; -} - -/* - * ------------------------------------------------------------------- - * Here ends the serial interrupt routines. - * ------------------------------------------------------------------- - */ - -static void mcfrs_offintr(struct work_struct *work) -{ - struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue); - struct tty_struct *tty = info->port.tty; - - if (tty) - tty_wakeup(tty); -} - - -/* - * Change of state on a DCD line. - */ -void mcfrs_modem_change(struct mcf_serial *info, int dcd) -{ - if (info->count == 0) - return; - - if (info->flags & ASYNC_CHECK_CD) { - if (dcd) - wake_up_interruptible(&info->open_wait); - else - schedule_work(&info->tqueue_hangup); - } -} - - -#ifdef MCFPP_DCD0 - -unsigned short mcfrs_ppstatus; - -/* - * This subroutine is called when the RS_TIMER goes off. It is used - * to monitor the state of the DCD lines - since they have no edge - * sensors and interrupt generators. - */ -static void mcfrs_timer(void) -{ - unsigned int ppstatus, dcdval, i; - - ppstatus = mcf_getppdata() & (MCFPP_DCD0 | MCFPP_DCD1); - - if (ppstatus != mcfrs_ppstatus) { - for (i = 0; (i < 2); i++) { - dcdval = (i ? MCFPP_DCD1 : MCFPP_DCD0); - if ((ppstatus & dcdval) != (mcfrs_ppstatus & dcdval)) { - mcfrs_modem_change(&mcfrs_table[i], - ((ppstatus & dcdval) ? 0 : 1)); - } - } - } - mcfrs_ppstatus = ppstatus; - - /* Re-arm timer */ - mcfrs_timer_struct.expires = jiffies + HZ/25; - add_timer(&mcfrs_timer_struct); -} - -#endif /* MCFPP_DCD0 */ - - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> mcfrs_hangup() - * - */ -static void do_serial_hangup(struct work_struct *work) -{ - struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue_hangup); - struct tty_struct *tty = info->port.tty; - - if (tty) - tty_hangup(tty); -} - -static int startup(struct mcf_serial * info) -{ - volatile unsigned char *uartp; - unsigned long flags; - - if (info->flags & ASYNC_INITIALIZED) - return 0; - - if (!info->xmit_buf) { - info->xmit_buf = (unsigned char *) __get_free_page(GFP_KERNEL); - if (!info->xmit_buf) - return -ENOMEM; - } - - local_irq_save(flags); - -#ifdef SERIAL_DEBUG_OPEN - printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq); -#endif - - /* - * Reset UART, get it into known state... - */ - uartp = info->addr; - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - mcfrs_setsignals(info, 1, 1); - - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - /* - * and set the speed of the serial port - */ - mcfrs_change_speed(info); - - /* - * Lastly enable the UART transmitter and receiver, and - * interrupt enables. - */ - info->imr = MCFUART_UIR_RXREADY; - uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; - uartp[MCFUART_UIMR] = info->imr; - - info->flags |= ASYNC_INITIALIZED; - local_irq_restore(flags); - return 0; -} - -/* - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - */ -static void shutdown(struct mcf_serial * info) -{ - volatile unsigned char *uartp; - unsigned long flags; - - if (!(info->flags & ASYNC_INITIALIZED)) - return; - -#ifdef SERIAL_DEBUG_OPEN - printk("Shutting down serial port %d (irq %d)....\n", info->line, - info->irq); -#endif - - local_irq_save(flags); - - uartp = info->addr; - uartp[MCFUART_UIMR] = 0; /* mask all interrupts */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) - mcfrs_setsignals(info, 0, 0); - - if (info->xmit_buf) { - free_page((unsigned long) info->xmit_buf); - info->xmit_buf = 0; - } - - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); - - info->flags &= ~ASYNC_INITIALIZED; - local_irq_restore(flags); -} - - -/* - * This routine is called to set the UART divisor registers to match - * the specified baud rate for a serial port. - */ -static void mcfrs_change_speed(struct mcf_serial *info) -{ - volatile unsigned char *uartp; - unsigned int baudclk, cflag; - unsigned long flags; - unsigned char mr1, mr2; - int i; -#ifdef CONFIG_M5272 - unsigned int fraction; -#endif - - if (!info->port.tty || !info->port.tty->termios) - return; - cflag = info->port.tty->termios->c_cflag; - if (info->addr == 0) - return; - -#if 0 - printk("%s(%d): mcfrs_change_speed()\n", __FILE__, __LINE__); -#endif - - i = cflag & CBAUD; - if (i & CBAUDEX) { - i &= ~CBAUDEX; - if (i < 1 || i > 4) - info->port.tty->termios->c_cflag &= ~CBAUDEX; - else - i += 15; - } - if (i == 0) { - mcfrs_setsignals(info, 0, -1); - return; - } - - /* compute the baudrate clock */ -#ifdef CONFIG_M5272 - /* - * For the MCF5272, also compute the baudrate fraction. - */ - baudclk = (MCF_BUSCLK / mcfrs_baud_table[i]) / 32; - fraction = MCF_BUSCLK - (baudclk * 32 * mcfrs_baud_table[i]); - fraction *= 16; - fraction /= (32 * mcfrs_baud_table[i]); -#else - baudclk = ((MCF_BUSCLK / mcfrs_baud_table[i]) + 16) / 32; -#endif - - info->baud = mcfrs_baud_table[i]; - - mr1 = MCFUART_MR1_RXIRQRDY | MCFUART_MR1_RXERRCHAR; - mr2 = 0; - - switch (cflag & CSIZE) { - case CS5: mr1 |= MCFUART_MR1_CS5; break; - case CS6: mr1 |= MCFUART_MR1_CS6; break; - case CS7: mr1 |= MCFUART_MR1_CS7; break; - case CS8: - default: mr1 |= MCFUART_MR1_CS8; break; - } - - if (cflag & PARENB) { - if (cflag & CMSPAR) { - if (cflag & PARODD) - mr1 |= MCFUART_MR1_PARITYMARK; - else - mr1 |= MCFUART_MR1_PARITYSPACE; - } else { - if (cflag & PARODD) - mr1 |= MCFUART_MR1_PARITYODD; - else - mr1 |= MCFUART_MR1_PARITYEVEN; - } - } else { - mr1 |= MCFUART_MR1_PARITYNONE; - } - - if (cflag & CSTOPB) - mr2 |= MCFUART_MR2_STOP2; - else - mr2 |= MCFUART_MR2_STOP1; - - if (cflag & CRTSCTS) { - mr1 |= MCFUART_MR1_RXRTS; - mr2 |= MCFUART_MR2_TXCTS; - } - - if (cflag & CLOCAL) - info->flags &= ~ASYNC_CHECK_CD; - else - info->flags |= ASYNC_CHECK_CD; - - uartp = info->addr; - - local_irq_save(flags); -#if 0 - printk("%s(%d): mr1=%x mr2=%x baudclk=%x\n", __FILE__, __LINE__, - mr1, mr2, baudclk); -#endif - /* - Note: pg 12-16 of MCF5206e User's Manual states that a - software reset should be performed prior to changing - UMR1,2, UCSR, UACR, bit 7 - */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */ - uartp[MCFUART_UMR] = mr1; - uartp[MCFUART_UMR] = mr2; - uartp[MCFUART_UBG1] = (baudclk & 0xff00) >> 8; /* set msb byte */ - uartp[MCFUART_UBG2] = (baudclk & 0xff); /* set lsb byte */ -#ifdef CONFIG_M5272 - uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */ -#endif - uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER; - uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; - mcfrs_setsignals(info, 1, -1); - local_irq_restore(flags); - return; -} - -static void mcfrs_flush_chars(struct tty_struct *tty) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_flush_chars")) - return; - - uartp = (volatile unsigned char *) info->addr; - - /* - * re-enable receiver interrupt - */ - local_irq_save(flags); - if ((!(info->imr & MCFUART_UIR_RXREADY)) && - (info->flags & ASYNC_INITIALIZED) ) { - info->imr |= MCFUART_UIR_RXREADY; - uartp[MCFUART_UIMR] = info->imr; - } - local_irq_restore(flags); - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) - return; - - /* Enable transmitter */ - local_irq_save(flags); - info->imr |= MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - local_irq_restore(flags); -} - -static int mcfrs_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - int c, total = 0; - -#if 0 - printk("%s(%d): mcfrs_write(tty=%x,buf=%x,count=%d)\n", - __FILE__, __LINE__, (int)tty, (int)buf, count); -#endif - - if (serial_paranoia_check(info, tty->name, "mcfrs_write")) - return 0; - - if (!tty || !info->xmit_buf) - return 0; - - local_save_flags(flags); - while (1) { - local_irq_disable(); - c = min(count, (int) min(((int)SERIAL_XMIT_SIZE) - info->xmit_cnt - 1, - ((int)SERIAL_XMIT_SIZE) - info->xmit_head)); - local_irq_restore(flags); - - if (c <= 0) - break; - - memcpy(info->xmit_buf + info->xmit_head, buf, c); - - local_irq_disable(); - info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - info->xmit_cnt += c; - local_irq_restore(flags); - - buf += c; - count -= c; - total += c; - } - - local_irq_disable(); - uartp = info->addr; - info->imr |= MCFUART_UIR_TXREADY; - uartp[MCFUART_UIMR] = info->imr; - local_irq_restore(flags); - - return total; -} - -static int mcfrs_write_room(struct tty_struct *tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - int ret; - - if (serial_paranoia_check(info, tty->name, "mcfrs_write_room")) - return 0; - ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - return ret; -} - -static int mcfrs_chars_in_buffer(struct tty_struct *tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "mcfrs_chars_in_buffer")) - return 0; - return info->xmit_cnt; -} - -static void mcfrs_flush_buffer(struct tty_struct *tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (serial_paranoia_check(info, tty->name, "mcfrs_flush_buffer")) - return; - - local_irq_save(flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - local_irq_restore(flags); - - tty_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * mcfrs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void mcfrs_throttle(struct tty_struct * tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "mcfrs_throttle")) - return; - - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); - - /* Turn off RTS line (do this atomic) */ -} - -static void mcfrs_unthrottle(struct tty_struct * tty) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; -#ifdef SERIAL_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - if (serial_paranoia_check(info, tty->name, "mcfrs_unthrottle")) - return; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } - - /* Assert RTS line (do this atomic) */ -} - -/* - * ------------------------------------------------------------ - * mcfrs_ioctl() and friends - * ------------------------------------------------------------ - */ - -static int get_serial_info(struct mcf_serial * info, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = (unsigned int) info->addr; - tmp.irq = info->irq; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - tmp.custom_divisor = info->custom_divisor; - return copy_to_user(retinfo,&tmp,sizeof(*retinfo)) ? -EFAULT : 0; -} - -static int set_serial_info(struct mcf_serial * info, - struct serial_struct * new_info) -{ - struct serial_struct new_serial; - struct mcf_serial old_info; - int retval = 0; - - if (!new_info) - return -EFAULT; - if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) - return -EFAULT; - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) { - if ((new_serial.baud_base != info->baud_base) || - (new_serial.type != info->type) || - (new_serial.close_delay != info->close_delay) || - ((new_serial.flags & ~ASYNC_USR_MASK) != - (info->flags & ~ASYNC_USR_MASK))) - return -EPERM; - info->flags = ((info->flags & ~ASYNC_USR_MASK) | - (new_serial.flags & ASYNC_USR_MASK)); - info->custom_divisor = new_serial.custom_divisor; - goto check_and_exit; - } - - if (info->count > 1) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->flags = ((info->flags & ~ASYNC_FLAGS) | - (new_serial.flags & ASYNC_FLAGS)); - info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - -check_and_exit: - retval = startup(info); - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info(struct mcf_serial * info, unsigned int *value) -{ - volatile unsigned char *uartp; - unsigned long flags; - unsigned char status; - - local_irq_save(flags); - uartp = info->addr; - status = (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) ? TIOCSER_TEMT : 0; - local_irq_restore(flags); - - return put_user(status,value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break( struct mcf_serial * info, int duration) -{ - volatile unsigned char *uartp; - unsigned long flags; - - if (!info->addr) - return; - set_current_state(TASK_INTERRUPTIBLE); - uartp = info->addr; - - local_irq_save(flags); - uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTART; - schedule_timeout(duration); - uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTOP; - local_irq_restore(flags); -} - -static int mcfrs_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - return mcfrs_getsignals(info); -} - -static int mcfrs_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - int rts = -1, dtr = -1; - - if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) - return -ENODEV; - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - - if (set & TIOCM_RTS) - rts = 1; - if (set & TIOCM_DTR) - dtr = 1; - if (clear & TIOCM_RTS) - rts = 0; - if (clear & TIOCM_DTR) - dtr = 0; - - mcfrs_setsignals(info, dtr, rts); - - return 0; -} - -static int mcfrs_ioctl(struct tty_struct *tty, struct file * file, - unsigned int cmd, unsigned long arg) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - int retval, error; - - if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) - return -ENODEV; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && - (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, HZ/4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(HZ/10) : HZ/4); - return 0; - case TIOCGSERIAL: - if (access_ok(VERIFY_WRITE, (void *) arg, - sizeof(struct serial_struct))) - return get_serial_info(info, - (struct serial_struct *) arg); - return -EFAULT; - case TIOCSSERIAL: - return set_serial_info(info, - (struct serial_struct *) arg); - case TIOCSERGETLSR: /* Get line status register */ - if (access_ok(VERIFY_WRITE, (void *) arg, - sizeof(unsigned int))) - return get_lsr_info(info, (unsigned int *) arg); - return -EFAULT; - case TIOCSERGSTRUCT: - error = copy_to_user((struct mcf_serial *) arg, - info, sizeof(struct mcf_serial)); - if (error) - return -EFAULT; - return 0; - -#ifdef TIOCSET422 - case TIOCSET422: { - unsigned int val; - get_user(val, (unsigned int *) arg); - mcf_setpa(MCFPP_PA11, (val ? 0 : MCFPP_PA11)); - break; - } - case TIOCGET422: { - unsigned int val; - val = (mcf_getpa() & MCFPP_PA11) ? 0 : 1; - put_user(val, (unsigned int *) arg); - break; - } -#endif - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static void mcfrs_set_termios(struct tty_struct *tty, struct ktermios *old_termios) -{ - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - - mcfrs_change_speed(info); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - mcfrs_setsignals(info, -1, 1); -#if 0 - mcfrs_start(tty); -#endif - } -} - -/* - * ------------------------------------------------------------ - * mcfrs_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * S structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - * ------------------------------------------------------------ - */ -static void mcfrs_close(struct tty_struct *tty, struct file * filp) -{ - volatile unsigned char *uartp; - struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; - unsigned long flags; - - if (!info || serial_paranoia_check(info, tty->name, "mcfrs_close")) - return; - - local_irq_save(flags); - - if (tty_hung_up_p(filp)) { - local_irq_restore(flags); - return; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_close ttyS%d, count = %d\n", info->line, info->count); -#endif - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk("MCFRS: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - if (--info->count < 0) { - printk("MCFRS: bad serial port count for ttyS%d: %d\n", - info->line, info->count); - info->count = 0; - } - if (info->count) { - local_irq_restore(flags); - return; - } - info->flags |= ASYNC_CLOSING; - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - - /* - * At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - info->imr &= ~MCFUART_UIR_RXREADY; - uartp = info->addr; - uartp[MCFUART_UIMR] = info->imr; - -#if 0 - /* FIXME: do we need to keep this enabled for console?? */ - if (mcfrs_console_inited && (mcfrs_console_port == info->line)) { - /* Do not disable the UART */ ; - } else -#endif - shutdown(info); - mcfrs_flush_buffer(tty); - tty_ldisc_flush(tty); - - tty->closing = 0; - info->event = 0; - info->port.tty = NULL; -#if 0 - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - (tty->ldisc.close)(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - (tty->ldisc.open)(tty); - } -#endif - if (info->blocked_open) { - if (info->close_delay) { - msleep_interruptible(jiffies_to_msecs(info->close_delay)); - } - wake_up_interruptible(&info->open_wait); - } - info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&info->close_wait); - local_irq_restore(flags); -} - -/* - * mcfrs_wait_until_sent() --- wait until the transmitter is empty - */ -static void -mcfrs_wait_until_sent(struct tty_struct *tty, int timeout) -{ -#ifdef CONFIG_M5272 -#define MCF5272_FIFO_SIZE 25 /* fifo size + shift reg */ - - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - volatile unsigned char *uartp; - unsigned long orig_jiffies, fifo_time, char_time, fifo_cnt; - - if (serial_paranoia_check(info, tty->name, "mcfrs_wait_until_sent")) - return; - - orig_jiffies = jiffies; - - /* - * Set the check interval to be 1/5 of the approximate time - * to send the entire fifo, and make it at least 1. The check - * interval should also be less than the timeout. - * - * Note: we have to use pretty tight timings here to satisfy - * the NIST-PCTS. - */ - lock_kernel(); - - fifo_time = (MCF5272_FIFO_SIZE * HZ * 10) / info->baud; - char_time = fifo_time / 5; - if (char_time == 0) - char_time = 1; - if (timeout && timeout < char_time) - char_time = timeout; - - /* - * Clamp the timeout period at 2 * the time to empty the - * fifo. Just to be safe, set the minimum at .5 seconds. - */ - fifo_time *= 2; - if (fifo_time < (HZ/2)) - fifo_time = HZ/2; - if (!timeout || timeout > fifo_time) - timeout = fifo_time; - - /* - * Account for the number of bytes in the UART - * transmitter FIFO plus any byte being shifted out. - */ - uartp = (volatile unsigned char *) info->addr; - for (;;) { - fifo_cnt = (uartp[MCFUART_UTF] & MCFUART_UTF_TXB); - if ((uartp[MCFUART_USR] & (MCFUART_USR_TXREADY| - MCFUART_USR_TXEMPTY)) == - MCFUART_USR_TXREADY) - fifo_cnt++; - if (fifo_cnt == 0) - break; - msleep_interruptible(jiffies_to_msecs(char_time)); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - unlock_kernel(); -#else - /* - * For the other coldfire models, assume all data has been sent - */ -#endif -} - -/* - * mcfrs_hangup() --- called by tty_hangup() when a hangup is signaled. - */ -void mcfrs_hangup(struct tty_struct *tty) -{ - struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; - - if (serial_paranoia_check(info, tty->name, "mcfrs_hangup")) - return; - - mcfrs_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~ASYNC_NORMAL_ACTIVE; - info->port.tty = NULL; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * mcfrs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct mcf_serial *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & ASYNC_CLOSING) { - interruptible_sleep_on(&info->close_wait); -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - return -EAGAIN; - else - return -ERESTARTSYS; -#else - return -EAGAIN; -#endif - } - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->count is dropped by one, so that - * mcfrs_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready before block: ttyS%d, count = %d\n", - info->line, info->count); -#endif - info->count--; - info->blocked_open++; - while (1) { - local_irq_disable(); - mcfrs_setsignals(info, 1, 1); - local_irq_enable(); - current->state = TASK_INTERRUPTIBLE; - if (tty_hung_up_p(filp) || - !(info->flags & ASYNC_INITIALIZED)) { -#ifdef SERIAL_DO_RESTART - if (info->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; -#else - retval = -EAGAIN; -#endif - break; - } - if (!(info->flags & ASYNC_CLOSING) && - (do_clocal || (mcfrs_getsignals(info) & TIOCM_CD))) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready blocking: ttyS%d, count = %d\n", - info->line, info->count); -#endif - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; -#ifdef SERIAL_DEBUG_OPEN - printk("block_til_ready after blocking: ttyS%d, count = %d\n", - info->line, info->count); -#endif - if (retval) - return retval; - info->flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port, linking in its structure into - * the IRQ chain. It also performs the serial-specific - * initialization for the tty structure. - */ -int mcfrs_open(struct tty_struct *tty, struct file * filp) -{ - struct mcf_serial *info; - int retval, line; - - line = tty->index; - if ((line < 0) || (line >= NR_PORTS)) - return -ENODEV; - info = mcfrs_table + line; - if (serial_paranoia_check(info, tty->name, "mcfrs_open")) - return -ENODEV; -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_open %s, count = %d\n", tty->name, info->count); -#endif - info->count++; - tty->driver_data = info; - info->port.tty = tty; - - /* - * Start up serial port - */ - retval = startup(info); - if (retval) - return retval; - - retval = block_til_ready(tty, filp, info); - if (retval) { -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_open returning after block_til_ready with %d\n", - retval); -#endif - return retval; - } - -#ifdef SERIAL_DEBUG_OPEN - printk("mcfrs_open %s successful...\n", tty->name); -#endif - return 0; -} - -/* - * Based on the line number set up the internal interrupt stuff. - */ -static void mcfrs_irqinit(struct mcf_serial *info) -{ -#if defined(CONFIG_M5272) - volatile unsigned long *icrp; - volatile unsigned long *portp; - volatile unsigned char *uartp; - - uartp = info->addr; - icrp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_ICR2); - - switch (info->line) { - case 0: - *icrp = 0xe0000000; - break; - case 1: - *icrp = 0x0e000000; - break; - default: - printk("MCFRS: don't know how to handle UART %d interrupt?\n", - info->line); - return; - } - - /* Enable the output lines for the serial ports */ - portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PBCNT); - *portp = (*portp & ~0x000000ff) | 0x00000055; - portp = (volatile unsigned long *) (MCF_MBAR + MCFSIM_PDCNT); - *portp = (*portp & ~0x000003fc) | 0x000002a8; -#elif defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) - volatile unsigned char *icrp, *uartp; - volatile unsigned long *imrp; - - uartp = info->addr; - - icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_ICR0 + MCFINT_UART0 + info->line); - *icrp = 0x30 + info->line; /* level 6, line based priority */ - - imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_IMRL); - *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1); -#if defined(CONFIG_M527x) - { - /* - * External Pin Mask Setting & Enable External Pin for Interface - * mrcbis@aliceposta.it - */ - u16 *serpin_enable_mask; - serpin_enable_mask = (u16 *) (MCF_IPSBAR + MCF_GPIO_PAR_UART); - if (info->line == 0) - *serpin_enable_mask |= UART0_ENABLE_MASK; - else if (info->line == 1) - *serpin_enable_mask |= UART1_ENABLE_MASK; - else if (info->line == 2) - *serpin_enable_mask |= UART2_ENABLE_MASK; - } -#endif -#if defined(CONFIG_M528x) - /* make sure PUAPAR is set for UART0 and UART1 */ - if (info->line < 2) { - volatile unsigned char *portp = (volatile unsigned char *) (MCF_MBAR + MCF5282_GPIO_PUAPAR); - *portp |= (0x03 << (info->line * 2)); - } -#endif -#elif defined(CONFIG_M520x) - volatile unsigned char *icrp, *uartp; - volatile unsigned long *imrp; - - uartp = info->addr; - - icrp = (volatile unsigned char *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_ICR0 + MCFINT_UART0 + info->line); - *icrp = 0x03; - - imrp = (volatile unsigned long *) (MCF_MBAR + MCFICM_INTC0 + - MCFINTC_IMRL); - *imrp &= ~((1 << (info->irq - MCFINT_VECBASE)) | 1); - if (info->line < 2) { - unsigned short *uart_par; - uart_par = (unsigned short *)(MCF_IPSBAR + MCF_GPIO_PAR_UART); - if (info->line == 0) - *uart_par |= MCF_GPIO_PAR_UART_PAR_UTXD0 - | MCF_GPIO_PAR_UART_PAR_URXD0; - else if (info->line == 1) - *uart_par |= MCF_GPIO_PAR_UART_PAR_UTXD1 - | MCF_GPIO_PAR_UART_PAR_URXD1; - } else if (info->line == 2) { - unsigned char *feci2c_par; - feci2c_par = (unsigned char *)(MCF_IPSBAR + MCF_GPIO_PAR_FECI2C); - *feci2c_par &= ~0x0F; - *feci2c_par |= MCF_GPIO_PAR_FECI2C_PAR_SCL_UTXD2 - | MCF_GPIO_PAR_FECI2C_PAR_SDA_URXD2; - } -#elif defined(CONFIG_M532x) - volatile unsigned char *uartp; - uartp = info->addr; - switch (info->line) { - case 0: - MCF_INTC0_ICR26 = 0x3; - MCF_INTC0_CIMR = 26; - /* GPIO initialization */ - MCF_GPIO_PAR_UART |= 0x000F; - break; - case 1: - MCF_INTC0_ICR27 = 0x3; - MCF_INTC0_CIMR = 27; - /* GPIO initialization */ - MCF_GPIO_PAR_UART |= 0x0FF0; - break; - case 2: - MCF_INTC0_ICR28 = 0x3; - MCF_INTC0_CIMR = 28; - /* GPIOs also must be initalized, depends on board */ - break; - } -#else - volatile unsigned char *icrp, *uartp; - - switch (info->line) { - case 0: - icrp = (volatile unsigned char *) (MCF_MBAR + MCFSIM_UART1ICR); - *icrp = /*MCFSIM_ICR_AUTOVEC |*/ MCFSIM_ICR_LEVEL6 | - MCFSIM_ICR_PRI1; - mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_UART1); - break; - case 1: - icrp = (volatile unsigned char *) (MCF_MBAR + MCFSIM_UART2ICR); - *icrp = /*MCFSIM_ICR_AUTOVEC |*/ MCFSIM_ICR_LEVEL6 | - MCFSIM_ICR_PRI2; - mcf_setimr(mcf_getimr() & ~MCFSIM_IMR_UART2); - break; - default: - printk("MCFRS: don't know how to handle UART %d interrupt?\n", - info->line); - return; - } - - uartp = info->addr; - uartp[MCFUART_UIVR] = info->irq; -#endif - - /* Clear mask, so no surprise interrupts. */ - uartp[MCFUART_UIMR] = 0; - - if (request_irq(info->irq, mcfrs_interrupt, IRQF_DISABLED, - "ColdFire UART", NULL)) { - printk("MCFRS: Unable to attach ColdFire UART %d interrupt " - "vector=%d\n", info->line, info->irq); - } - - return; -} - - -char *mcfrs_drivername = "ColdFire internal UART serial driver version 1.00\n"; - - -/* - * Serial stats reporting... - */ -int mcfrs_readproc(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct mcf_serial *info; - char str[20]; - int len, sigs, i; - - len = sprintf(page, mcfrs_drivername); - for (i = 0; (i < NR_PORTS); i++) { - info = &mcfrs_table[i]; - len += sprintf((page + len), "%d: port:%x irq=%d baud:%d ", - i, (unsigned int) info->addr, info->irq, info->baud); - if (info->stats.rx || info->stats.tx) - len += sprintf((page + len), "tx:%d rx:%d ", - info->stats.tx, info->stats.rx); - if (info->stats.rxframing) - len += sprintf((page + len), "fe:%d ", - info->stats.rxframing); - if (info->stats.rxparity) - len += sprintf((page + len), "pe:%d ", - info->stats.rxparity); - if (info->stats.rxbreak) - len += sprintf((page + len), "brk:%d ", - info->stats.rxbreak); - if (info->stats.rxoverrun) - len += sprintf((page + len), "oe:%d ", - info->stats.rxoverrun); - - str[0] = str[1] = 0; - if ((sigs = mcfrs_getsignals(info))) { - if (sigs & TIOCM_RTS) - strcat(str, "|RTS"); - if (sigs & TIOCM_CTS) - strcat(str, "|CTS"); - if (sigs & TIOCM_DTR) - strcat(str, "|DTR"); - if (sigs & TIOCM_CD) - strcat(str, "|CD"); - } - - len += sprintf((page + len), "%s\n", &str[1]); - } - - return(len); -} - - -/* Finally, routines used to initialize the serial driver. */ - -static void show_serial_version(void) -{ - printk(mcfrs_drivername); -} - -static const struct tty_operations mcfrs_ops = { - .open = mcfrs_open, - .close = mcfrs_close, - .write = mcfrs_write, - .flush_chars = mcfrs_flush_chars, - .write_room = mcfrs_write_room, - .chars_in_buffer = mcfrs_chars_in_buffer, - .flush_buffer = mcfrs_flush_buffer, - .ioctl = mcfrs_ioctl, - .throttle = mcfrs_throttle, - .unthrottle = mcfrs_unthrottle, - .set_termios = mcfrs_set_termios, - .stop = mcfrs_stop, - .start = mcfrs_start, - .hangup = mcfrs_hangup, - .read_proc = mcfrs_readproc, - .wait_until_sent = mcfrs_wait_until_sent, - .tiocmget = mcfrs_tiocmget, - .tiocmset = mcfrs_tiocmset, -}; - -/* mcfrs_init inits the driver */ -static int __init -mcfrs_init(void) -{ - struct mcf_serial *info; - unsigned long flags; - int i; - - /* Setup base handler, and timer table. */ -#ifdef MCFPP_DCD0 - init_timer(&mcfrs_timer_struct); - mcfrs_timer_struct.function = mcfrs_timer; - mcfrs_timer_struct.data = 0; - mcfrs_timer_struct.expires = jiffies + HZ/25; - add_timer(&mcfrs_timer_struct); - mcfrs_ppstatus = mcf_getppdata() & (MCFPP_DCD0 | MCFPP_DCD1); -#endif - mcfrs_serial_driver = alloc_tty_driver(NR_PORTS); - if (!mcfrs_serial_driver) - return -ENOMEM; - - show_serial_version(); - - /* Initialize the tty_driver structure */ - mcfrs_serial_driver->owner = THIS_MODULE; - mcfrs_serial_driver->name = "ttyS"; - mcfrs_serial_driver->driver_name = "mcfserial"; - mcfrs_serial_driver->major = TTY_MAJOR; - mcfrs_serial_driver->minor_start = 64; - mcfrs_serial_driver->type = TTY_DRIVER_TYPE_SERIAL; - mcfrs_serial_driver->subtype = SERIAL_TYPE_NORMAL; - mcfrs_serial_driver->init_termios = tty_std_termios; - - mcfrs_serial_driver->init_termios.c_cflag = - mcfrs_console_cbaud | CS8 | CREAD | HUPCL | CLOCAL; - mcfrs_serial_driver->flags = TTY_DRIVER_REAL_RAW; - - tty_set_operations(mcfrs_serial_driver, &mcfrs_ops); - - if (tty_register_driver(mcfrs_serial_driver)) { - printk("MCFRS: Couldn't register serial driver\n"); - put_tty_driver(mcfrs_serial_driver); - return(-EBUSY); - } - - local_irq_save(flags); - - /* - * Configure all the attached serial ports. - */ - for (i = 0, info = mcfrs_table; (i < NR_PORTS); i++, info++) { - info->magic = SERIAL_MAGIC; - info->line = i; - info->port.tty = NULL; - info->custom_divisor = 16; - info->close_delay = 50; - info->closing_wait = 3000; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - INIT_WORK(&info->tqueue, mcfrs_offintr); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup); - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - - info->imr = 0; - mcfrs_setsignals(info, 0, 0); - mcfrs_irqinit(info); - - printk("ttyS%d at 0x%04x (irq = %d)", info->line, - (unsigned int) info->addr, info->irq); - printk(" is a builtin ColdFire UART\n"); - } - - local_irq_restore(flags); - return 0; -} - -module_init(mcfrs_init); - -/****************************************************************************/ -/* Serial Console */ -/****************************************************************************/ - -/* - * Quick and dirty UART initialization, for console output. - */ - -void mcfrs_init_console(void) -{ - volatile unsigned char *uartp; - unsigned int clk; - - /* - * Reset UART, get it into known state... - */ - uartp = (volatile unsigned char *) (MCF_MBAR + - (mcfrs_console_port ? MCFUART_BASE2 : MCFUART_BASE1)); - - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETRX; /* reset RX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETTX; /* reset TX */ - uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETMRPTR; /* reset MR pointer */ - - /* - * Set port for defined baud , 8 data bits, 1 stop bit, no parity. - */ - uartp[MCFUART_UMR] = MCFUART_MR1_PARITYNONE | MCFUART_MR1_CS8; - uartp[MCFUART_UMR] = MCFUART_MR2_STOP1; - -#ifdef CONFIG_M5272 -{ - /* - * For the MCF5272, also compute the baudrate fraction. - */ - int fraction = MCF_BUSCLK - (clk * 32 * mcfrs_console_baud); - fraction *= 16; - fraction /= (32 * mcfrs_console_baud); - uartp[MCFUART_UFPD] = (fraction & 0xf); /* set fraction */ - clk = (MCF_BUSCLK / mcfrs_console_baud) / 32; -} -#else - clk = ((MCF_BUSCLK / mcfrs_console_baud) + 16) / 32; /* set baud */ -#endif - - uartp[MCFUART_UBG1] = (clk & 0xff00) >> 8; /* set msb baud */ - uartp[MCFUART_UBG2] = (clk & 0xff); /* set lsb baud */ - uartp[MCFUART_UCSR] = MCFUART_UCSR_RXCLKTIMER | MCFUART_UCSR_TXCLKTIMER; - uartp[MCFUART_UCR] = MCFUART_UCR_RXENABLE | MCFUART_UCR_TXENABLE; - - mcfrs_console_inited++; - return; -} - - -/* - * Setup for console. Argument comes from the boot command line. - */ - -int mcfrs_console_setup(struct console *cp, char *arg) -{ - int i, n = CONSOLE_BAUD_RATE; - - if (!cp) - return(-1); - - if (!strncmp(cp->name, "ttyS", 4)) - mcfrs_console_port = cp->index; - else if (!strncmp(cp->name, "cua", 3)) - mcfrs_console_port = cp->index; - else - return(-1); - - if (arg) - n = simple_strtoul(arg,NULL,0); - for (i = 0; i < MCFRS_BAUD_TABLE_SIZE; i++) - if (mcfrs_baud_table[i] == n) - break; - if (i < MCFRS_BAUD_TABLE_SIZE) { - mcfrs_console_baud = n; - mcfrs_console_cbaud = 0; - if (i > 15) { - mcfrs_console_cbaud |= CBAUDEX; - i -= 15; - } - mcfrs_console_cbaud |= i; - } - mcfrs_init_console(); /* make sure baud rate changes */ - return(0); -} - - -static struct tty_driver *mcfrs_console_device(struct console *c, int *index) -{ - *index = c->index; - return mcfrs_serial_driver; -} - - -/* - * Output a single character, using UART polled mode. - * This is used for console output. - */ - -int mcfrs_put_char(char ch) -{ - volatile unsigned char *uartp; - unsigned long flags; - int i; - - uartp = (volatile unsigned char *) (MCF_MBAR + - (mcfrs_console_port ? MCFUART_BASE2 : MCFUART_BASE1)); - - local_irq_save(flags); - for (i = 0; (i < 0x10000); i++) { - if (uartp[MCFUART_USR] & MCFUART_USR_TXREADY) - break; - } - if (i < 0x10000) { - uartp[MCFUART_UTB] = ch; - for (i = 0; (i < 0x10000); i++) - if (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) - break; - } - if (i >= 0x10000) - mcfrs_init_console(); /* try and get it back */ - local_irq_restore(flags); - - return 1; -} - - -/* - * rs_console_write is registered for printk output. - */ - -void mcfrs_console_write(struct console *cp, const char *p, unsigned len) -{ - if (!mcfrs_console_inited) - mcfrs_init_console(); - while (len-- > 0) { - if (*p == '\n') - mcfrs_put_char('\r'); - mcfrs_put_char(*p++); - } -} - -/* - * declare our consoles - */ - -struct console mcfrs_console = { - .name = "ttyS", - .write = mcfrs_console_write, - .device = mcfrs_console_device, - .setup = mcfrs_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1, -}; - -static int __init mcfrs_console_init(void) -{ - register_console(&mcfrs_console); - return 0; -} - -console_initcall(mcfrs_console_init); - -/****************************************************************************/ diff --git a/drivers/serial/mcfserial.h b/drivers/serial/mcfserial.h deleted file mode 100644 index 56420e2cb11..00000000000 --- a/drivers/serial/mcfserial.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * mcfserial.c -- serial driver for ColdFire internal UARTS. - * - * Copyright (c) 1999 Greg Ungerer <gerg@snapgear.com> - * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com> - * Copyright (c) 2002 SnapGear Inc., <www.snapgear.com> - * - * Based on code from 68332serial.c which was: - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - * Copyright (C) 1998 TSHG - * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org> - */ -#ifndef _MCF_SERIAL_H -#define _MCF_SERIAL_H - -#include <linux/serial.h> - -#ifdef __KERNEL__ - -/* - * Define a local serial stats structure. - */ - -struct mcf_stats { - unsigned int rx; - unsigned int tx; - unsigned int rxbreak; - unsigned int rxframing; - unsigned int rxparity; - unsigned int rxoverrun; -}; - - -/* - * This is our internal structure for each serial port's state. - * Each serial port has one of these structures associated with it. - */ - -struct mcf_serial { - int magic; - volatile unsigned char *addr; /* UART memory address */ - int irq; - int flags; /* defined in tty.h */ - int type; /* UART type */ - struct tty_struct *tty; - unsigned char imr; /* Software imr register */ - unsigned int baud; - int sigs; - int custom_divisor; - int x_char; /* xon/xoff character */ - int baud_base; - int close_delay; - unsigned short closing_wait; - unsigned short closing_wait2; - unsigned long event; - int line; - int count; /* # of fd on device */ - int blocked_open; /* # of blocked opens */ - unsigned char *xmit_buf; - int xmit_head; - int xmit_tail; - int xmit_cnt; - struct mcf_stats stats; - struct work_struct tqueue; - struct work_struct tqueue_hangup; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - -}; - -#endif /* __KERNEL__ */ - -#endif /* _MCF_SERIAL_H */ diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index f977c98cfa9..6bdf3362e3b 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -2051,7 +2051,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) "transmitter\n", port->dev ? port->dev->bus_id : "", port->dev ? ": " : "", - drv->dev_name, port->line); + drv->dev_name, + drv->tty_driver->name_base + port->line); ops->shutdown(port); } @@ -2154,12 +2155,11 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) switch (port->iotype) { case UPIO_PORT: - snprintf(address, sizeof(address), - "I/O 0x%x", port->iobase); + snprintf(address, sizeof(address), "I/O 0x%lx", port->iobase); break; case UPIO_HUB6: snprintf(address, sizeof(address), - "I/O 0x%x offset 0x%x", port->iobase, port->hub6); + "I/O 0x%lx offset 0x%x", port->iobase, port->hub6); break; case UPIO_MEM: case UPIO_MEM32: @@ -2177,7 +2177,9 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", port->dev ? port->dev->bus_id : "", port->dev ? ": " : "", - drv->dev_name, port->line, address, port->irq, uart_type(port)); + drv->dev_name, + drv->tty_driver->name_base + port->line, + address, port->irq, uart_type(port)); } static void diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index e41766d0803..a94a2ab4b57 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c @@ -616,7 +616,7 @@ static int __devexit hv_remove(struct of_device *dev) return 0; } -static struct of_device_id hv_match[] = { +static const struct of_device_id hv_match[] = { { .name = "console", .compatible = "qcn", diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index 29b4458abf7..0355efe115d 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -1078,7 +1078,7 @@ static int __devexit sab_remove(struct of_device *op) return 0; } -static struct of_device_id sab_match[] = { +static const struct of_device_id sab_match[] = { { .name = "se", }, diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index a378464f929..a4dc79b1d7a 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -1506,7 +1506,7 @@ static int __devexit su_remove(struct of_device *op) return 0; } -static struct of_device_id su_match[] = { +static const struct of_device_id su_match[] = { { .name = "su", }, diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 3cb4c8aee13..45a299f3561 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -1480,7 +1480,7 @@ static int __devexit zs_remove(struct of_device *op) return 0; } -static struct of_device_id zs_match[] = { +static const struct of_device_id zs_match[] = { { .name = "zs", }, diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index db410e92c80..77fa7a08080 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -97,7 +97,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_devuid, "devuid=%u"}, {Opt_devgid, "devgid=%u"}, {Opt_devmode, "devmode=%o"}, diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 5799298364f..b697a13364e 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -210,143 +210,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action) /*-------------------------------------------------------------------------*/ -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct ehci_caps { - /* these fields are specified as 8 and 16 bit registers, - * but some hosts can't perform 8 or 16 bit PCI accesses. - */ - u32 hc_capbase; -#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ -#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ - u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ -#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ -#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ -#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ -#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ -#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ -#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ -#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ -#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ -#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ - u8 portroute [8]; /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); - - -/* Section 2.3 Host Controller Operational Registers */ -struct ehci_regs { - - /* USBCMD: offset 0x00 */ - u32 command; -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK (1<<11) /* enable "park" on async qh */ -#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ -#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -#define CMD_ASE (1<<5) /* async schedule enable */ -#define CMD_PSE (1<<4) /* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ - - /* USBSTS: offset 0x04 */ - u32 status; -#define STS_ASS (1<<15) /* Async Schedule Status */ -#define STS_PSS (1<<14) /* Periodic Schedule Status */ -#define STS_RECL (1<<13) /* Reclamation */ -#define STS_HALT (1<<12) /* Not running (any reason) */ -/* some bits reserved */ - /* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA (1<<5) /* Interrupted on async advance */ -#define STS_FATAL (1<<4) /* such as some PCI access errors */ -#define STS_FLR (1<<3) /* frame list rolled over */ -#define STS_PCD (1<<2) /* port change detect */ -#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -#define STS_INT (1<<0) /* "normal" completion (short, ...) */ - - /* USBINTR: offset 0x08 */ - u32 intr_enable; - - /* FRINDEX: offset 0x0C */ - u32 frame_index; /* current microframe number */ - /* CTRLDSSEGMENT: offset 0x10 */ - u32 segment; /* address bits 63:32 if needed */ - /* PERIODICLISTBASE: offset 0x14 */ - u32 frame_list; /* points to periodic list */ - /* ASYNCLISTADDR: offset 0x18 */ - u32 async_next; /* address of next async queue head */ - - u32 reserved [9]; - - /* CONFIGFLAG: offset 0x40 */ - u32 configured_flag; -#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ - - /* PORTSC: offset 0x44 */ - u32 port_status [0]; /* up to N_PORTS */ -/* 31:23 reserved */ -#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ -#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ -#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ -/* 19:16 for port testing */ -#define PORT_LED_OFF (0<<14) -#define PORT_LED_AMBER (1<<14) -#define PORT_LED_GREEN (2<<14) -#define PORT_LED_MASK (3<<14) -#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ -#define PORT_POWER (1<<12) /* true: has power (see PPC) */ -#define PORT_USB11(x) (((x)&(3<<10))==(1<<10)) /* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ -/* 9 reserved */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_OCC (1<<5) /* over current change */ -#define PORT_OC (1<<4) /* over current active */ -#define PORT_PEC (1<<3) /* port enable change */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -} __attribute__ ((packed)); - -#define USBMODE 0x68 /* USB Device mode */ -#define USBMODE_SDIS (1<<3) /* Stream disable */ -#define USBMODE_BE (1<<2) /* BE/LE endianness select */ -#define USBMODE_CM_HC (3<<0) /* host controller mode */ -#define USBMODE_CM_IDLE (0<<0) /* idle state */ - -/* Appendix C, Debug port ... intended for use with special "debug devices" - * that can help if there's no serial console. (nonstandard enumeration.) - */ -struct ehci_dbg_port { - u32 control; -#define DBGP_OWNER (1<<30) -#define DBGP_ENABLED (1<<28) -#define DBGP_DONE (1<<16) -#define DBGP_INUSE (1<<10) -#define DBGP_ERRCODE(x) (((x)>>7)&0x07) -# define DBGP_ERR_BAD 1 -# define DBGP_ERR_SIGNAL 2 -#define DBGP_ERROR (1<<6) -#define DBGP_GO (1<<5) -#define DBGP_OUT (1<<4) -#define DBGP_LEN(x) (((x)>>0)&0x0f) - u32 pids; -#define DBGP_PID_GET(x) (((x)>>16)&0xff) -#define DBGP_PID_SET(data,tok) (((data)<<8)|(tok)) - u32 data03; - u32 data47; - u32 address; -#define DBGP_EPADDR(dev,ep) (((dev)<<8)|(ep)) -} __attribute__ ((packed)); +#include <linux/usb/ehci_def.h> /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 79ea98c66fa..99fb7dc59c4 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -272,7 +272,7 @@ static void aircable_read(struct work_struct *work) * 64 bytes, to ensure I do not get throttled. * Ask USB mailing list for better aproach. */ - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { schedule_work(&priv->rx_work); @@ -283,12 +283,13 @@ static void aircable_read(struct work_struct *work) count = min(64, serial_buf_data_avail(priv->rx_buf)); if (count <= 0) - return; /* We have finished sending everything. */ + goto out; /* We have finished sending everything. */ tty_prepare_flip_string(tty, &data, count); if (!data) { - err("%s- kzalloc(%d) failed.", __func__, count); - return; + dev_err(&port->dev, "%s- kzalloc(%d) failed.", + __func__, count); + goto out; } serial_buf_get(priv->rx_buf, data, count); @@ -297,7 +298,8 @@ static void aircable_read(struct work_struct *work) if (serial_buf_data_avail(priv->rx_buf)) schedule_work(&priv->rx_work); - +out: + tty_kref_put(tty); return; } /* End of private methods */ @@ -495,7 +497,7 @@ static void aircable_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, urb->transfer_buffer); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { if (urb->actual_length <= 2) { /* This is an incomplete package */ @@ -527,6 +529,7 @@ static void aircable_read_bulk_callback(struct urb *urb) } aircable_read(&priv->rx_work); } + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 2ebe06c3405..1913bc7c5f0 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -322,7 +322,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & BELKIN_SA_LSR_ERR) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* Overrun Error */ if (priv->last_lsr & BELKIN_SA_LSR_OE) { } @@ -335,6 +335,7 @@ static void belkin_sa_read_int_callback(struct urb *urb) /* Break Indicator */ if (priv->last_lsr & BELKIN_SA_LSR_BI) { } + tty_kref_put(tty); } #endif spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index e980766bb84..5b20de130e0 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -117,7 +117,7 @@ static int usb_console_setup(struct console *co, char *options) } port = serial->port[0]; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); info->port = port; @@ -143,7 +143,7 @@ static int usb_console_setup(struct console *co, char *options) } memset(&dummy, 0, sizeof(struct ktermios)); tty->termios = termios; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); } /* only call the device specific open if this @@ -163,7 +163,7 @@ static int usb_console_setup(struct console *co, char *options) tty_termios_encode_baud_rate(termios, baud, baud); serial->type->set_termios(tty, port, &dummy); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); kfree(termios); kfree(tty); } @@ -176,7 +176,7 @@ out: return retval; free_termios: kfree(termios); - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); free_tty: kfree(tty); reset_open_count: diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index b4d72351cb9..94ef36c4764 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -384,7 +384,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - ignoring since device not open\n", __func__); return; @@ -394,6 +394,7 @@ static void cyberjack_read_bulk_callback(struct urb *urb) tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); spin_lock(&priv->lock); diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 22837a3f2f8..f3514a91f91 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1286,7 +1286,7 @@ static void cypress_read_int_callback(struct urb *urb) } spin_unlock_irqrestore(&priv->lock, flags); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1362,7 +1362,7 @@ static void cypress_read_int_callback(struct urb *urb) data[i]); tty_insert_flip_char(tty, data[i], tty_flag); } - tty_flip_buffer_push(port->port.tty); + tty_flip_buffer_push(tty); } spin_lock_irqsave(&priv->lock, flags); @@ -1371,6 +1371,7 @@ static void cypress_read_int_callback(struct urb *urb) spin_unlock_irqrestore(&priv->lock, flags); continue_read: + tty_kref_put(tty); /* Continue trying to always read... unless the port has closed. */ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 240aad1acaa..5756ac6d6c9 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -604,7 +604,9 @@ static void digi_wakeup_write_lock(struct work_struct *work) static void digi_wakeup_write(struct usb_serial_port *port) { - tty_wakeup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_wakeup(tty); + tty_kref_put(tty); } @@ -1668,7 +1670,7 @@ static int digi_read_inb_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode = ((unsigned char *)urb->transfer_buffer)[0]; int len = ((unsigned char *)urb->transfer_buffer)[1]; @@ -1692,6 +1694,7 @@ static int digi_read_inb_callback(struct urb *urb) return -1; } + tty = tty_port_tty_get(&port->port); spin_lock(&priv->dp_port_lock); /* check for throttle; if set, do not resubmit read urb */ @@ -1735,6 +1738,7 @@ static int digi_read_inb_callback(struct urb *urb) } } spin_unlock(&priv->dp_port_lock); + tty_kref_put(tty); if (opcode == DIGI_CMD_RECEIVE_DISABLE) dbg("%s: got RECEIVE_DISABLE", __func__); @@ -1760,6 +1764,7 @@ static int digi_read_oob_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct usb_serial *serial = port->serial; + struct tty_struct *tty; struct digi_port *priv = usb_get_serial_port_data(port); int opcode, line, status, val; int i; @@ -1787,10 +1792,11 @@ static int digi_read_oob_callback(struct urb *urb) if (priv == NULL) return -1; + tty = tty_port_tty_get(&port->port); rts = 0; if (port->port.count) - rts = port->port.tty->termios->c_cflag & CRTSCTS; - + rts = tty->termios->c_cflag & CRTSCTS; + if (opcode == DIGI_CMD_READ_INPUT_SIGNALS) { spin_lock(&priv->dp_port_lock); /* convert from digi flags to termiox flags */ @@ -1798,14 +1804,14 @@ static int digi_read_oob_callback(struct urb *urb) priv->dp_modem_signals |= TIOCM_CTS; /* port must be open to use tty struct */ if (rts) { - port->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; digi_wakeup_write(port); } } else { priv->dp_modem_signals &= ~TIOCM_CTS; /* port must be open to use tty struct */ if (rts) - port->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; } if (val & DIGI_READ_INPUT_SIGNALS_DSR) priv->dp_modem_signals |= TIOCM_DSR; @@ -1830,6 +1836,7 @@ static int digi_read_oob_callback(struct urb *urb) } else if (opcode == DIGI_CMD_IFLUSH_FIFO) { wake_up_interruptible(&priv->dp_flush_wait); } + tty_kref_put(tty); } return 0; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index a6ab5b58d9c..1072e847280 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -33,9 +33,8 @@ * Moved MOD_DEC_USE_COUNT to end of empeg_close(). * * (12/03/2000) gb - * Added port->port.tty->ldisc.set_termios(port->port.tty, NULL) to - * empeg_open(). This notifies the tty driver that the termios have - * changed. + * Added tty->ldisc.set_termios(port, tty, NULL) to empeg_open(). + * This notifies the tty driver that the termios have changed. * * (11/13/2000) gb * Moved tty->low_latency = 1 from empeg_read_bulk_callback() to @@ -354,7 +353,7 @@ static void empeg_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); @@ -362,6 +361,7 @@ static void empeg_read_bulk_callback(struct urb *urb) tty_flip_buffer_push(tty); bytes_in += urb->actual_length; } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb( diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 3dc93b542b3..c2ac129557a 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -860,7 +860,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set, kfree(buf); if (rv < 0) { - err("%s Error from MODEM_CTRL urb: DTR %s, RTS %s", + dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s", __func__, (set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged", @@ -1808,7 +1808,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) if (port->port.count <= 0) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1817,7 +1817,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __func__); - return; + goto out; } if (urb != port->read_urb) @@ -1827,7 +1827,7 @@ static void ftdi_read_bulk_callback(struct urb *urb) /* This will happen at close every time so it is a dbg not an err */ dbg("(this is ok on close) nonzero read bulk status received: %d", status); - return; + goto out; } /* count data bytes, but not status bytes */ @@ -1838,7 +1838,8 @@ static void ftdi_read_bulk_callback(struct urb *urb) spin_unlock_irqrestore(&priv->rx_lock, flags); ftdi_process_read(&priv->rx_work.work); - +out: + tty_kref_put(tty); } /* ftdi_read_bulk_callback */ @@ -1863,7 +1864,7 @@ static void ftdi_process_read(struct work_struct *work) if (port->port.count <= 0) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) { dbg("%s - bad tty pointer - exiting", __func__); return; @@ -1872,13 +1873,13 @@ static void ftdi_process_read(struct work_struct *work) priv = usb_get_serial_port_data(port); if (!priv) { dbg("%s - bad port private data pointer - exiting", __func__); - return; + goto out; } urb = port->read_urb; if (!urb) { dbg("%s - bad read_urb pointer - exiting", __func__); - return; + goto out; } data = urb->transfer_buffer; @@ -2020,7 +2021,7 @@ static void ftdi_process_read(struct work_struct *work) schedule_delayed_work(&priv->rx_work, 1); else dbg("%s - port is closed", __func__); - return; + goto out; } /* urb is completely processed */ @@ -2041,6 +2042,8 @@ static void ftdi_process_read(struct work_struct *work) err("%s - failed resubmitting read urb, error %d", __func__, result); } +out: + tty_kref_put(tty); } /* ftdi_process_read */ @@ -2256,7 +2259,7 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) 0, 0, buf, 1, WDR_TIMEOUT); if (ret < 0) { - err("%s Could not get modem status of device - err: %d", __func__, + dbg("%s Could not get modem status of device - err: %d", __func__, ret); return ret; } @@ -2275,7 +2278,7 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file) 0, priv->interface, buf, 2, WDR_TIMEOUT); if (ret < 0) { - err("%s Could not get modem status of device - err: %d", __func__, + dbg("%s Could not get modem status of device - err: %d", __func__, ret); return ret; } diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index d9538208807..2ad0569bcf1 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -276,7 +276,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf) static void send_to_tty(struct usb_serial_port *port, char *data, unsigned int actual_length) { - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); if (tty && actual_length) { @@ -287,6 +287,7 @@ static void send_to_tty(struct usb_serial_port *port, tty_insert_flip_string(tty, data, actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); } diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index fe84c88ec20..814909f1ee6 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -330,7 +330,7 @@ static void resubmit_read_urb(struct usb_serial_port *port, gfp_t mem_flags) static void flush_and_resubmit_read_urb(struct usb_serial_port *port) { struct urb *urb = port->read_urb; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); int room; /* Push data to tty */ @@ -341,6 +341,7 @@ static void flush_and_resubmit_read_urb(struct usb_serial_port *port) tty_flip_buffer_push(tty); } } + tty_kref_put(tty); resubmit_read_urb(port, GFP_ATOMIC); } diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index bfa508ddb0f..611f97fd62f 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -600,6 +600,7 @@ static void edge_interrupt_callback(struct urb *urb) struct edgeport_serial *edge_serial = urb->context; struct edgeport_port *edge_port; struct usb_serial_port *port; + struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; int length = urb->actual_length; int bytes_avail; @@ -675,9 +676,12 @@ static void edge_interrupt_callback(struct urb *urb) /* tell the tty driver that something has changed */ - if (edge_port->port->port.tty) - tty_wakeup(edge_port->port->port.tty); - + tty = tty_port_tty_get( + &edge_port->port->port); + if (tty) { + tty_wakeup(tty); + tty_kref_put(tty); + } /* Since we have more credit, check if more data can be sent */ send_more_port_data(edge_serial, @@ -778,13 +782,14 @@ static void edge_bulk_out_data_callback(struct urb *urb) __func__, status); } - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); if (tty && edge_port->open) { /* let the tty driver wakeup if it has a special write_wakeup function */ tty_wakeup(tty); } + tty_kref_put(tty); /* Release the Write URB */ edge_port->write_in_progress = false; @@ -826,11 +831,12 @@ static void edge_bulk_out_cmd_callback(struct urb *urb) } /* Get pointer to tty */ - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); /* tell the tty driver that something has changed */ if (tty && edge_port->open) tty_wakeup(tty); + tty_kref_put(tty); /* we have completed the command */ edge_port->commandPending = false; @@ -1932,11 +1938,13 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial, edge_serial->rxPort]; edge_port = usb_get_serial_port_data(port); if (edge_port->open) { - tty = edge_port->port->port.tty; + tty = tty_port_tty_get( + &edge_port->port->port); if (tty) { dbg("%s - Sending %d bytes to TTY for port %d", __func__, rxLen, edge_serial->rxPort); edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen); + tty_kref_put(tty); } edge_port->icount.rx += rxLen; } @@ -1971,6 +1979,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, { struct usb_serial_port *port; struct edgeport_port *edge_port; + struct tty_struct *tty; __u8 code = edge_serial->rxStatusCode; /* switch the port pointer to the one being currently talked about */ @@ -2020,10 +2029,12 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial, /* send the current line settings to the port so we are in sync with any further termios calls */ - /* FIXME: locking on tty */ - if (edge_port->port->port.tty) - change_port_settings(edge_port->port->port.tty, - edge_port, edge_port->port->port.tty->termios); + tty = tty_port_tty_get(&edge_port->port->port); + if (tty) { + change_port_settings(tty, + edge_port, tty->termios); + tty_kref_put(tty); + } /* we have completed the open */ edge_port->openPending = false; @@ -2163,10 +2174,14 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData, } /* Place LSR data byte into Rx buffer */ - if (lsrData && edge_port->port->port.tty) - edge_tty_recv(&edge_port->port->dev, - edge_port->port->port.tty, &data, 1); - + if (lsrData) { + struct tty_struct *tty = + tty_port_tty_get(&edge_port->port->port); + if (tty) { + edge_tty_recv(&edge_port->port->dev, tty, &data, 1); + tty_kref_put(tty); + } + } /* update input line counters */ icount = &edge_port->icount; if (newLsr & LSR_BREAK) diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index cb4c54316cf..541dd8e6e7a 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -572,7 +572,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, int flush) { int baud_rate; - struct tty_struct *tty = port->port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port->port); wait_queue_t wait; unsigned long flags; @@ -599,6 +599,7 @@ static void chase_port(struct edgeport_port *port, unsigned long timeout, if (flush) edge_buf_clear(port->ep_out_buf); spin_unlock_irqrestore(&port->ep_lock, flags); + tty_kref_put(tty); /* wait for data to drain from the device */ timeout += jiffies; @@ -1554,7 +1555,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) /* Save the new modem status */ edge_port->shadow_msr = msr & 0xf0; - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); /* handle CTS flow control */ if (tty && C_CRTSCTS(tty)) { if (msr & EDGEPORT_MSR_CTS) { @@ -1564,6 +1565,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr) tty->hw_stopped = 1; } } + tty_kref_put(tty); return; } @@ -1574,6 +1576,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, struct async_icount *icount; __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK)); + struct tty_struct *tty; dbg("%s - %02x", __func__, new_lsr); @@ -1587,8 +1590,13 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK); /* Place LSR data byte into Rx buffer */ - if (lsr_data && edge_port->port->port.tty) - edge_tty_recv(&edge_port->port->dev, edge_port->port->port.tty, &data, 1); + if (lsr_data) { + tty = tty_port_tty_get(&edge_port->port->port); + if (tty) { + edge_tty_recv(&edge_port->port->dev, tty, &data, 1); + tty_kref_put(tty); + } + } /* update input line counters */ icount = &edge_port->icount; @@ -1749,7 +1757,7 @@ static void edge_bulk_in_callback(struct urb *urb) ++data; } - tty = edge_port->port->port.tty; + tty = tty_port_tty_get(&edge_port->port->port); if (tty && urb->actual_length) { usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data); @@ -1761,6 +1769,7 @@ static void edge_bulk_in_callback(struct urb *urb) urb->actual_length); edge_port->icount.rx += urb->actual_length; } + tty_kref_put(tty); exit: /* continue read unless stopped */ @@ -1796,6 +1805,7 @@ static void edge_bulk_out_callback(struct urb *urb) struct usb_serial_port *port = urb->context; struct edgeport_port *edge_port = usb_get_serial_port_data(port); int status = urb->status; + struct tty_struct *tty; dbg("%s - port %d", __func__, port->number); @@ -1818,7 +1828,9 @@ static void edge_bulk_out_callback(struct urb *urb) } /* send any buffered data */ - edge_send(port->port.tty); + tty = tty_port_tty_get(&port->port); + edge_send(tty); + tty_kref_put(tty); } static int edge_open(struct tty_struct *tty, @@ -1876,7 +1888,7 @@ static int edge_open(struct tty_struct *tty, /* set up the port settings */ if (tty) - edge_set_termios(tty, port, port->port.tty->termios); + edge_set_termios(tty, port, tty->termios); /* open up the port */ diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index cd9a2e138c8..2affa9c118b 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -764,13 +764,14 @@ static void ipaq_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); bytes_in += urb->actual_length; } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index a842025b9b5..480cac27d64 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -170,12 +170,13 @@ static void ipw_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(port->read_urb, port->serial->dev, diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index e59155c6607..45d4043e04a 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -465,11 +465,12 @@ static void ir_read_bulk_callback(struct urb *urb) ir_baud = *data & 0x0f; usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty_buffer_request_room(tty, urb->actual_length - 1)) { tty_insert_flip_string(tty, data+1, urb->actual_length - 1); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* * No break here. diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index ddff37fa633..53710aa7ead 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -629,13 +629,14 @@ static void read_buf_callback(struct urb *urb) } dbg("%s - %i chars to write", __func__, urb->actual_length); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (data == NULL) dbg("%s - data is NULL !!!", __func__); if (tty && urb->actual_length && data) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); iuu_led_activity_on(urb); } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 704716f6f6d..15447af4869 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -430,7 +430,7 @@ static void usa26_indat_callback(struct urb *urb) } port = urb->context; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -459,6 +459,7 @@ static void usa26_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -513,6 +514,7 @@ static void usa26_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -553,12 +555,11 @@ static void usa26_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -604,11 +605,12 @@ static void usa28_indat_callback(struct urb *urb) p_priv = usb_get_serial_port_data(port); data = urb->transfer_buffer; - tty = port->port.tty; - if (urb->actual_length) { + tty =tty_port_tty_get(&port->port); + if (tty && urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -652,6 +654,7 @@ static void usa28_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state; int status = urb->status; @@ -689,12 +692,11 @@ static void usa28_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if( old_dcd_state != p_priv->dcd_state && old_dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -785,12 +787,11 @@ static void usa49_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -827,7 +828,7 @@ static void usa49_indat_callback(struct urb *urb) } port = urb->context; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { /* 0x80 bit is error flag */ if ((data[0] & 0x80) == 0) { @@ -850,6 +851,7 @@ static void usa49_indat_callback(struct urb *urb) } tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ urb->dev = port->serial->dev; @@ -893,7 +895,7 @@ static void usa49wg_indat_callback(struct urb *urb) return; } port = serial->port[data[i++]]; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); len = data[i++]; /* 0x80 bit is error flag */ @@ -927,6 +929,7 @@ static void usa49wg_indat_callback(struct urb *urb) } if (port->port.count) tty_flip_buffer_push(tty); + tty_kref_put(tty); } } @@ -967,8 +970,8 @@ static void usa90_indat_callback(struct urb *urb) port = urb->context; p_priv = usb_get_serial_port_data(port); - tty = port->port.tty; if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); /* if current mode is DMA, looks like usa28 format otherwise looks like usa26 data format */ @@ -1004,6 +1007,7 @@ static void usa90_indat_callback(struct urb *urb) } } tty_flip_buffer_push(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1025,6 +1029,7 @@ static void usa90_instat_callback(struct urb *urb) struct usb_serial *serial; struct usb_serial_port *port; struct keyspan_port_private *p_priv; + struct tty_struct *tty; int old_dcd_state, err; int status = urb->status; @@ -1053,12 +1058,11 @@ static void usa90_instat_callback(struct urb *urb) p_priv->dcd_state = ((msg->dcd) ? 1 : 0); p_priv->ri_state = ((msg->ri) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1130,12 +1134,11 @@ static void usa67_instat_callback(struct urb *urb) p_priv->cts_state = ((msg->hskia_cts) ? 1 : 0); p_priv->dcd_state = ((msg->gpia_dcd) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) - && old_dcd_state != p_priv->dcd_state) { - if (old_dcd_state) - tty_hangup(port->port.tty); - /* else */ - /* wake_up_interruptible(&p_priv->open_wait); */ + if (old_dcd_state != p_priv->dcd_state && old_dcd_state) { + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); } /* Resubmit urb so we continue receiving */ @@ -1332,7 +1335,7 @@ static void keyspan_close(struct tty_struct *tty, stop_urb(p_priv->out_urbs[i]); } } - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); } /* download the firmware to a pre-renumeration device */ diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 040040a267d..99e9a14c5bf 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -172,8 +172,9 @@ static void keyspan_pda_wakeup_write(struct work_struct *work) struct keyspan_pda_private *priv = container_of(work, struct keyspan_pda_private, wakeup_work); struct usb_serial_port *port = priv->port; - - tty_wakeup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_wakeup(tty); + tty_kref_put(tty); } static void keyspan_pda_request_unthrottle(struct work_struct *work) @@ -205,7 +206,7 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work) static void keyspan_pda_rx_interrupt(struct urb *urb) { struct usb_serial_port *port = urb->context; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); unsigned char *data = urb->transfer_buffer; int retval; int status = urb->status; @@ -222,7 +223,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) /* this urb is terminated, clean up */ dbg("%s - urb shutting down with status: %d", __func__, status); - return; + goto out; default: dbg("%s - nonzero urb status received: %d", __func__, status); @@ -261,8 +262,11 @@ static void keyspan_pda_rx_interrupt(struct urb *urb) exit: retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) - err("%s - usb_submit_urb failed with result %d", - __func__, retval); + dev_err(&port->dev, + "%s - usb_submit_urb failed with result %d", + __func__, retval); +out: + tty_kref_put(tty); } diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index b84dddc7112..ff3a07f5102 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -658,7 +658,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb) } else { int bytes_sent = ((__u8 *) data)[0] + ((unsigned int) ((__u8 *) data)[1] << 8); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* we should immediately resubmit the URB, before attempting * to pass the data on to the tty layer. But that needs locking * against re-entry an then mixed-up data because of @@ -679,6 +679,7 @@ static void klsi_105_read_bulk_callback(struct urb *urb) tty_buffer_request_room(tty, bytes_sent); tty_insert_flip_string(tty, data + 2, bytes_sent); tty_flip_buffer_push(tty); + tty_kref_put(tty); /* again lockless, but debug info only */ priv->bytes_in += bytes_sent; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index deba28ec77e..cfcf37c2b95 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -383,7 +383,7 @@ static void kobil_read_int_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { /* BEGIN DEBUG */ @@ -405,6 +405,7 @@ static void kobil_read_int_callback(struct urb *urb) tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 0ded8bd6ec8..9b2cef81cde 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -563,10 +563,11 @@ static void mct_u232_read_int_callback(struct urb *urb) * Work-a-round: handle the 'usual' bulk-in pipe here */ if (urb->transfer_buffer_length > 2) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); + tty_kref_put(tty); } goto exit; } @@ -591,7 +592,7 @@ static void mct_u232_read_int_callback(struct urb *urb) * to look in to this before committing any code. */ if (priv->last_lsr & MCT_U232_LSR_ERR) { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); /* Overrun Error */ if (priv->last_lsr & MCT_U232_LSR_OE) { } @@ -604,6 +605,7 @@ static void mct_u232_read_int_callback(struct urb *urb) /* Break Indicator */ if (priv->last_lsr & MCT_U232_LSR_BI) { } + tty_kref_put(tty); } #endif spin_unlock_irqrestore(&priv->lock, flags); diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 7c4917d77c0..7b538caec37 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -216,12 +216,13 @@ static void mos7720_bulk_in_callback(struct urb *urb) data = urb->transfer_buffer; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); if (!port->read_urb) { dbg("URB KILLED !!!"); @@ -262,10 +263,11 @@ static void mos7720_bulk_out_data_callback(struct urb *urb) dbg("Entering ........."); - tty = mos7720_port->port->port.tty; + tty = tty_port_tty_get(&mos7720_port->port->port); if (tty && mos7720_port->open) tty_wakeup(tty); + tty_kref_put(tty); } /* @@ -1267,29 +1269,6 @@ static int get_lsr_info(struct tty_struct *tty, return 0; } -/* - * get_number_bytes_avail - get number of bytes available - * - * Purpose: Let user call ioctl to get the count of number of bytes available. - */ -static int get_number_bytes_avail(struct moschip_port *mos7720_port, - unsigned int __user *value) -{ - unsigned int result = 0; - struct tty_struct *tty = mos7720_port->port->port.tty; - - if (!tty) - return -ENOIOCTLCMD; - - result = tty->read_cnt; - - dbg("%s(%d) = %d", __func__, mos7720_port->port->number, result); - if (copy_to_user(value, &result, sizeof(int))) - return -EFAULT; - - return -ENOIOCTLCMD; -} - static int set_modem_info(struct moschip_port *mos7720_port, unsigned int cmd, unsigned int __user *value) { @@ -1409,13 +1388,6 @@ static int mos7720_ioctl(struct tty_struct *tty, struct file *file, dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd); switch (cmd) { - case TIOCINQ: - /* return number of bytes available */ - dbg("%s (%d) TIOCINQ", __func__, port->number); - return get_number_bytes_avail(mos7720_port, - (unsigned int __user *)arg); - break; - case TIOCSERGETLSR: dbg("%s (%d) TIOCSERGETLSR", __func__, port->number); return get_lsr_info(tty, mos7720_port, diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 09d82062b97..60543d79ef5 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -709,12 +709,13 @@ static void mos7840_bulk_in_callback(struct urb *urb) dbg("%s", "Entering ........... \n"); if (urb->actual_length) { - tty = mos7840_port->port->port.tty; + tty = tty_port_tty_get(&mos7840_port->port->port); if (tty) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); dbg(" %s \n", data); tty_flip_buffer_push(tty); + tty_kref_put(tty); } mos7840_port->icount.rx += urb->actual_length; smp_wmb(); @@ -773,10 +774,10 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) dbg("%s \n", "Entering ........."); - tty = mos7840_port->port->port.tty; - + tty = tty_port_tty_get(&mos7840_port->port->port); if (tty && mos7840_port->open) tty_wakeup(tty); + tty_kref_put(tty); } diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index d6736531a0f..bcdcbb82270 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -64,12 +64,13 @@ static void navman_read_int_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); exit: result = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index ae8e227f3db..c4d70b0f1e4 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -172,7 +172,7 @@ static int omninet_open(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); wport = serial->port[1]; - wport->port.tty = tty; /* FIXME */ + tty_port_tty_set(&wport->port, tty); /* Start reading from the device */ usb_fill_bulk_urb(port->read_urb, serial->dev, @@ -229,9 +229,11 @@ static void omninet_read_bulk_callback(struct urb *urb) } if (urb->actual_length && header->oh_len) { - tty_insert_flip_string(port->port.tty, - data + OMNINET_DATAOFFSET, header->oh_len); - tty_flip_buffer_push(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET, + header->oh_len); + tty_flip_buffer_push(tty); + tty_kref_put(tty); } /* Continue trying to always read */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 73f8277f88f..6b1727e751e 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -571,14 +571,14 @@ static void option_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); - } else { + } else dbg("%s: empty read urb received", __func__); - } + tty_kref_put(tty); /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN) { @@ -647,9 +647,13 @@ static void option_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) && - old_dcd_state && !portdata->dcd_state) - tty_hangup(port->port.tty); + if (old_dcd_state && !portdata->dcd_state) { + struct tty_struct *tty = + tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty)) + tty_hangup(tty); + tty_kref_put(tty); + } } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -793,7 +797,7 @@ static void option_close(struct tty_struct *tty, for (i = 0; i < N_OUT_URB; i++) usb_kill_urb(portdata->out_urbs[i]); } - port->port.tty = NULL; /* FIXME */ + tty_port_tty_set(&port->port, NULL); } /* Helper functions used by option_setup_urbs */ diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index 81db5715ee2..ba551f00f16 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -224,10 +224,6 @@ struct oti6858_private { struct usb_serial_port *port; /* USB port with which associated */ }; -#undef dbg -/* #define dbg(format, arg...) printk(KERN_INFO "%s: " format "\n", __FILE__, ## arg) */ -#define dbg(format, arg...) printk(KERN_INFO "" format "\n", ## arg) - static void setup_line(struct work_struct *work) { struct oti6858_private *priv = container_of(work, @@ -1002,11 +998,12 @@ static void oti6858_read_bulk_callback(struct urb *urb) return; } - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty != NULL && urb->actual_length > 0) { tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* schedule the interrupt urb if we are still open */ if (port->port.count != 0) { diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 1ede1441cb1..90843784716 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -154,7 +154,6 @@ struct pl2303_private { wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; - u8 termios_initialized; enum pl2303_type type; }; @@ -526,16 +525,6 @@ static void pl2303_set_termios(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); - /* The PL2303 is reported to lose bytes if you change serial settings even to the same values as before. Thus we actually need to filter in this specific case */ @@ -1057,7 +1046,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dbg("%s - tty_flag = %d", __func__, tty_flag); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -1067,7 +1056,7 @@ static void pl2303_read_bulk_callback(struct urb *urb) tty_insert_flip_char(tty, data[i], tty_flag); tty_flip_buffer_push(tty); } - + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { urb->dev = port->serial->dev; diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index def52d07a4e..72903ac9f5c 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -217,6 +217,7 @@ static void safe_read_bulk_callback(struct urb *urb) struct usb_serial_port *port = urb->context; unsigned char *data = urb->transfer_buffer; unsigned char length = urb->actual_length; + struct tty_struct *tty; int result; int status = urb->status; @@ -242,6 +243,7 @@ static void safe_read_bulk_callback(struct urb *urb) printk("\n"); } #endif + tty = tty_port_tty_get(&port->port); if (safe) { __u16 fcs; fcs = fcs_compute10(data, length, CRC10_INITFCS); @@ -250,9 +252,9 @@ static void safe_read_bulk_callback(struct urb *urb) if (actual_length <= (length - 2)) { info("%s - actual: %d", __func__, actual_length); - tty_insert_flip_string(port->port.tty, + tty_insert_flip_string(tty, data, actual_length); - tty_flip_buffer_push(port->port.tty); + tty_flip_buffer_push(tty); } else { err("%s - inconsistent lengths %d:%d", __func__, actual_length, length); @@ -261,9 +263,10 @@ static void safe_read_bulk_callback(struct urb *urb) err("%s - bad CRC %x", __func__, fcs); } } else { - tty_insert_flip_string(port->port.tty, data, length); - tty_flip_buffer_push(port->port.tty); + tty_insert_flip_string(tty, data, length); + tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Continue trying to always read */ usb_fill_bulk_urb(urb, port->serial->dev, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ea1a103c99b..8b9eaf38367 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -440,14 +440,14 @@ static void sierra_indat_callback(struct urb *urb) dbg("%s: nonzero status: %d on endpoint %02x.", __func__, status, endpoint); } else { - tty = port->port.tty; if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); tty_buffer_request_room(tty, urb->actual_length); tty_insert_flip_string(tty, data, urb->actual_length); tty_flip_buffer_push(tty); - } else { + tty_kref_put(tty); + } else dbg("%s: empty read urb received", __func__); - } /* Resubmit urb so we continue receiving */ if (port->port.count && status != -ESHUTDOWN) { @@ -485,6 +485,7 @@ static void sierra_instat_callback(struct urb *urb) unsigned char signals = *((unsigned char *) urb->transfer_buffer + sizeof(struct usb_ctrlrequest)); + struct tty_struct *tty; dbg("%s: signal x%x", __func__, signals); @@ -494,9 +495,11 @@ static void sierra_instat_callback(struct urb *urb) portdata->dsr_state = ((signals & 0x02) ? 1 : 0); portdata->ri_state = ((signals & 0x08) ? 1 : 0); - if (port->port.tty && !C_CLOCAL(port->port.tty) && + tty = tty_port_tty_get(&port->port); + if (tty && !C_CLOCAL(tty) && old_dcd_state && !portdata->dcd_state) - tty_hangup(port->port.tty); + tty_hangup(tty); + tty_kref_put(tty); } else { dbg("%s: type %x req %x", __func__, req_pkt->bRequestType, req_pkt->bRequest); @@ -616,8 +619,7 @@ static void sierra_close(struct tty_struct *tty, } usb_kill_urb(port->interrupt_in_urb); - - port->port.tty = NULL; /* FIXME */ + tty_port_tty_set(&port->port, NULL); } static int sierra_startup(struct usb_serial *serial) diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 283cf6b36b2..1533d6e1223 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -755,7 +755,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_flag = TTY_FRAME; dev_dbg(&port->dev, "tty_flag = %d\n", tty_flag); - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length + 1); /* overrun is special, not associated with a char */ @@ -765,6 +765,7 @@ static void spcp8x5_read_bulk_callback(struct urb *urb) tty_insert_flip_char(tty, data[i], tty_flag); tty_flip_buffer_push(tty); } + tty_kref_put(tty); /* Schedule the next read _if_ we are still open */ if (port->port.count) { diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 9a3e495c769..c90237d48b0 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -179,7 +179,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr); static int ti_get_lsr(struct ti_port *tport); static int ti_get_serial_info(struct ti_port *tport, struct serial_struct __user *ret_arg); -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg); static void ti_handle_new_msr(struct ti_port *tport, __u8 msr); @@ -857,8 +857,8 @@ static int ti_ioctl(struct tty_struct *tty, struct file *file, (struct serial_struct __user *)arg); case TIOCSSERIAL: dbg("%s - (%d) TIOCSSERIAL", __func__, port->number); - return ti_set_serial_info(tport, - (struct serial_struct __user *)arg); + return ti_set_serial_info(tty, tport, + (struct serial_struct __user *)arg); case TIOCMIWAIT: dbg("%s - (%d) TIOCMIWAIT", __func__, port->number); cprev = tport->tp_icount; @@ -1211,6 +1211,7 @@ static void ti_bulk_in_callback(struct urb *urb) struct device *dev = &urb->dev->dev; int status = urb->status; int retval = 0; + struct tty_struct *tty; dbg("%s", __func__); @@ -1239,20 +1240,22 @@ static void ti_bulk_in_callback(struct urb *urb) return; } - if (port->port.tty && urb->actual_length) { + tty = tty_port_tty_get(&port->port); + if (tty && urb->actual_length) { usb_serial_debug_data(debug, dev, __func__, urb->actual_length, urb->transfer_buffer); if (!tport->tp_is_open) dbg("%s - port closed, dropping data", __func__); else - ti_recv(&urb->dev->dev, port->port.tty, + ti_recv(&urb->dev->dev, tty, urb->transfer_buffer, urb->actual_length); spin_lock(&tport->tp_lock); tport->tp_icount.rx += urb->actual_length; spin_unlock(&tport->tp_lock); + tty_kref_put(tty); } exit: @@ -1330,7 +1333,7 @@ static void ti_send(struct ti_port *tport) { int count, result; struct usb_serial_port *port = tport->tp_port; - struct tty_struct *tty = port->port.tty; /* FIXME */ + struct tty_struct *tty = tty_port_tty_get(&port->port); /* FIXME */ unsigned long flags; @@ -1338,19 +1341,15 @@ static void ti_send(struct ti_port *tport) spin_lock_irqsave(&tport->tp_lock, flags); - if (tport->tp_write_urb_in_use) { - spin_unlock_irqrestore(&tport->tp_lock, flags); - return; - } + if (tport->tp_write_urb_in_use) + goto unlock; count = ti_buf_get(tport->tp_write_buf, port->write_urb->transfer_buffer, port->bulk_out_size); - if (count == 0) { - spin_unlock_irqrestore(&tport->tp_lock, flags); - return; - } + if (count == 0) + goto unlock; tport->tp_write_urb_in_use = 1; @@ -1380,7 +1379,13 @@ static void ti_send(struct ti_port *tport) /* more room in the buffer for new writes, wakeup */ if (tty) tty_wakeup(tty); + tty_kref_put(tty); wake_up_interruptible(&tport->tp_write_wait); + return; +unlock: + spin_unlock_irqrestore(&tport->tp_lock, flags); + tty_kref_put(tty); + return; } @@ -1464,20 +1469,16 @@ static int ti_get_serial_info(struct ti_port *tport, } -static int ti_set_serial_info(struct ti_port *tport, +static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport, struct serial_struct __user *new_arg) { - struct usb_serial_port *port = tport->tp_port; struct serial_struct new_serial; if (copy_from_user(&new_serial, new_arg, sizeof(new_serial))) return -EFAULT; tport->tp_flags = new_serial.flags & TI_SET_SERIAL_FLAGS; - /* FIXME */ - if (port->port.tty) - port->port.tty->low_latency = - (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; + tty->low_latency = (tport->tp_flags & ASYNC_LOW_LATENCY) ? 1 : 0; tport->tp_closing_wait = new_serial.closing_wait; return 0; @@ -1510,7 +1511,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tport->tp_msr = msr & TI_MSR_MASK; /* handle CTS flow control */ - tty = tport->tp_port->port.tty; + tty = tty_port_tty_get(&tport->tp_port->port); if (tty && C_CRTSCTS(tty)) { if (msr & TI_MSR_CTS) { tty->hw_stopped = 0; @@ -1519,6 +1520,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr) tty->hw_stopped = 1; } } + tty_kref_put(tty); } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 4f7f9e3ae0a..e7d4246027b 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -214,7 +214,7 @@ static int serial_open (struct tty_struct *tty, struct file *filp) /* set up our port structure making the tty driver * remember our port object, and us it */ tty->driver_data = port; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); if (port->port.count == 1) { @@ -246,7 +246,7 @@ bailout_module_put: bailout_mutex_unlock: port->port.count = 0; tty->driver_data = NULL; - port->port.tty = NULL; + tty_port_tty_set(&port->port, NULL); mutex_unlock(&port->mutex); bailout_kref_put: usb_serial_put(serial); @@ -276,10 +276,11 @@ static void serial_close(struct tty_struct *tty, struct file *filp) port->serial->type->close(tty, port, filp); if (port->port.count == (port->console? 1 : 0)) { - if (port->port.tty) { - if (port->port.tty->driver_data) - port->port.tty->driver_data = NULL; - port->port.tty = NULL; + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty) { + if (tty->driver_data) + tty->driver_data = NULL; + tty_port_tty_set(&port->port, NULL); } } @@ -508,11 +509,12 @@ static void usb_serial_port_work(struct work_struct *work) if (!port) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (!tty) return; tty_wakeup(tty); + tty_kref_put(tty); } static void port_release(struct device *dev) @@ -819,6 +821,7 @@ int usb_serial_probe(struct usb_interface *interface, port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) goto probe_error; + tty_port_init(&port->port); port->serial = serial; spin_lock_init(&port->lock); mutex_init(&port->mutex); @@ -1040,8 +1043,11 @@ void usb_serial_disconnect(struct usb_interface *interface) for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; if (port) { - if (port->port.tty) - tty_hangup(port->port.tty); + struct tty_struct *tty = tty_port_tty_get(&port->port); + if (tty) { + tty_hangup(tty); + tty_kref_put(tty); + } kill_traffic(port); } } diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index cf8924f9a2c..a6d1c75a1c8 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -499,7 +499,7 @@ static void visor_read_bulk_callback(struct urb *urb) int status = urb->status; struct tty_struct *tty; int result; - int available_room; + int available_room = 0; dbg("%s - port %d", __func__, port->number); @@ -512,13 +512,17 @@ static void visor_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - tty = port->port.tty; - if (tty && urb->actual_length) { - available_room = tty_buffer_request_room(tty, + if (urb->actual_length) { + tty = tty_port_tty_get(&port->port); + if (tty) { + available_room = tty_buffer_request_room(tty, urb->actual_length); - if (available_room) { - tty_insert_flip_string(tty, data, available_room); - tty_flip_buffer_push(tty); + if (available_room) { + tty_insert_flip_string(tty, data, + available_room); + tty_flip_buffer_push(tty); + } + tty_kref_put(tty); } spin_lock(&priv->lock); priv->bytes_in += available_room; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 3a9d14384a4..11c8b97a517 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1481,7 +1481,7 @@ static void rx_data_softint(struct work_struct *work) struct whiteheat_private *info = container_of(work, struct whiteheat_private, rx_work); struct usb_serial_port *port = info->port; - struct tty_struct *tty = port->port.tty; + struct tty_struct *tty = tty_port_tty_get(&port->port); struct whiteheat_urb_wrap *wrap; struct urb *urb; unsigned long flags; @@ -1493,7 +1493,7 @@ static void rx_data_softint(struct work_struct *work) spin_lock_irqsave(&info->lock, flags); if (info->flags & THROTTLED) { spin_unlock_irqrestore(&info->lock, flags); - return; + goto out; } list_for_each_safe(tmp, tmp2, &info->rx_urb_q) { @@ -1513,7 +1513,7 @@ static void rx_data_softint(struct work_struct *work) spin_unlock_irqrestore(&info->lock, flags); tty_flip_buffer_push(tty); schedule_work(&info->rx_work); - return; + goto out; } tty_insert_flip_string(tty, urb->transfer_buffer, len); sent += len; @@ -1536,6 +1536,8 @@ static void rx_data_softint(struct work_struct *work) if (sent) tty_flip_buffer_push(tty); +out: + tty_kref_put(tty); } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index d85a74c64b5..f79c2040758 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -673,7 +673,6 @@ config FB_VESA select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT - select VIDEO_SELECT help This is the frame buffer device driver for generic VESA 2.0 compliant graphic cards. The older VESA 1.2 cards are not supported. @@ -1578,7 +1577,6 @@ config FB_CYBLA tristate "Cyberblade/i1 support" depends on FB && PCI && X86_32 && !64BIT select FB_CFB_IMAGEBLIT - select VIDEO_SELECT ---help--- This driver is supposed to support the Trident Cyberblade/i1 graphics core integrated in the VIA VT8601A North Bridge, diff --git a/drivers/video/backlight/mbp_nvidia_bl.c b/drivers/video/backlight/mbp_nvidia_bl.c index 385cba40ea8..06964af761c 100644 --- a/drivers/video/backlight/mbp_nvidia_bl.c +++ b/drivers/video/backlight/mbp_nvidia_bl.c @@ -111,6 +111,4 @@ module_exit(mbp_exit); MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); MODULE_DESCRIPTION("Nvidia-based Macbook Pro Backlight Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,1"); -MODULE_ALIAS("svnAppleInc.:pnMacBookPro3,2"); -MODULE_ALIAS("svnAppleInc.:pnMacBookPro4,1"); +MODULE_DEVICE_TABLE(dmi, mbp_device_table); diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index e721644bad7..1e35ba6f18e 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -372,7 +372,7 @@ static int __devexit bw2_remove(struct of_device *op) return 0; } -static struct of_device_id bw2_match[] = { +static const struct of_device_id bw2_match[] = { { .name = "bwtwo", }, diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index b17e7467177..a2d1882791a 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -589,7 +589,7 @@ static int __devexit cg14_remove(struct of_device *op) return 0; } -static struct of_device_id cg14_match[] = { +static const struct of_device_id cg14_match[] = { { .name = "cgfourteen", }, diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index 3aa7b6cb026..99f87fb61d0 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -456,7 +456,7 @@ static int __devexit cg3_remove(struct of_device *op) return 0; } -static struct of_device_id cg3_match[] = { +static const struct of_device_id cg3_match[] = { { .name = "cgthree", }, diff --git a/drivers/video/cg6.c b/drivers/video/cg6.c index 2f64bb3bd25..940ec04f0f1 100644 --- a/drivers/video/cg6.c +++ b/drivers/video/cg6.c @@ -34,10 +34,11 @@ static int cg6_blank(int, struct fb_info *); static void cg6_imageblit(struct fb_info *, const struct fb_image *); static void cg6_fillrect(struct fb_info *, const struct fb_fillrect *); +static void cg6_copyarea(struct fb_info *info, const struct fb_copyarea *area); static int cg6_sync(struct fb_info *); static int cg6_mmap(struct fb_info *, struct vm_area_struct *); static int cg6_ioctl(struct fb_info *, unsigned int, unsigned long); -static void cg6_copyarea(struct fb_info *info, const struct fb_copyarea *area); +static int cg6_pan_display(struct fb_var_screeninfo *, struct fb_info *); /* * Frame buffer operations @@ -47,6 +48,7 @@ static struct fb_ops cg6_ops = { .owner = THIS_MODULE, .fb_setcolreg = cg6_setcolreg, .fb_blank = cg6_blank, + .fb_pan_display = cg6_pan_display, .fb_fillrect = cg6_fillrect, .fb_copyarea = cg6_copyarea, .fb_imageblit = cg6_imageblit, @@ -161,6 +163,7 @@ static struct fb_ops cg6_ops = { #define CG6_THC_MISC_INT_ENAB (1 << 5) #define CG6_THC_MISC_INT (1 << 4) #define CG6_THC_MISC_INIT 0x9f +#define CG6_THC_CURSOFF ((65536-32) | ((65536-32) << 16)) /* The contents are unknown */ struct cg6_tec { @@ -280,6 +283,33 @@ static int cg6_sync(struct fb_info *info) return 0; } +static void cg6_switch_from_graph(struct cg6_par *par) +{ + struct cg6_thc __iomem *thc = par->thc; + unsigned long flags; + + spin_lock_irqsave(&par->lock, flags); + + /* Hide the cursor. */ + sbus_writel(CG6_THC_CURSOFF, &thc->thc_cursxy); + + spin_unlock_irqrestore(&par->lock, flags); +} + +static int cg6_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct cg6_par *par = (struct cg6_par *)info->par; + + /* We just use this to catch switches out of + * graphics mode. + */ + cg6_switch_from_graph(par); + + if (var->xoffset || var->yoffset || var->vmode) + return -EINVAL; + return 0; +} + /** * cg6_fillrect - Draws a rectangle on the screen. * @@ -643,9 +673,13 @@ static void __devinit cg6_chip_init(struct fb_info *info) struct cg6_par *par = (struct cg6_par *)info->par; struct cg6_tec __iomem *tec = par->tec; struct cg6_fbc __iomem *fbc = par->fbc; + struct cg6_thc __iomem *thc = par->thc; u32 rev, conf, mode; int i; + /* Hide the cursor. */ + sbus_writel(CG6_THC_CURSOFF, &thc->thc_cursxy); + /* Turn off stuff in the Transform Engine. */ sbus_writel(0, &tec->tec_matrix); sbus_writel(0, &tec->tec_clip); @@ -814,7 +848,7 @@ static int __devexit cg6_remove(struct of_device *op) return 0; } -static struct of_device_id cg6_match[] = { +static const struct of_device_id cg6_match[] = { { .name = "cgsix", }, diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 06f87b04f20..2f50a80b413 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -43,22 +43,6 @@ config VGACON_SOFT_SCROLLBACK_SIZE buffer. Each 64KB will give you approximately 16 80x25 screenfuls of scrollback buffer -config VIDEO_SELECT - bool "Video mode selection support" - depends on X86 && VGA_CONSOLE - ---help--- - This enables support for text mode selection on kernel startup. If - you want to take advantage of some high-resolution text mode your - card's BIOS offers, but the traditional Linux utilities like - SVGATextMode don't, you can say Y here and set the mode using the - "vga=" option from your boot loader (lilo or loadlin) or set - "vga=ask" which brings up a video mode menu on kernel startup. (Try - "man bootparam" or see the documentation of your boot loader about - how to pass options to the kernel.) - - Read the file <file:Documentation/svga.txt> for more information - about the Video mode selection support. If unsure, say N. - config MDA_CONSOLE depends on !M68K && !PARISC && ISA tristate "MDA text console (dual-headed) (EXPERIMENTAL)" diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c index 7992b13ee68..9dbb9646081 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c @@ -1042,7 +1042,7 @@ static int __devexit ffb_remove(struct of_device *op) return 0; } -static struct of_device_id ffb_match[] = { +static const struct of_device_id ffb_match[] = { { .name = "SUNW,ffb", }, diff --git a/drivers/video/leo.c b/drivers/video/leo.c index 13fea61d6ae..7c7e8c2da9d 100644 --- a/drivers/video/leo.c +++ b/drivers/video/leo.c @@ -33,6 +33,7 @@ static int leo_blank(int, struct fb_info *); static int leo_mmap(struct fb_info *, struct vm_area_struct *); static int leo_ioctl(struct fb_info *, unsigned int, unsigned long); +static int leo_pan_display(struct fb_var_screeninfo *, struct fb_info *); /* * Frame buffer operations @@ -42,6 +43,7 @@ static struct fb_ops leo_ops = { .owner = THIS_MODULE, .fb_setcolreg = leo_setcolreg, .fb_blank = leo_blank, + .fb_pan_display = leo_pan_display, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, @@ -206,6 +208,60 @@ static void leo_wait(struct leo_lx_krn __iomem *lx_krn) return; } +static void leo_switch_from_graph(struct fb_info *info) +{ + struct leo_par *par = (struct leo_par *) info->par; + struct leo_ld_ss0 __iomem *ss = par->ld_ss0; + struct leo_cursor __iomem *cursor = par->cursor; + unsigned long flags; + u32 val; + + spin_lock_irqsave(&par->lock, flags); + + par->extent = ((info->var.xres - 1) | + ((info->var.yres - 1) << 16)); + + sbus_writel(0xffffffff, &ss->wid); + sbus_writel(0xffff, &ss->wmask); + sbus_writel(0, &ss->vclipmin); + sbus_writel(par->extent, &ss->vclipmax); + sbus_writel(0, &ss->fg); + sbus_writel(0xff000000, &ss->planemask); + sbus_writel(0x310850, &ss->rop); + sbus_writel(0, &ss->widclip); + sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11), + &par->lc_ss0_usr->extent); + sbus_writel(4, &par->lc_ss0_usr->addrspace); + sbus_writel(0x80000000, &par->lc_ss0_usr->fill); + sbus_writel(0, &par->lc_ss0_usr->fontt); + do { + val = sbus_readl(&par->lc_ss0_usr->csr); + } while (val & 0x20000000); + + /* setup screen buffer for cfb_* functions */ + sbus_writel(1, &ss->wid); + sbus_writel(0x00ffffff, &ss->planemask); + sbus_writel(0x310b90, &ss->rop); + sbus_writel(0, &par->lc_ss0_usr->addrspace); + + /* hide cursor */ + sbus_writel(sbus_readl(&cursor->cur_misc) & ~LEO_CUR_ENABLE, &cursor->cur_misc); + + spin_unlock_irqrestore(&par->lock, flags); +} + +static int leo_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) +{ + /* We just use this to catch switches out of + * graphics mode. + */ + leo_switch_from_graph(info); + + if (var->xoffset || var->yoffset || var->vmode) + return -EINVAL; + return 0; +} + /** * leo_setcolreg - Optional function. Sets a color register. * @regno: boolean, 0 copy local, 1 get_user() function @@ -454,44 +510,6 @@ static void leo_init_wids(struct fb_info *info) leo_wid_put(info, &wl); } -static void leo_switch_from_graph(struct fb_info *info) -{ - struct leo_par *par = (struct leo_par *) info->par; - struct leo_ld_ss0 __iomem *ss = par->ld_ss0; - unsigned long flags; - u32 val; - - spin_lock_irqsave(&par->lock, flags); - - par->extent = ((info->var.xres - 1) | - ((info->var.yres - 1) << 16)); - - sbus_writel(0xffffffff, &ss->wid); - sbus_writel(0xffff, &ss->wmask); - sbus_writel(0, &ss->vclipmin); - sbus_writel(par->extent, &ss->vclipmax); - sbus_writel(0, &ss->fg); - sbus_writel(0xff000000, &ss->planemask); - sbus_writel(0x310850, &ss->rop); - sbus_writel(0, &ss->widclip); - sbus_writel((info->var.xres-1) | ((info->var.yres-1) << 11), - &par->lc_ss0_usr->extent); - sbus_writel(4, &par->lc_ss0_usr->addrspace); - sbus_writel(0x80000000, &par->lc_ss0_usr->fill); - sbus_writel(0, &par->lc_ss0_usr->fontt); - do { - val = sbus_readl(&par->lc_ss0_usr->csr); - } while (val & 0x20000000); - - /* setup screen buffer for cfb_* functions */ - sbus_writel(1, &ss->wid); - sbus_writel(0x00ffffff, &ss->planemask); - sbus_writel(0x310b90, &ss->rop); - sbus_writel(0, &par->lc_ss0_usr->addrspace); - - spin_unlock_irqrestore(&par->lock, flags); -} - static void leo_init_hw(struct fb_info *info) { struct leo_par *par = (struct leo_par *) info->par; @@ -641,7 +659,7 @@ static int __devexit leo_remove(struct of_device *op) return 0; } -static struct of_device_id leo_match[] = { +static const struct of_device_id leo_match[] = { { .name = "SUNW,leo", }, diff --git a/drivers/video/p9100.c b/drivers/video/p9100.c index 9e903454ffc..7000f2cd585 100644 --- a/drivers/video/p9100.c +++ b/drivers/video/p9100.c @@ -349,7 +349,7 @@ static int __devexit p9100_remove(struct of_device *op) return 0; } -static struct of_device_id p9100_match[] = { +static const struct of_device_id p9100_match[] = { { .name = "p9100", }, diff --git a/drivers/video/tcx.c b/drivers/video/tcx.c index 2a03f78bbb0..643afbfe827 100644 --- a/drivers/video/tcx.c +++ b/drivers/video/tcx.c @@ -505,7 +505,7 @@ static int __devexit tcx_remove(struct of_device *op) return 0; } -static struct of_device_id tcx_match[] = { +static const struct of_device_id tcx_match[] = { { .name = "SUNW,tcx", }, diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c index 47ed39b52f9..a463b3dd837 100644 --- a/drivers/video/xen-fbfront.c +++ b/drivers/video/xen-fbfront.c @@ -680,11 +680,11 @@ static struct xenbus_driver xenfb = { static int __init xenfb_init(void) { - if (!is_running_on_xen()) + if (!xen_domain()) return -ENODEV; /* Nothing to do if running in dom0. */ - if (is_initial_xendomain()) + if (xen_initial_domain()) return -ENODEV; return xenbus_register_frontend(&xenfb); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index c5103671670..1a22fe782a2 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -66,6 +66,13 @@ config AT91RM9200_WATCHDOG Watchdog timer embedded into AT91RM9200 chips. This will reboot your system when the timeout is reached. +config AT91SAM9X_WATCHDOG + tristate "AT91SAM9X watchdog" + depends on WATCHDOG && (ARCH_AT91SAM9260 || ARCH_AT91SAM9261) + help + Watchdog timer embedded into AT91SAM9X chips. This will reboot your + system when the timeout is reached. + config 21285_WATCHDOG tristate "DC21285 watchdog" depends on FOOTBRIDGE @@ -217,6 +224,15 @@ config DAVINCI_WATCHDOG NOTE: once enabled, this timer cannot be disabled. Say N if you are unsure. +config ORION5X_WATCHDOG + tristate "Orion5x watchdog" + depends on ARCH_ORION5X + help + Say Y here if to include support for the watchdog timer + in the Orion5x ARM SoCs. + To compile this driver as a module, choose M here: the + module will be called orion5x_wdt. + # ARM26 Architecture # AVR32 Architecture @@ -416,6 +432,18 @@ config IT8712F_WDT To compile this driver as a module, choose M here: the module will be called it8712f_wdt. +config IT87_WDT + tristate "IT87 Watchdog Timer" + depends on X86 && EXPERIMENTAL + ---help--- + This is the driver for the hardware watchdog on the ITE IT8716, + IT8718, IT8726, IT8712(Version J,K) Super I/O chips. This watchdog + simply watches your kernel to make sure it doesn't freeze, and if + it does, it reboots your computer after a certain amount of time. + + To compile this driver as a module, choose M here: the module will + be called it87_wdt. + config HP_WATCHDOG tristate "HP Proliant iLO 2 Hardware Watchdog Timer" depends on X86 @@ -573,6 +601,21 @@ config W83697HF_WDT Most people will say N. +config W83697UG_WDT + tristate "W83697UG/W83697UF Watchdog Timer" + depends on X86 + ---help--- + This is the driver for the hardware watchdog on the W83697UG/UF + chipset as used in MSI Fuzzy CX700 VIA motherboards (and likely others). + This watchdog simply watches your kernel to make sure it doesn't + freeze, and if it does, it reboots your computer after a certain + amount of time. + + To compile this driver as a module, choose M here: the + module will be called w83697ug_wdt. + + Most people will say N. + config W83877F_WDT tristate "W83877F (EMACS) Watchdog Timer" depends on X86 diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index e0ef123fbde..e352bbb7630 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o # ARM Architecture obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o +obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o @@ -39,6 +40,7 @@ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o +obj-$(CONFIG_ORION5X_WATCHDOG) += orion5x_wdt.o # ARM26 Architecture @@ -71,6 +73,7 @@ ifeq ($(CONFIG_ITCO_VENDOR_SUPPORT),y) obj-$(CONFIG_ITCO_WDT) += iTCO_vendor_support.o endif obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o +obj-$(CONFIG_IT87_WDT) += it87_wdt.o obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o @@ -83,6 +86,7 @@ obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o +obj-$(CONFIG_W83697UG_WDT) += w83697ug_wdt.o obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o @@ -123,6 +127,9 @@ obj-$(CONFIG_SH_WDT) += shwdt.o # SPARC64 Architecture +obj-$(CONFIG_WATCHDOG_RIO) += riowd.o +obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o + # XTENSA Architecture # Architecture Independant diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c new file mode 100644 index 00000000000..b4babfc3158 --- /dev/null +++ b/drivers/watchdog/at91sam9_wdt.c @@ -0,0 +1,328 @@ +/* + * Watchdog driver for Atmel AT91SAM9x processors. + * + * Copyright (C) 2008 Renaud CERRATO r.cerrato@til-technologies.fr + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* + * The Watchdog Timer Mode Register can be only written to once. If the + * timeout need to be set from Linux, be sure that the bootstrap or the + * bootloader doesn't write to this register. + */ + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/watchdog.h> +#include <linux/jiffies.h> +#include <linux/timer.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> + +#include <asm/arch/at91_wdt.h> + +#define DRV_NAME "AT91SAM9 Watchdog" + +/* AT91SAM9 watchdog runs a 12bit counter @ 256Hz, + * use this to convert a watchdog + * value from/to milliseconds. + */ +#define ms_to_ticks(t) (((t << 8) / 1000) - 1) +#define ticks_to_ms(t) (((t + 1) * 1000) >> 8) + +/* Hardware timeout in seconds */ +#define WDT_HW_TIMEOUT 2 + +/* Timer heartbeat (500ms) */ +#define WDT_TIMEOUT (HZ/2) + +/* User land timeout */ +#define WDT_HEARTBEAT 15 +static int heartbeat = WDT_HEARTBEAT; +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. " + "(default = " __MODULE_STRING(WDT_HEARTBEAT) ")"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static void at91_ping(unsigned long data); + +static struct { + unsigned long next_heartbeat; /* the next_heartbeat for the timer */ + unsigned long open; + char expect_close; + struct timer_list timer; /* The timer that pings the watchdog */ +} at91wdt_private; + +/* ......................................................................... */ + + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static inline void at91_wdt_reset(void) +{ + at91_sys_write(AT91_WDT_CR, AT91_WDT_KEY | AT91_WDT_WDRSTT); +} + +/* + * Timer tick + */ +static void at91_ping(unsigned long data) +{ + if (time_before(jiffies, at91wdt_private.next_heartbeat) || + (!nowayout && !at91wdt_private.open)) { + at91_wdt_reset(); + mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); + } else + printk(KERN_CRIT DRV_NAME": I will reset your machine !\n"); +} + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int at91_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &at91wdt_private.open)) + return -EBUSY; + + at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; + mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); + + return nonseekable_open(inode, file); +} + +/* + * Close the watchdog device. + */ +static int at91_wdt_close(struct inode *inode, struct file *file) +{ + clear_bit(0, &at91wdt_private.open); + + /* stop internal ping */ + if (!at91wdt_private.expect_close) + del_timer(&at91wdt_private.timer); + + at91wdt_private.expect_close = 0; + return 0; +} + +/* + * Set the watchdog time interval in 1/256Hz (write-once) + * Counter is 12 bit. + */ +static int at91_wdt_settimeout(unsigned int timeout) +{ + unsigned int reg; + unsigned int mr; + + /* Check if disabled */ + mr = at91_sys_read(AT91_WDT_MR); + if (mr & AT91_WDT_WDDIS) { + printk(KERN_ERR DRV_NAME": sorry, watchdog is disabled\n"); + return -EIO; + } + + /* + * All counting occurs at SLOW_CLOCK / 128 = 256 Hz + * + * Since WDV is a 12-bit counter, the maximum period is + * 4096 / 256 = 16 seconds. + */ + reg = AT91_WDT_WDRSTEN /* causes watchdog reset */ + /* | AT91_WDT_WDRPROC causes processor reset only */ + | AT91_WDT_WDDBGHLT /* disabled in debug mode */ + | AT91_WDT_WDD /* restart at any time */ + | (timeout & AT91_WDT_WDV); /* timer value */ + at91_sys_write(AT91_WDT_MR, reg); + + return 0; +} + +static const struct watchdog_info at91_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +/* + * Handle commands from user-space. + */ +static long at91_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_value; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &at91_wdt_info, + sizeof(at91_wdt_info)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + heartbeat = new_value; + at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; + + return put_user(new_value, p); /* return current value */ + + case WDIOC_GETTIMEOUT: + return put_user(heartbeat, p); + } + return -ENOTTY; +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, + loff_t *ppos) +{ + if (!len) + return 0; + + /* Scan for magic character */ + if (!nowayout) { + size_t i; + + at91wdt_private.expect_close = 0; + + for (i = 0; i < len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') { + at91wdt_private.expect_close = 42; + break; + } + } + } + + at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; + + return len; +} + +/* ......................................................................... */ + +static const struct file_operations at91wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = at91_wdt_ioctl, + .open = at91_wdt_open, + .release = at91_wdt_close, + .write = at91_wdt_write, +}; + +static struct miscdevice at91wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &at91wdt_fops, +}; + +static int __init at91wdt_probe(struct platform_device *pdev) +{ + int res; + + if (at91wdt_miscdev.parent) + return -EBUSY; + at91wdt_miscdev.parent = &pdev->dev; + + /* Set watchdog */ + res = at91_wdt_settimeout(ms_to_ticks(WDT_HW_TIMEOUT * 1000)); + if (res) + return res; + + res = misc_register(&at91wdt_miscdev); + if (res) + return res; + + at91wdt_private.next_heartbeat = jiffies + heartbeat * HZ; + setup_timer(&at91wdt_private.timer, at91_ping, 0); + mod_timer(&at91wdt_private.timer, jiffies + WDT_TIMEOUT); + + printk(KERN_INFO DRV_NAME " enabled (heartbeat=%d sec, nowayout=%d)\n", + heartbeat, nowayout); + + return 0; +} + +static int __exit at91wdt_remove(struct platform_device *pdev) +{ + int res; + + res = misc_deregister(&at91wdt_miscdev); + if (!res) + at91wdt_miscdev.parent = NULL; + + return res; +} + +#ifdef CONFIG_PM + +static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) +{ + return 0; +} + +static int at91wdt_resume(struct platform_device *pdev) +{ + return 0; +} + +#else +#define at91wdt_suspend NULL +#define at91wdt_resume NULL +#endif + +static struct platform_driver at91wdt_driver = { + .remove = __exit_p(at91wdt_remove), + .suspend = at91wdt_suspend, + .resume = at91wdt_resume, + .driver = { + .name = "at91_wdt", + .owner = THIS_MODULE, + }, +}; + +static int __init at91sam_wdt_init(void) +{ + return platform_driver_probe(&at91wdt_driver, at91wdt_probe); +} + +static void __exit at91sam_wdt_exit(void) +{ + platform_driver_unregister(&at91wdt_driver); +} + +module_init(at91sam_wdt_init); +module_exit(at91sam_wdt_exit); + +MODULE_AUTHOR("Renaud CERRATO <r.cerrato@til-technologies.fr>"); +MODULE_DESCRIPTION("Watchdog driver for Atmel AT91SAM9x processors"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c new file mode 100644 index 00000000000..084dfe9cecf --- /dev/null +++ b/drivers/watchdog/cpwd.c @@ -0,0 +1,695 @@ +/* cpwd.c - driver implementation for hardware watchdog + * timers found on Sun Microsystems CP1400 and CP1500 boards. + * + * This device supports both the generic Linux watchdog + * interface and Solaris-compatible ioctls as best it is + * able. + * + * NOTE: CP1400 systems appear to have a defective intr_mask + * register on the PLD, preventing the disabling of + * timer interrupts. We use a timer to periodically + * reset 'stopped' watchdogs on affected platforms. + * + * Copyright (c) 2000 Eric Brower (ebrower@usa.net) + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/timer.h> +#include <linux/smp_lock.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include <asm/irq.h> +#include <asm/uaccess.h> + +#include <asm/watchdog.h> + +#define DRIVER_NAME "cpwd" +#define PFX DRIVER_NAME ": " + +#define WD_OBPNAME "watchdog" +#define WD_BADMODEL "SUNW,501-5336" +#define WD_BTIMEOUT (jiffies + (HZ * 1000)) +#define WD_BLIMIT 0xFFFF + +#define WD0_MINOR 212 +#define WD1_MINOR 213 +#define WD2_MINOR 214 + +/* Internal driver definitions. */ +#define WD0_ID 0 +#define WD1_ID 1 +#define WD2_ID 2 +#define WD_NUMDEVS 3 + +#define WD_INTR_OFF 0 +#define WD_INTR_ON 1 + +#define WD_STAT_INIT 0x01 /* Watchdog timer is initialized */ +#define WD_STAT_BSTOP 0x02 /* Watchdog timer is brokenstopped */ +#define WD_STAT_SVCD 0x04 /* Watchdog interrupt occurred */ + +/* Register value definitions + */ +#define WD0_INTR_MASK 0x01 /* Watchdog device interrupt masks */ +#define WD1_INTR_MASK 0x02 +#define WD2_INTR_MASK 0x04 + +#define WD_S_RUNNING 0x01 /* Watchdog device status running */ +#define WD_S_EXPIRED 0x02 /* Watchdog device status expired */ + +struct cpwd { + void __iomem *regs; + spinlock_t lock; + + unsigned int irq; + + unsigned long timeout; + bool enabled; + bool reboot; + bool broken; + bool initialized; + + struct { + struct miscdevice misc; + void __iomem *regs; + u8 intr_mask; + u8 runstatus; + u16 timeout; + } devs[WD_NUMDEVS]; +}; + +static struct cpwd *cpwd_device; + +/* Sun uses Altera PLD EPF8820ATC144-4 + * providing three hardware watchdogs: + * + * 1) RIC - sends an interrupt when triggered + * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU + * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board + * + *** Timer register block definition (struct wd_timer_regblk) + * + * dcntr and limit registers (halfword access): + * ------------------- + * | 15 | ...| 1 | 0 | + * ------------------- + * |- counter val -| + * ------------------- + * dcntr - Current 16-bit downcounter value. + * When downcounter reaches '0' watchdog expires. + * Reading this register resets downcounter with 'limit' value. + * limit - 16-bit countdown value in 1/10th second increments. + * Writing this register begins countdown with input value. + * Reading from this register does not affect counter. + * NOTES: After watchdog reset, dcntr and limit contain '1' + * + * status register (byte access): + * --------------------------- + * | 7 | ... | 2 | 1 | 0 | + * --------------+------------ + * |- UNUSED -| EXP | RUN | + * --------------------------- + * status- Bit 0 - Watchdog is running + * Bit 1 - Watchdog has expired + * + *** PLD register block definition (struct wd_pld_regblk) + * + * intr_mask register (byte access): + * --------------------------------- + * | 7 | ... | 3 | 2 | 1 | 0 | + * +-------------+------------------ + * |- UNUSED -| WD3 | WD2 | WD1 | + * --------------------------------- + * WD3 - 1 == Interrupt disabled for watchdog 3 + * WD2 - 1 == Interrupt disabled for watchdog 2 + * WD1 - 1 == Interrupt disabled for watchdog 1 + * + * pld_status register (byte access): + * UNKNOWN, MAGICAL MYSTERY REGISTER + * + */ +#define WD_TIMER_REGSZ 16 +#define WD0_OFF 0 +#define WD1_OFF (WD_TIMER_REGSZ * 1) +#define WD2_OFF (WD_TIMER_REGSZ * 2) +#define PLD_OFF (WD_TIMER_REGSZ * 3) + +#define WD_DCNTR 0x00 +#define WD_LIMIT 0x04 +#define WD_STATUS 0x08 + +#define PLD_IMASK (PLD_OFF + 0x00) +#define PLD_STATUS (PLD_OFF + 0x04) + +static struct timer_list cpwd_timer; + +static int wd0_timeout = 0; +static int wd1_timeout = 0; +static int wd2_timeout = 0; + +module_param (wd0_timeout, int, 0); +MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); +module_param (wd1_timeout, int, 0); +MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs"); +module_param (wd2_timeout, int, 0); +MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); + +MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); +MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("watchdog"); + +static void cpwd_writew(u16 val, void __iomem *addr) +{ + writew(cpu_to_le16(val), addr); +} +static u16 cpwd_readw(void __iomem *addr) +{ + u16 val = readw(addr); + + return le16_to_cpu(val); +} + +static void cpwd_writeb(u8 val, void __iomem *addr) +{ + writeb(val, addr); +} + +static u8 cpwd_readb(void __iomem *addr) +{ + return readb(addr); +} + +/* Enable or disable watchdog interrupts + * Because of the CP1400 defect this should only be + * called during initialzation or by wd_[start|stop]timer() + * + * index - sub-device index, or -1 for 'all' + * enable - non-zero to enable interrupts, zero to disable + */ +static void cpwd_toggleintr(struct cpwd *p, int index, int enable) +{ + unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK); + unsigned char setregs = + (index == -1) ? + (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : + (p->devs[index].intr_mask); + + if (enable == WD_INTR_ON) + curregs &= ~setregs; + else + curregs |= setregs; + + cpwd_writeb(curregs, p->regs + PLD_IMASK); +} + +/* Restarts timer with maximum limit value and + * does not unset 'brokenstop' value. + */ +static void cpwd_resetbrokentimer(struct cpwd *p, int index) +{ + cpwd_toggleintr(p, index, WD_INTR_ON); + cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT); +} + +/* Timer method called to reset stopped watchdogs-- + * because of the PLD bug on CP1400, we cannot mask + * interrupts within the PLD so me must continually + * reset the timers ad infinitum. + */ +static void cpwd_brokentimer(unsigned long data) +{ + struct cpwd *p = (struct cpwd *) data; + int id, tripped = 0; + + /* kill a running timer instance, in case we + * were called directly instead of by kernel timer + */ + if (timer_pending(&cpwd_timer)) + del_timer(&cpwd_timer); + + for (id = 0; id < WD_NUMDEVS; id++) { + if (p->devs[id].runstatus & WD_STAT_BSTOP) { + ++tripped; + cpwd_resetbrokentimer(p, id); + } + } + + if (tripped) { + /* there is at least one timer brokenstopped-- reschedule */ + cpwd_timer.expires = WD_BTIMEOUT; + add_timer(&cpwd_timer); + } +} + +/* Reset countdown timer with 'limit' value and continue countdown. + * This will not start a stopped timer. + */ +static void cpwd_pingtimer(struct cpwd *p, int index) +{ + if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) + cpwd_readw(p->devs[index].regs + WD_DCNTR); +} + +/* Stop a running watchdog timer-- the timer actually keeps + * running, but the interrupt is masked so that no action is + * taken upon expiration. + */ +static void cpwd_stoptimer(struct cpwd *p, int index) +{ + if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) { + cpwd_toggleintr(p, index, WD_INTR_OFF); + + if (p->broken) { + p->devs[index].runstatus |= WD_STAT_BSTOP; + cpwd_brokentimer((unsigned long) p); + } + } +} + +/* Start a watchdog timer with the specified limit value + * If the watchdog is running, it will be restarted with + * the provided limit value. + * + * This function will enable interrupts on the specified + * watchdog. + */ +static void cpwd_starttimer(struct cpwd *p, int index) +{ + if (p->broken) + p->devs[index].runstatus &= ~WD_STAT_BSTOP; + + p->devs[index].runstatus &= ~WD_STAT_SVCD; + + cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT); + cpwd_toggleintr(p, index, WD_INTR_ON); +} + +static int cpwd_getstatus(struct cpwd *p, int index) +{ + unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS); + unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK); + unsigned char ret = WD_STOPPED; + + /* determine STOPPED */ + if (!stat) + return ret; + + /* determine EXPIRED vs FREERUN vs RUNNING */ + else if (WD_S_EXPIRED & stat) { + ret = WD_EXPIRED; + } else if(WD_S_RUNNING & stat) { + if (intr & p->devs[index].intr_mask) { + ret = WD_FREERUN; + } else { + /* Fudge WD_EXPIRED status for defective CP1400-- + * IF timer is running + * AND brokenstop is set + * AND an interrupt has been serviced + * we are WD_EXPIRED. + * + * IF timer is running + * AND brokenstop is set + * AND no interrupt has been serviced + * we are WD_FREERUN. + */ + if (p->broken && + (p->devs[index].runstatus & WD_STAT_BSTOP)) { + if (p->devs[index].runstatus & WD_STAT_SVCD) { + ret = WD_EXPIRED; + } else { + /* we could as well pretend we are expired */ + ret = WD_FREERUN; + } + } else { + ret = WD_RUNNING; + } + } + } + + /* determine SERVICED */ + if (p->devs[index].runstatus & WD_STAT_SVCD) + ret |= WD_SERVICED; + + return(ret); +} + +static irqreturn_t cpwd_interrupt(int irq, void *dev_id) +{ + struct cpwd *p = dev_id; + + /* Only WD0 will interrupt-- others are NMI and we won't + * see them here.... + */ + spin_lock_irq(&p->lock); + + cpwd_stoptimer(p, WD0_ID); + p->devs[WD0_ID].runstatus |= WD_STAT_SVCD; + + spin_unlock_irq(&p->lock); + + return IRQ_HANDLED; +} + +static int cpwd_open(struct inode *inode, struct file *f) +{ + struct cpwd *p = cpwd_device; + + lock_kernel(); + switch(iminor(inode)) { + case WD0_MINOR: + case WD1_MINOR: + case WD2_MINOR: + break; + + default: + unlock_kernel(); + return -ENODEV; + } + + /* Register IRQ on first open of device */ + if (!p->initialized) { + if (request_irq(p->irq, &cpwd_interrupt, + IRQF_SHARED, DRIVER_NAME, p)) { + printk(KERN_ERR PFX "Cannot register IRQ %d\n", + p->irq); + unlock_kernel(); + return -EBUSY; + } + p->initialized = true; + } + + unlock_kernel(); + + return nonseekable_open(inode, f); +} + +static int cpwd_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int cpwd_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info info = { + .options = WDIOF_SETTIMEOUT, + .firmware_version = 1, + .identity = DRIVER_NAME, + }; + void __user *argp = (void __user *)arg; + int index = iminor(inode) - WD0_MINOR; + struct cpwd *p = cpwd_device; + int setopt = 0; + + switch (cmd) { + /* Generic Linux IOCTLs */ + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &info, sizeof(struct watchdog_info))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + if (put_user(0, (int __user *)argp)) + return -EFAULT; + break; + + case WDIOC_KEEPALIVE: + cpwd_pingtimer(p, index); + break; + + case WDIOC_SETOPTIONS: + if (copy_from_user(&setopt, argp, sizeof(unsigned int))) + return -EFAULT; + + if (setopt & WDIOS_DISABLECARD) { + if (p->enabled) + return -EINVAL; + cpwd_stoptimer(p, index); + } else if (setopt & WDIOS_ENABLECARD) { + cpwd_starttimer(p, index); + } else { + return -EINVAL; + } + break; + + /* Solaris-compatible IOCTLs */ + case WIOCGSTAT: + setopt = cpwd_getstatus(p, index); + if (copy_to_user(argp, &setopt, sizeof(unsigned int))) + return -EFAULT; + break; + + case WIOCSTART: + cpwd_starttimer(p, index); + break; + + case WIOCSTOP: + if (p->enabled) + return(-EINVAL); + + cpwd_stoptimer(p, index); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int rval = -ENOIOCTLCMD; + + switch (cmd) { + /* solaris ioctls are specific to this driver */ + case WIOCSTART: + case WIOCSTOP: + case WIOCGSTAT: + lock_kernel(); + rval = cpwd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); + unlock_kernel(); + break; + + /* everything else is handled by the generic compat layer */ + default: + break; + } + + return rval; +} + +static ssize_t cpwd_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct cpwd *p = cpwd_device; + int index = iminor(inode); + + if (count) { + cpwd_pingtimer(p, index); + return 1; + } + + return 0; +} + +static ssize_t cpwd_read(struct file * file, char __user *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const struct file_operations cpwd_fops = { + .owner = THIS_MODULE, + .ioctl = cpwd_ioctl, + .compat_ioctl = cpwd_compat_ioctl, + .open = cpwd_open, + .write = cpwd_write, + .read = cpwd_read, + .release = cpwd_release, +}; + +static int __devinit cpwd_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct device_node *options; + const char *str_prop; + const void *prop_val; + int i, err = -EINVAL; + struct cpwd *p; + + if (cpwd_device) + return -EINVAL; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + err = -ENOMEM; + if (!p) { + printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n"); + goto out; + } + + p->irq = op->irqs[0]; + + spin_lock_init(&p->lock); + + p->regs = of_ioremap(&op->resource[0], 0, + 4 * WD_TIMER_REGSZ, DRIVER_NAME); + if (!p->regs) { + printk(KERN_ERR PFX "Unable to map registers.\n"); + goto out_free; + } + + options = of_find_node_by_path("/options"); + err = -ENODEV; + if (!options) { + printk(KERN_ERR PFX "Unable to find /options node.\n"); + goto out_iounmap; + } + + prop_val = of_get_property(options, "watchdog-enable?", NULL); + p->enabled = (prop_val ? true : false); + + prop_val = of_get_property(options, "watchdog-reboot?", NULL); + p->reboot = (prop_val ? true : false); + + str_prop = of_get_property(options, "watchdog-timeout", NULL); + if (str_prop) + p->timeout = simple_strtoul(str_prop, NULL, 10); + + /* CP1400s seem to have broken PLD implementations-- the + * interrupt_mask register cannot be written, so no timer + * interrupts can be masked within the PLD. + */ + str_prop = of_get_property(op->node, "model", NULL); + p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL)); + + if (!p->enabled) + cpwd_toggleintr(p, -1, WD_INTR_OFF); + + for (i = 0; i < WD_NUMDEVS; i++) { + static const char *cpwd_names[] = { "RIC", "XIR", "POR" }; + static int *parms[] = { &wd0_timeout, + &wd1_timeout, + &wd2_timeout }; + struct miscdevice *mp = &p->devs[i].misc; + + mp->minor = WD0_MINOR + i; + mp->name = cpwd_names[i]; + mp->fops = &cpwd_fops; + + p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ); + p->devs[i].intr_mask = (WD0_INTR_MASK << i); + p->devs[i].runstatus &= ~WD_STAT_BSTOP; + p->devs[i].runstatus |= WD_STAT_INIT; + p->devs[i].timeout = p->timeout; + if (*parms[i]) + p->devs[i].timeout = *parms[i]; + + err = misc_register(&p->devs[i].misc); + if (err) { + printk(KERN_ERR "Could not register misc device for " + "dev %d\n", i); + goto out_unregister; + } + } + + if (p->broken) { + init_timer(&cpwd_timer); + cpwd_timer.function = cpwd_brokentimer; + cpwd_timer.data = (unsigned long) p; + cpwd_timer.expires = WD_BTIMEOUT; + + printk(KERN_INFO PFX "PLD defect workaround enabled for " + "model " WD_BADMODEL ".\n"); + } + + dev_set_drvdata(&op->dev, p); + cpwd_device = p; + err = 0; + +out: + return err; + +out_unregister: + for (i--; i >= 0; i--) + misc_deregister(&p->devs[i].misc); + +out_iounmap: + of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ); + +out_free: + kfree(p); + goto out; +} + +static int __devexit cpwd_remove(struct of_device *op) +{ + struct cpwd *p = dev_get_drvdata(&op->dev); + int i; + + for (i = 0; i < 4; i++) { + misc_deregister(&p->devs[i].misc); + + if (!p->enabled) { + cpwd_stoptimer(p, i); + if (p->devs[i].runstatus & WD_STAT_BSTOP) + cpwd_resetbrokentimer(p, i); + } + } + + if (p->broken) + del_timer_sync(&cpwd_timer); + + if (p->initialized) + free_irq(p->irq, p); + + of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ); + kfree(p); + + cpwd_device = NULL; + + return 0; +} + +static const struct of_device_id cpwd_match[] = { + { + .name = "watchdog", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, cpwd_match); + +static struct of_platform_driver cpwd_driver = { + .name = DRIVER_NAME, + .match_table = cpwd_match, + .probe = cpwd_probe, + .remove = __devexit_p(cpwd_remove), +}; + +static int __init cpwd_init(void) +{ + return of_register_driver(&cpwd_driver, &of_bus_type); +} + +static void __exit cpwd_exit(void) +{ + of_unregister_driver(&cpwd_driver); +} + +module_init(cpwd_init); +module_exit(cpwd_exit); diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c new file mode 100644 index 00000000000..afb8af397a9 --- /dev/null +++ b/drivers/watchdog/it87_wdt.c @@ -0,0 +1,725 @@ +/* + * Watchdog Timer Driver + * for ITE IT87xx Environment Control - Low Pin Count Input / Output + * + * (c) Copyright 2007 Oliver Schuster <olivers137@aol.com> + * + * Based on softdog.c by Alan Cox, + * 83977f_wdt.c by Jose Goncalves, + * it87.c by Chris Gauthron, Jean Delvare + * + * Data-sheets: Publicly available at the ITE website + * http://www.ite.com.tw/ + * + * Support of the watchdog timers, which are available on + * IT8716, IT8718, IT8726 and IT8712 (J,K version). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/watchdog.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/uaccess.h> +#include <linux/io.h> + +#include <asm/system.h> + +#define WATCHDOG_VERSION "1.12" +#define WATCHDOG_NAME "IT87 WDT" +#define PFX WATCHDOG_NAME ": " +#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" +#define WD_MAGIC 'V' + +/* Defaults for Module Parameter */ +#define DEFAULT_NOGAMEPORT 0 +#define DEFAULT_EXCLUSIVE 1 +#define DEFAULT_TIMEOUT 60 +#define DEFAULT_TESTMODE 0 +#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT + +/* IO Ports */ +#define REG 0x2e +#define VAL 0x2f + +/* Logical device Numbers LDN */ +#define GPIO 0x07 +#define GAMEPORT 0x09 +#define CIR 0x0a + +/* Configuration Registers and Functions */ +#define LDNREG 0x07 +#define CHIPID 0x20 +#define CHIPREV 0x22 +#define ACTREG 0x30 +#define BASEREG 0x60 + +/* Chip Id numbers */ +#define NO_DEV_ID 0xffff +#define IT8705_ID 0x8705 +#define IT8712_ID 0x8712 +#define IT8716_ID 0x8716 +#define IT8718_ID 0x8718 +#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ + +/* GPIO Configuration Registers LDN=0x07 */ +#define WDTCTRL 0x71 +#define WDTCFG 0x72 +#define WDTVALLSB 0x73 +#define WDTVALMSB 0x74 + +/* GPIO Bits WDTCTRL */ +#define WDT_CIRINT 0x80 +#define WDT_MOUSEINT 0x40 +#define WDT_KYBINT 0x20 +#define WDT_GAMEPORT 0x10 /* not it8718 */ +#define WDT_FORCE 0x02 +#define WDT_ZERO 0x01 + +/* GPIO Bits WDTCFG */ +#define WDT_TOV1 0x80 +#define WDT_KRST 0x40 +#define WDT_TOVE 0x20 +#define WDT_PWROK 0x10 +#define WDT_INT_MASK 0x0f + +/* CIR Configuration Register LDN=0x0a */ +#define CIR_ILS 0x70 + +/* The default Base address is not always available, we use this */ +#define CIR_BASE 0x0208 + +/* CIR Controller */ +#define CIR_DR(b) (b) +#define CIR_IER(b) (b + 1) +#define CIR_RCR(b) (b + 2) +#define CIR_TCR1(b) (b + 3) +#define CIR_TCR2(b) (b + 4) +#define CIR_TSR(b) (b + 5) +#define CIR_RSR(b) (b + 6) +#define CIR_BDLR(b) (b + 5) +#define CIR_BDHR(b) (b + 6) +#define CIR_IIR(b) (b + 7) + +/* Default Base address of Game port */ +#define GP_BASE_DEFAULT 0x0201 + +/* wdt_status */ +#define WDTS_TIMER_RUN 0 +#define WDTS_DEV_OPEN 1 +#define WDTS_KEEPALIVE 2 +#define WDTS_LOCKED 3 +#define WDTS_USE_GP 4 +#define WDTS_EXPECTED 5 + +static unsigned int base, gpact, ciract; +static unsigned long wdt_status; +static DEFINE_SPINLOCK(spinlock); + +static int nogameport = DEFAULT_NOGAMEPORT; +static int exclusive = DEFAULT_EXCLUSIVE; +static int timeout = DEFAULT_TIMEOUT; +static int testmode = DEFAULT_TESTMODE; +static int nowayout = DEFAULT_NOWAYOUT; + +module_param(nogameport, int, 0); +MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" + __MODULE_STRING(DEFAULT_NOGAMEPORT)); +module_param(exclusive, int, 0); +MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default=" + __MODULE_STRING(DEFAULT_EXCLUSIVE)); +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" + __MODULE_STRING(DEFAULT_TIMEOUT)); +module_param(testmode, int, 0); +MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default=" + __MODULE_STRING(DEFAULT_TESTMODE)); +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT)); + +/* Superio Chip */ + +static inline void superio_enter(void) +{ + outb(0x87, REG); + outb(0x01, REG); + outb(0x55, REG); + outb(0x55, REG); +} + +static inline void superio_exit(void) +{ + outb(0x02, REG); + outb(0x02, VAL); +} + +static inline void superio_select(int ldn) +{ + outb(LDNREG, REG); + outb(ldn, VAL); +} + +static inline int superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +static inline void superio_outb(int val, int reg) +{ + outb(reg, REG); + outb(val, VAL); +} + +static inline int superio_inw(int reg) +{ + int val; + outb(reg++, REG); + val = inb(VAL) << 8; + outb(reg, REG); + val |= inb(VAL); + return val; +} + +static inline void superio_outw(int val, int reg) +{ + outb(reg++, REG); + outb(val >> 8, VAL); + outb(reg, REG); + outb(val, VAL); +} + +/* watchdog timer handling */ + +static void wdt_keepalive(void) +{ + if (test_bit(WDTS_USE_GP, &wdt_status)) + inb(base); + else + /* The timer reloads with around 5 msec delay */ + outb(0x55, CIR_DR(base)); + set_bit(WDTS_KEEPALIVE, &wdt_status); +} + +static void wdt_start(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + + superio_select(GPIO); + if (test_bit(WDTS_USE_GP, &wdt_status)) + superio_outb(WDT_GAMEPORT, WDTCTRL); + else + superio_outb(WDT_CIRINT, WDTCTRL); + if (!testmode) + superio_outb(WDT_TOV1 | WDT_KRST | WDT_PWROK, WDTCFG); + else + superio_outb(WDT_TOV1, WDTCFG); + superio_outb(timeout>>8, WDTVALMSB); + superio_outb(timeout, WDTVALLSB); + + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); +} + +static void wdt_stop(void) +{ + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + + superio_select(GPIO); + superio_outb(0x00, WDTCTRL); + superio_outb(WDT_TOV1, WDTCFG); + superio_outb(0x00, WDTVALMSB); + superio_outb(0x00, WDTVALLSB); + + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); +} + +/** + * wdt_set_timeout - set a new timeout value with watchdog ioctl + * @t: timeout value in seconds + * + * The hardware device has a 16 bit watchdog timer, thus the + * timeout time ranges between 1 and 65535 seconds. + * + * Used within WDIOC_SETTIMEOUT watchdog device ioctl. + */ + +static int wdt_set_timeout(int t) +{ + unsigned long flags; + + if (t < 1 || t > 65535) + return -EINVAL; + + timeout = t; + + spin_lock_irqsave(&spinlock, flags); + if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { + superio_enter(); + + superio_select(GPIO); + superio_outb(t>>8, WDTVALMSB); + superio_outb(t, WDTVALLSB); + + superio_exit(); + } + spin_unlock_irqrestore(&spinlock, flags); + return 0; +} + +/** + * wdt_get_status - determines the status supported by watchdog ioctl + * @status: status returned to user space + * + * The status bit of the device does not allow to distinguish + * between a regular system reset and a watchdog forced reset. + * But, in test mode it is useful, so it is supported through + * WDIOC_GETSTATUS watchdog ioctl. Additionally the driver + * reports the keepalive signal and the acception of the magic. + * + * Used within WDIOC_GETSTATUS watchdog device ioctl. + */ + +static int wdt_get_status(int *status) +{ + unsigned long flags; + + *status = 0; + if (testmode) { + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + superio_select(GPIO); + if (superio_inb(WDTCTRL) & WDT_ZERO) { + superio_outb(0x00, WDTCTRL); + clear_bit(WDTS_TIMER_RUN, &wdt_status); + *status |= WDIOF_CARDRESET; + } + + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + } + if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) + *status |= WDIOF_KEEPALIVEPING; + if (test_bit(WDTS_EXPECTED, &wdt_status)) + *status |= WDIOF_MAGICCLOSE; + return 0; +} + +/* /dev/watchdog handling */ + +/** + * wdt_open - watchdog file_operations .open + * @inode: inode of the device + * @file: file handle to the device + * + * The watchdog timer starts by opening the device. + * + * Used within the file operation of the watchdog device. + */ + +static int wdt_open(struct inode *inode, struct file *file) +{ + if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) + return -EBUSY; + if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { + if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) + __module_get(THIS_MODULE); + wdt_start(); + } + return nonseekable_open(inode, file); +} + +/** + * wdt_release - watchdog file_operations .release + * @inode: inode of the device + * @file: file handle to the device + * + * Closing the watchdog device either stops the watchdog timer + * or in the case, that nowayout is set or the magic character + * wasn't written, a critical warning about an running watchdog + * timer is given. + * + * Used within the file operation of the watchdog device. + */ + +static int wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { + if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { + wdt_stop(); + clear_bit(WDTS_TIMER_RUN, &wdt_status); + } else { + wdt_keepalive(); + printk(KERN_CRIT PFX + "unexpected close, not stopping watchdog!\n"); + } + } + clear_bit(WDTS_DEV_OPEN, &wdt_status); + return 0; +} + +/** + * wdt_write - watchdog file_operations .write + * @file: file handle to the watchdog + * @buf: buffer to write + * @count: count of bytes + * @ppos: pointer to the position to write. No seeks allowed + * + * A write to a watchdog device is defined as a keepalive signal. Any + * write of data will do, as we don't define content meaning. + * + * Used within the file operation of the watchdog device. + */ + +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) { + clear_bit(WDTS_EXPECTED, &wdt_status); + wdt_keepalive(); + } + if (!nowayout) { + size_t ofs; + + /* note: just in case someone wrote the magic character long ago */ + for (ofs = 0; ofs != count; ofs++) { + char c; + if (get_user(c, buf + ofs)) + return -EFAULT; + if (c == WD_MAGIC) + set_bit(WDTS_EXPECTED, &wdt_status); + } + } + return count; +} + +static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, + .firmware_version = 1, + .identity = WATCHDOG_NAME, +}; + +/** + * wdt_ioctl - watchdog file_operations .unlocked_ioctl + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + * + * Used within the file operation of the watchdog device. + */ + +static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int rc = 0, status, new_options, new_timeout; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + + uarg.i = (int __user *)arg; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(uarg.ident, + &ident, sizeof(ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + wdt_get_status(&status); + return put_user(status, uarg.i); + + case WDIOC_GETBOOTSTATUS: + return put_user(0, uarg.i); + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + return 0; + + case WDIOC_SETOPTIONS: + if (get_user(new_options, uarg.i)) + return -EFAULT; + + switch (new_options) { + case WDIOS_DISABLECARD: + if (test_bit(WDTS_TIMER_RUN, &wdt_status)) + wdt_stop(); + clear_bit(WDTS_TIMER_RUN, &wdt_status); + return 0; + + case WDIOS_ENABLECARD: + if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) + wdt_start(); + return 0; + + default: + return -EFAULT; + } + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, uarg.i)) + return -EFAULT; + rc = wdt_set_timeout(new_timeout); + case WDIOC_GETTIMEOUT: + if (put_user(timeout, uarg.i)) + return -EFAULT; + return rc; + + default: + return -ENOTTY; + } +} + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + wdt_stop(); + return NOTIFY_DONE; +} + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .unlocked_ioctl = wdt_ioctl, + .open = wdt_open, + .release = wdt_release, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; + +static int __init it87_wdt_init(void) +{ + int rc = 0; + u16 chip_type; + u8 chip_rev; + unsigned long flags; + + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + chip_type = superio_inw(CHIPID); + chip_rev = superio_inb(CHIPREV) & 0x0f; + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + + switch (chip_type) { + case IT8716_ID: + case IT8718_ID: + case IT8726_ID: + break; + case IT8712_ID: + if (chip_rev > 7) + break; + case IT8705_ID: + printk(KERN_ERR PFX + "Unsupported Chip found, Chip %04x Revision %02x\n", + chip_type, chip_rev); + return -ENODEV; + case NO_DEV_ID: + printk(KERN_ERR PFX "no device\n"); + return -ENODEV; + default: + printk(KERN_ERR PFX + "Unknown Chip found, Chip %04x Revision %04x\n", + chip_type, chip_rev); + return -ENODEV; + } + + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + + superio_select(GPIO); + superio_outb(WDT_TOV1, WDTCFG); + superio_outb(0x00, WDTCTRL); + + /* First try to get Gameport support */ + if (chip_type != IT8718_ID && !nogameport) { + superio_select(GAMEPORT); + base = superio_inw(BASEREG); + if (!base) { + base = GP_BASE_DEFAULT; + superio_outw(base, BASEREG); + } + gpact = superio_inb(ACTREG); + superio_outb(0x01, ACTREG); + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + if (request_region(base, 1, WATCHDOG_NAME)) + set_bit(WDTS_USE_GP, &wdt_status); + else + rc = -EIO; + } else { + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + } + + /* If we haven't Gameport support, try to get CIR support */ + if (!test_bit(WDTS_USE_GP, &wdt_status)) { + if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { + if (rc == -EIO) + printk(KERN_ERR PFX + "I/O Address 0x%04x and 0x%04x" + " already in use\n", base, CIR_BASE); + else + printk(KERN_ERR PFX + "I/O Address 0x%04x already in use\n", + CIR_BASE); + rc = -EIO; + goto err_out; + } + base = CIR_BASE; + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + + superio_select(CIR); + superio_outw(base, BASEREG); + superio_outb(0x00, CIR_ILS); + ciract = superio_inb(ACTREG); + superio_outb(0x01, ACTREG); + if (rc == -EIO) { + superio_select(GAMEPORT); + superio_outb(gpact, ACTREG); + } + + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + } + + if (timeout < 1 || timeout > 65535) { + timeout = DEFAULT_TIMEOUT; + printk(KERN_WARNING PFX + "Timeout value out of range, use default %d sec\n", + DEFAULT_TIMEOUT); + } + + rc = register_reboot_notifier(&wdt_notifier); + if (rc) { + printk(KERN_ERR PFX + "Cannot register reboot notifier (err=%d)\n", rc); + goto err_out_region; + } + + rc = misc_register(&wdt_miscdev); + if (rc) { + printk(KERN_ERR PFX + "Cannot register miscdev on minor=%d (err=%d)\n", + wdt_miscdev.minor, rc); + goto err_out_reboot; + } + + /* Initialize CIR to use it as keepalive source */ + if (!test_bit(WDTS_USE_GP, &wdt_status)) { + outb(0x00, CIR_RCR(base)); + outb(0xc0, CIR_TCR1(base)); + outb(0x5c, CIR_TCR2(base)); + outb(0x10, CIR_IER(base)); + outb(0x00, CIR_BDHR(base)); + outb(0x01, CIR_BDLR(base)); + outb(0x09, CIR_IER(base)); + } + + printk(KERN_INFO PFX "Chip it%04x revision %d initialized. " + "timeout=%d sec (nowayout=%d testmode=%d exclusive=%d " + "nogameport=%d)\n", chip_type, chip_rev, timeout, + nowayout, testmode, exclusive, nogameport); + + return 0; + +err_out_reboot: + unregister_reboot_notifier(&wdt_notifier); +err_out_region: + release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); + if (!test_bit(WDTS_USE_GP, &wdt_status)) { + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + superio_select(CIR); + superio_outb(ciract, ACTREG); + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + } +err_out: + if (chip_type != IT8718_ID && !nogameport) { + spin_lock_irqsave(&spinlock, flags); + superio_enter(); + superio_select(GAMEPORT); + superio_outb(gpact, ACTREG); + superio_exit(); + spin_unlock_irqrestore(&spinlock, flags); + } + + return rc; +} + +static void __exit it87_wdt_exit(void) +{ + unsigned long flags; + int nolock; + + nolock = !spin_trylock_irqsave(&spinlock, flags); + superio_enter(); + superio_select(GPIO); + superio_outb(0x00, WDTCTRL); + superio_outb(0x00, WDTCFG); + superio_outb(0x00, WDTVALMSB); + superio_outb(0x00, WDTVALLSB); + if (test_bit(WDTS_USE_GP, &wdt_status)) { + superio_select(GAMEPORT); + superio_outb(gpact, ACTREG); + } else { + superio_select(CIR); + superio_outb(ciract, ACTREG); + } + superio_exit(); + if (!nolock) + spin_unlock_irqrestore(&spinlock, flags); + + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); +} + +module_init(it87_wdt_init); +module_exit(it87_wdt_exit); + +MODULE_AUTHOR("Oliver Schuster"); +MODULE_DESCRIPTION("Hardware Watchdog Device Driver for IT87xx EC-LPC I/O"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index 3a11dadfd8e..7bcbb7f4745 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -1,7 +1,7 @@ /* - * linux/drivers/char/watchdog/omap_wdt.c + * omap_wdt.c * - * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog + * Watchdog driver for the TI OMAP 16xx & 24xx/34xx 32KHz (non-secure) watchdog * * Author: MontaVista Software, Inc. * <gdavis@mvista.com> or <source@mvista.com> @@ -47,50 +47,68 @@ #include "omap_wdt.h" +static struct platform_device *omap_wdt_dev; + static unsigned timer_margin; module_param(timer_margin, uint, 0); MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); -static int omap_wdt_users; -static struct clk *armwdt_ck; -static struct clk *mpu_wdt_ick; -static struct clk *mpu_wdt_fck; - static unsigned int wdt_trgr_pattern = 0x1234; static spinlock_t wdt_lock; -static void omap_wdt_ping(void) +struct omap_wdt_dev { + void __iomem *base; /* physical */ + struct device *dev; + int omap_wdt_users; + struct clk *armwdt_ck; + struct clk *mpu_wdt_ick; + struct clk *mpu_wdt_fck; + struct resource *mem; + struct miscdevice omap_wdt_miscdev; +}; + +static void omap_wdt_ping(struct omap_wdt_dev *wdev) { + void __iomem *base = wdev->base; + /* wait for posted write to complete */ - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) cpu_relax(); + wdt_trgr_pattern = ~wdt_trgr_pattern; - omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); + __raw_writel(wdt_trgr_pattern, (base + OMAP_WATCHDOG_TGR)); + /* wait for posted write to complete */ - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) + while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x08) cpu_relax(); /* reloaded WCRR from WLDR */ } -static void omap_wdt_enable(void) +static void omap_wdt_enable(struct omap_wdt_dev *wdev) { + void __iomem *base = wdev->base; + /* Sequence to enable the watchdog */ - omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + __raw_writel(0xBBBB, base + OMAP_WATCHDOG_SPR); + while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10) cpu_relax(); - omap_writel(0x4444, OMAP_WATCHDOG_SPR); - while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) + + __raw_writel(0x4444, base + OMAP_WATCHDOG_SPR); + while ((__raw_readl(base + OMAP_WATCHDOG_WPS)) & 0x10) cpu_relax(); } -static void omap_wdt_disable(void) +static void omap_wdt_disable(struct omap_wdt_dev *wdev) { + void __iomem *base = wdev->base; + /* sequence required to disable watchdog */ - omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + __raw_writel(0xAAAA, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10) cpu_relax(); - omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) + + __raw_writel(0x5555, base + OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x10) cpu_relax(); } @@ -103,83 +121,90 @@ static void omap_wdt_adjust_timeout(unsigned new_timeout) timer_margin = new_timeout; } -static void omap_wdt_set_timeout(void) +static void omap_wdt_set_timeout(struct omap_wdt_dev *wdev) { u32 pre_margin = GET_WLDR_VAL(timer_margin); + void __iomem *base = wdev->base; /* just count up at 32 KHz */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04) cpu_relax(); - omap_writel(pre_margin, OMAP_WATCHDOG_LDR); - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + + __raw_writel(pre_margin, base + OMAP_WATCHDOG_LDR); + while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x04) cpu_relax(); } /* * Allow only one task to hold it open */ - static int omap_wdt_open(struct inode *inode, struct file *file) { - if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) + struct omap_wdt_dev *wdev = platform_get_drvdata(omap_wdt_dev); + void __iomem *base = wdev->base; + + if (test_and_set_bit(1, (unsigned long *)&(wdev->omap_wdt_users))) return -EBUSY; if (cpu_is_omap16xx()) - clk_enable(armwdt_ck); /* Enable the clock */ + clk_enable(wdev->armwdt_ck); /* Enable the clock */ - if (cpu_is_omap24xx()) { - clk_enable(mpu_wdt_ick); /* Enable the interface clock */ - clk_enable(mpu_wdt_fck); /* Enable the functional clock */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + clk_enable(wdev->mpu_wdt_ick); /* Enable the interface clock */ + clk_enable(wdev->mpu_wdt_fck); /* Enable the functional clock */ } /* initialize prescaler */ - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) cpu_relax(); - omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); - while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + + __raw_writel((1 << 5) | (PTV << 2), base + OMAP_WATCHDOG_CNTRL); + while (__raw_readl(base + OMAP_WATCHDOG_WPS) & 0x01) cpu_relax(); - omap_wdt_set_timeout(); - omap_wdt_enable(); + file->private_data = (void *) wdev; + + omap_wdt_set_timeout(wdev); + omap_wdt_enable(wdev); + return nonseekable_open(inode, file); } static int omap_wdt_release(struct inode *inode, struct file *file) { + struct omap_wdt_dev *wdev = file->private_data; + /* * Shut off the timer unless NOWAYOUT is defined. */ #ifndef CONFIG_WATCHDOG_NOWAYOUT - omap_wdt_disable(); - if (cpu_is_omap16xx()) { - clk_disable(armwdt_ck); /* Disable the clock */ - clk_put(armwdt_ck); - armwdt_ck = NULL; - } + omap_wdt_disable(wdev); - if (cpu_is_omap24xx()) { - clk_disable(mpu_wdt_ick); /* Disable the clock */ - clk_disable(mpu_wdt_fck); /* Disable the clock */ - clk_put(mpu_wdt_ick); - clk_put(mpu_wdt_fck); - mpu_wdt_ick = NULL; - mpu_wdt_fck = NULL; + if (cpu_is_omap16xx()) + clk_disable(wdev->armwdt_ck); /* Disable the clock */ + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) { + clk_disable(wdev->mpu_wdt_ick); /* Disable the clock */ + clk_disable(wdev->mpu_wdt_fck); /* Disable the clock */ } #else printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); #endif - omap_wdt_users = 0; + wdev->omap_wdt_users = 0; + return 0; } static ssize_t omap_wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { + struct omap_wdt_dev *wdev = file->private_data; + /* Refresh LOAD_TIME. */ if (len) { spin_lock(&wdt_lock); - omap_wdt_ping(); + omap_wdt_ping(wdev); spin_unlock(&wdt_lock); } return len; @@ -188,6 +213,7 @@ static ssize_t omap_wdt_write(struct file *file, const char __user *data, static long omap_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct omap_wdt_dev *wdev; int new_margin; static const struct watchdog_info ident = { .identity = "OMAP Watchdog", @@ -195,6 +221,8 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd, .firmware_version = 0, }; + wdev = file->private_data; + switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user((struct watchdog_info __user *)arg, &ident, @@ -203,14 +231,14 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd, return put_user(0, (int __user *)arg); case WDIOC_GETBOOTSTATUS: if (cpu_is_omap16xx()) - return put_user(omap_readw(ARM_SYSST), + return put_user(__raw_readw(ARM_SYSST), (int __user *)arg); if (cpu_is_omap24xx()) return put_user(omap_prcm_get_reset_sources(), (int __user *)arg); case WDIOC_KEEPALIVE: spin_lock(&wdt_lock); - omap_wdt_ping(); + omap_wdt_ping(wdev); spin_unlock(&wdt_lock); return 0; case WDIOC_SETTIMEOUT: @@ -219,11 +247,11 @@ static long omap_wdt_ioctl(struct file *file, unsigned int cmd, omap_wdt_adjust_timeout(new_margin); spin_lock(&wdt_lock); - omap_wdt_disable(); - omap_wdt_set_timeout(); - omap_wdt_enable(); + omap_wdt_disable(wdev); + omap_wdt_set_timeout(wdev); + omap_wdt_enable(wdev); - omap_wdt_ping(); + omap_wdt_ping(wdev); spin_unlock(&wdt_lock); /* Fall */ case WDIOC_GETTIMEOUT: @@ -241,96 +269,173 @@ static const struct file_operations omap_wdt_fops = { .release = omap_wdt_release, }; -static struct miscdevice omap_wdt_miscdev = { - .minor = WATCHDOG_MINOR, - .name = "watchdog", - .fops = &omap_wdt_fops, -}; - static int __init omap_wdt_probe(struct platform_device *pdev) { struct resource *res, *mem; + struct omap_wdt_dev *wdev; int ret; /* reserve static register mappings */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENOENT; + if (!res) { + ret = -ENOENT; + goto err_get_resource; + } + + if (omap_wdt_dev) { + ret = -EBUSY; + goto err_busy; + } mem = request_mem_region(res->start, res->end - res->start + 1, pdev->name); - if (mem == NULL) - return -EBUSY; + if (!mem) { + ret = -EBUSY; + goto err_busy; + } - platform_set_drvdata(pdev, mem); + wdev = kzalloc(sizeof(struct omap_wdt_dev), GFP_KERNEL); + if (!wdev) { + ret = -ENOMEM; + goto err_kzalloc; + } - omap_wdt_users = 0; + wdev->omap_wdt_users = 0; + wdev->mem = mem; if (cpu_is_omap16xx()) { - armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); - if (IS_ERR(armwdt_ck)) { - ret = PTR_ERR(armwdt_ck); - armwdt_ck = NULL; - goto fail; + wdev->armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); + if (IS_ERR(wdev->armwdt_ck)) { + ret = PTR_ERR(wdev->armwdt_ck); + wdev->armwdt_ck = NULL; + goto err_clk; } } if (cpu_is_omap24xx()) { - mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); - if (IS_ERR(mpu_wdt_ick)) { - ret = PTR_ERR(mpu_wdt_ick); - mpu_wdt_ick = NULL; - goto fail; + wdev->mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); + if (IS_ERR(wdev->mpu_wdt_ick)) { + ret = PTR_ERR(wdev->mpu_wdt_ick); + wdev->mpu_wdt_ick = NULL; + goto err_clk; } - mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); - if (IS_ERR(mpu_wdt_fck)) { - ret = PTR_ERR(mpu_wdt_fck); - mpu_wdt_fck = NULL; - goto fail; + wdev->mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); + if (IS_ERR(wdev->mpu_wdt_fck)) { + ret = PTR_ERR(wdev->mpu_wdt_fck); + wdev->mpu_wdt_fck = NULL; + goto err_clk; } } - omap_wdt_disable(); + if (cpu_is_omap34xx()) { + wdev->mpu_wdt_ick = clk_get(&pdev->dev, "wdt2_ick"); + if (IS_ERR(wdev->mpu_wdt_ick)) { + ret = PTR_ERR(wdev->mpu_wdt_ick); + wdev->mpu_wdt_ick = NULL; + goto err_clk; + } + wdev->mpu_wdt_fck = clk_get(&pdev->dev, "wdt2_fck"); + if (IS_ERR(wdev->mpu_wdt_fck)) { + ret = PTR_ERR(wdev->mpu_wdt_fck); + wdev->mpu_wdt_fck = NULL; + goto err_clk; + } + } + wdev->base = ioremap(res->start, res->end - res->start + 1); + if (!wdev->base) { + ret = -ENOMEM; + goto err_ioremap; + } + + platform_set_drvdata(pdev, wdev); + + omap_wdt_disable(wdev); omap_wdt_adjust_timeout(timer_margin); - omap_wdt_miscdev.parent = &pdev->dev; - ret = misc_register(&omap_wdt_miscdev); + wdev->omap_wdt_miscdev.parent = &pdev->dev; + wdev->omap_wdt_miscdev.minor = WATCHDOG_MINOR; + wdev->omap_wdt_miscdev.name = "watchdog"; + wdev->omap_wdt_miscdev.fops = &omap_wdt_fops; + + ret = misc_register(&(wdev->omap_wdt_miscdev)); if (ret) - goto fail; + goto err_misc; - pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); + pr_info("OMAP Watchdog Timer Rev 0x%02x: initial timeout %d sec\n", + __raw_readl(wdev->base + OMAP_WATCHDOG_REV) & 0xFF, + timer_margin); /* autogate OCP interface clock */ - omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); + __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); + + omap_wdt_dev = pdev; + return 0; -fail: - if (armwdt_ck) - clk_put(armwdt_ck); - if (mpu_wdt_ick) - clk_put(mpu_wdt_ick); - if (mpu_wdt_fck) - clk_put(mpu_wdt_fck); - release_resource(mem); +err_misc: + platform_set_drvdata(pdev, NULL); + iounmap(wdev->base); + +err_ioremap: + wdev->base = NULL; + +err_clk: + if (wdev->armwdt_ck) + clk_put(wdev->armwdt_ck); + if (wdev->mpu_wdt_ick) + clk_put(wdev->mpu_wdt_ick); + if (wdev->mpu_wdt_fck) + clk_put(wdev->mpu_wdt_fck); + kfree(wdev); + +err_kzalloc: + release_mem_region(res->start, res->end - res->start + 1); + +err_busy: +err_get_resource: + return ret; } static void omap_wdt_shutdown(struct platform_device *pdev) { - omap_wdt_disable(); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); + + if (wdev->omap_wdt_users) + omap_wdt_disable(wdev); } static int omap_wdt_remove(struct platform_device *pdev) { - struct resource *mem = platform_get_drvdata(pdev); - misc_deregister(&omap_wdt_miscdev); - release_resource(mem); - if (armwdt_ck) - clk_put(armwdt_ck); - if (mpu_wdt_ick) - clk_put(mpu_wdt_ick); - if (mpu_wdt_fck) - clk_put(mpu_wdt_fck); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) + return -ENOENT; + + misc_deregister(&(wdev->omap_wdt_miscdev)); + release_mem_region(res->start, res->end - res->start + 1); + platform_set_drvdata(pdev, NULL); + + if (wdev->armwdt_ck) { + clk_put(wdev->armwdt_ck); + wdev->armwdt_ck = NULL; + } + + if (wdev->mpu_wdt_ick) { + clk_put(wdev->mpu_wdt_ick); + wdev->mpu_wdt_ick = NULL; + } + + if (wdev->mpu_wdt_fck) { + clk_put(wdev->mpu_wdt_fck); + wdev->mpu_wdt_fck = NULL; + } + iounmap(wdev->base); + + kfree(wdev); + omap_wdt_dev = NULL; + return 0; } @@ -344,17 +449,23 @@ static int omap_wdt_remove(struct platform_device *pdev) static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) { - if (omap_wdt_users) - omap_wdt_disable(); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); + + if (wdev->omap_wdt_users) + omap_wdt_disable(wdev); + return 0; } static int omap_wdt_resume(struct platform_device *pdev) { - if (omap_wdt_users) { - omap_wdt_enable(); - omap_wdt_ping(); + struct omap_wdt_dev *wdev = platform_get_drvdata(pdev); + + if (wdev->omap_wdt_users) { + omap_wdt_enable(wdev); + omap_wdt_ping(wdev); } + return 0; } diff --git a/drivers/watchdog/omap_wdt.h b/drivers/watchdog/omap_wdt.h index 52a532a5114..fc02ec6a038 100644 --- a/drivers/watchdog/omap_wdt.h +++ b/drivers/watchdog/omap_wdt.h @@ -30,25 +30,15 @@ #ifndef _OMAP_WATCHDOG_H #define _OMAP_WATCHDOG_H -#define OMAP1610_WATCHDOG_BASE 0xfffeb000 -#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ - -#ifdef CONFIG_ARCH_OMAP24XX -#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE -#else -#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE -#define RM_RSTST_WKUP 0 -#endif - -#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) -#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) -#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) -#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) -#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) -#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) -#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) -#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) -#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) +#define OMAP_WATCHDOG_REV (0x00) +#define OMAP_WATCHDOG_SYS_CONFIG (0x10) +#define OMAP_WATCHDOG_STATUS (0x14) +#define OMAP_WATCHDOG_CNTRL (0x24) +#define OMAP_WATCHDOG_CRR (0x28) +#define OMAP_WATCHDOG_LDR (0x2c) +#define OMAP_WATCHDOG_TGR (0x30) +#define OMAP_WATCHDOG_WPS (0x34) +#define OMAP_WATCHDOG_SPR (0x48) /* Using the prescaler, the OMAP watchdog could go for many * months before firing. These limits work without scaling, diff --git a/drivers/watchdog/orion5x_wdt.c b/drivers/watchdog/orion5x_wdt.c new file mode 100644 index 00000000000..14a339f58b6 --- /dev/null +++ b/drivers/watchdog/orion5x_wdt.c @@ -0,0 +1,245 @@ +/* + * drivers/watchdog/orion5x_wdt.c + * + * Watchdog driver for Orion5x processors + * + * Author: Sylver Bruneau <sylver.bruneau@googlemail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/init.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/spinlock.h> + +/* + * Watchdog timer block registers. + */ +#define TIMER_CTRL (TIMER_VIRT_BASE + 0x0000) +#define WDT_EN 0x0010 +#define WDT_VAL (TIMER_VIRT_BASE + 0x0024) + +#define WDT_MAX_DURATION (0xffffffff / ORION5X_TCLK) +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +static int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = WDT_MAX_DURATION; /* (seconds) */ +static unsigned long wdt_status; +static spinlock_t wdt_lock; + +static void wdt_enable(void) +{ + u32 reg; + + spin_lock(&wdt_lock); + + /* Set watchdog duration */ + writel(ORION5X_TCLK * heartbeat, WDT_VAL); + + /* Clear watchdog timer interrupt */ + reg = readl(BRIDGE_CAUSE); + reg &= ~WDT_INT_REQ; + writel(reg, BRIDGE_CAUSE); + + /* Enable watchdog timer */ + reg = readl(TIMER_CTRL); + reg |= WDT_EN; + writel(reg, TIMER_CTRL); + + /* Enable reset on watchdog */ + reg = readl(CPU_RESET_MASK); + reg |= WDT_RESET; + writel(reg, CPU_RESET_MASK); + + spin_unlock(&wdt_lock); +} + +static void wdt_disable(void) +{ + u32 reg; + + spin_lock(&wdt_lock); + + /* Disable reset on watchdog */ + reg = readl(CPU_RESET_MASK); + reg &= ~WDT_RESET; + writel(reg, CPU_RESET_MASK); + + /* Disable watchdog timer */ + reg = readl(TIMER_CTRL); + reg &= ~WDT_EN; + writel(reg, TIMER_CTRL); + + spin_unlock(&wdt_lock); +} + +static int orion5x_wdt_get_timeleft(int *time_left) +{ + spin_lock(&wdt_lock); + *time_left = readl(WDT_VAL) / ORION5X_TCLK; + spin_unlock(&wdt_lock); + return 0; +} + +static int orion5x_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + wdt_enable(); + return nonseekable_open(inode, file); +} + +static ssize_t orion5x_wdt_write(struct file *file, const char *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_enable(); + } + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "Orion5x Watchdog", +}; + + +static long orion5x_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + int time; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + ret = put_user(0, (int *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_enable(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, (int *)arg); + if (ret) + break; + + if (time <= 0 || time > WDT_MAX_DURATION) { + ret = -EINVAL; + break; + } + heartbeat = time; + wdt_enable(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, (int *)arg); + break; + + case WDIOC_GETTIMELEFT: + if (orion5x_wdt_get_timeleft(&time)) { + ret = -EINVAL; + break; + } + ret = put_user(time, (int *)arg); + break; + } + return ret; +} + +static int orion5x_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + wdt_disable(); + else + printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " + "timer will not stop\n"); + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + + +static const struct file_operations orion5x_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = orion5x_wdt_write, + .unlocked_ioctl = orion5x_wdt_ioctl, + .open = orion5x_wdt_open, + .release = orion5x_wdt_release, +}; + +static struct miscdevice orion5x_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &orion5x_wdt_fops, +}; + +static int __init orion5x_wdt_init(void) +{ + int ret; + + spin_lock_init(&wdt_lock); + + ret = misc_register(&orion5x_wdt_miscdev); + if (ret == 0) + printk("Orion5x Watchdog Timer: heartbeat %d sec\n", + heartbeat); + + return ret; +} + +static void __exit orion5x_wdt_exit(void) +{ + misc_deregister(&orion5x_wdt_miscdev); +} + +module_init(orion5x_wdt_init); +module_exit(orion5x_wdt_exit); + +MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>"); +MODULE_DESCRIPTION("Orion5x Processor Watchdog"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default is " + __MODULE_STRING(WDT_MAX_DURATION) ")"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/sbus/char/riowatchdog.c b/drivers/watchdog/riowd.c index 88c0fc6395e..09cb1833ea2 100644 --- a/drivers/sbus/char/riowatchdog.c +++ b/drivers/watchdog/riowd.c @@ -1,7 +1,6 @@ -/* $Id: riowatchdog.c,v 1.3.2.2 2002/01/23 18:48:02 davem Exp $ - * riowatchdog.c - driver for hw watchdog inside Super I/O of RIO +/* riowd.c - driver for hw watchdog inside Super I/O of RIO * - * Copyright (C) 2001 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) */ #include <linux/kernel.h> @@ -12,14 +11,13 @@ #include <linux/init.h> #include <linux/miscdevice.h> #include <linux/smp_lock.h> +#include <linux/watchdog.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <asm/io.h> -#include <asm/ebus.h> -#include <asm/bbc.h> -#include <asm/oplib.h> #include <asm/uaccess.h> -#include <asm/watchdog.h> /* RIO uses the NatSemi Super I/O power management logical device * as its' watchdog. @@ -45,74 +43,35 @@ * The watchdog device generates no interrupts. */ -MODULE_AUTHOR("David S. Miller <davem@redhat.com>"); +MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO"); MODULE_SUPPORTED_DEVICE("watchdog"); MODULE_LICENSE("GPL"); -#define RIOWD_NAME "pmc" -#define RIOWD_MINOR 215 +#define DRIVER_NAME "riowd" +#define PFX DRIVER_NAME ": " -static DEFINE_SPINLOCK(riowd_lock); +struct riowd { + void __iomem *regs; + spinlock_t lock; +}; + +static struct riowd *riowd_device; -static void __iomem *bbc_regs; -static void __iomem *riowd_regs; #define WDTO_INDEX 0x05 static int riowd_timeout = 1; /* in minutes */ module_param(riowd_timeout, int, 0); MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes"); -#if 0 /* Currently unused. */ -static u8 riowd_readreg(int index) +static void riowd_writereg(struct riowd *p, u8 val, int index) { unsigned long flags; - u8 ret; - spin_lock_irqsave(&riowd_lock, flags); - writeb(index, riowd_regs + 0); - ret = readb(riowd_regs + 1); - spin_unlock_irqrestore(&riowd_lock, flags); - - return ret; -} -#endif - -static void riowd_writereg(u8 val, int index) -{ - unsigned long flags; - - spin_lock_irqsave(&riowd_lock, flags); - writeb(index, riowd_regs + 0); - writeb(val, riowd_regs + 1); - spin_unlock_irqrestore(&riowd_lock, flags); -} - -static void riowd_pingtimer(void) -{ - riowd_writereg(riowd_timeout, WDTO_INDEX); -} - -static void riowd_stoptimer(void) -{ - u8 val; - - riowd_writereg(0, WDTO_INDEX); - - val = readb(bbc_regs + BBC_WDACTION); - val &= ~BBC_WDACTION_RST; - writeb(val, bbc_regs + BBC_WDACTION); -} - -static void riowd_starttimer(void) -{ - u8 val; - - riowd_writereg(riowd_timeout, WDTO_INDEX); - - val = readb(bbc_regs + BBC_WDACTION); - val |= BBC_WDACTION_RST; - writeb(val, bbc_regs + BBC_WDACTION); + spin_lock_irqsave(&p->lock, flags); + writeb(index, p->regs + 0); + writeb(val, p->regs + 1); + spin_unlock_irqrestore(&p->lock, flags); } static int riowd_open(struct inode *inode, struct file *filp) @@ -131,9 +90,12 @@ static int riowd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { static struct watchdog_info info = { - WDIOF_SETTIMEOUT, 0, "Natl. Semiconductor PC97317" + .options = WDIOF_SETTIMEOUT, + .firmware_version = 1, + .identity = DRIVER_NAME, }; void __user *argp = (void __user *)arg; + struct riowd *p = riowd_device; unsigned int options; int new_margin; @@ -150,7 +112,7 @@ static int riowd_ioctl(struct inode *inode, struct file *filp, break; case WDIOC_KEEPALIVE: - riowd_pingtimer(); + riowd_writereg(p, riowd_timeout, WDTO_INDEX); break; case WDIOC_SETOPTIONS: @@ -158,9 +120,9 @@ static int riowd_ioctl(struct inode *inode, struct file *filp, return -EFAULT; if (options & WDIOS_DISABLECARD) - riowd_stoptimer(); + riowd_writereg(p, 0, WDTO_INDEX); else if (options & WDIOS_ENABLECARD) - riowd_starttimer(); + riowd_writereg(p, riowd_timeout, WDTO_INDEX); else return -EINVAL; @@ -170,9 +132,9 @@ static int riowd_ioctl(struct inode *inode, struct file *filp, if (get_user(new_margin, (int __user *)argp)) return -EFAULT; if ((new_margin < 60) || (new_margin > (255 * 60))) - return -EINVAL; + return -EINVAL; riowd_timeout = (new_margin + 59) / 60; - riowd_pingtimer(); + riowd_writereg(p, riowd_timeout, WDTO_INDEX); /* Fall */ case WDIOC_GETTIMEOUT: @@ -187,8 +149,10 @@ static int riowd_ioctl(struct inode *inode, struct file *filp, static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct riowd *p = riowd_device; + if (count) { - riowd_pingtimer(); + riowd_writereg(p, riowd_timeout, WDTO_INDEX); return 1; } @@ -197,99 +161,99 @@ static ssize_t riowd_write(struct file *file, const char __user *buf, size_t cou static const struct file_operations riowd_fops = { .owner = THIS_MODULE, + .llseek = no_llseek, .ioctl = riowd_ioctl, .open = riowd_open, .write = riowd_write, .release = riowd_release, }; -static struct miscdevice riowd_miscdev = { RIOWD_MINOR, RIOWD_NAME, &riowd_fops }; +static struct miscdevice riowd_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &riowd_fops +}; -static int __init riowd_bbc_init(void) +static int __devinit riowd_probe(struct of_device *op, + const struct of_device_id *match) { - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; - u8 val; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->ofdev.node->name, "bbc")) - goto found_bbc; - } - } + struct riowd *p; + int err = -EINVAL; -found_bbc: - if (!edev) - return -ENODEV; - bbc_regs = ioremap(edev->resource[0].start, BBC_REGS_SIZE); - if (!bbc_regs) - return -ENODEV; + if (riowd_device) + goto out; - /* Turn it off. */ - val = readb(bbc_regs + BBC_WDACTION); - val &= ~BBC_WDACTION_RST; - writeb(val, bbc_regs + BBC_WDACTION); + err = -ENOMEM; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + goto out; - return 0; -} + spin_lock_init(&p->lock); -static int __init riowd_init(void) -{ - struct linux_ebus *ebus = NULL; - struct linux_ebus_device *edev = NULL; - - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - if (!strcmp(edev->ofdev.node->name, RIOWD_NAME)) - goto ebus_done; - } + p->regs = of_ioremap(&op->resource[0], 0, 2, DRIVER_NAME); + if (!p->regs) { + printk(KERN_ERR PFX "Cannot map registers.\n"); + goto out_free; } -ebus_done: - if (!edev) - goto fail; - - riowd_regs = ioremap(edev->resource[0].start, 2); - if (riowd_regs == NULL) { - printk(KERN_ERR "pmc: Cannot map registers.\n"); - return -ENODEV; + err = misc_register(&riowd_miscdev); + if (err) { + printk(KERN_ERR PFX "Cannot register watchdog misc device.\n"); + goto out_iounmap; } - if (riowd_bbc_init()) { - printk(KERN_ERR "pmc: Failure initializing BBC config.\n"); - goto fail; - } + printk(KERN_INFO PFX "Hardware watchdog [%i minutes], " + "regs at %p\n", riowd_timeout, p->regs); - if (misc_register(&riowd_miscdev)) { - printk(KERN_ERR "pmc: Cannot register watchdog misc device.\n"); - goto fail; - } + dev_set_drvdata(&op->dev, p); + riowd_device = p; + err = 0; - printk(KERN_INFO "pmc: Hardware watchdog [%i minutes], " - "regs at %p\n", riowd_timeout, riowd_regs); +out_iounmap: + of_iounmap(&op->resource[0], p->regs, 2); - return 0; +out_free: + kfree(p); -fail: - if (riowd_regs) { - iounmap(riowd_regs); - riowd_regs = NULL; - } - if (bbc_regs) { - iounmap(bbc_regs); - bbc_regs = NULL; - } - return -ENODEV; +out: + return err; } -static void __exit riowd_cleanup(void) +static int __devexit riowd_remove(struct of_device *op) { + struct riowd *p = dev_get_drvdata(&op->dev); + misc_deregister(&riowd_miscdev); - iounmap(riowd_regs); - riowd_regs = NULL; - iounmap(bbc_regs); - bbc_regs = NULL; + of_iounmap(&op->resource[0], p->regs, 2); + kfree(p); + + return 0; +} + +static const struct of_device_id riowd_match[] = { + { + .name = "pmc", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, riowd_match); + +static struct of_platform_driver riowd_driver = { + .name = DRIVER_NAME, + .match_table = riowd_match, + .probe = riowd_probe, + .remove = __devexit_p(riowd_remove), +}; + +static int __init riowd_init(void) +{ + return of_register_driver(&riowd_driver, &of_bus_type); +} + +static void __exit riowd_exit(void) +{ + of_unregister_driver(&riowd_driver); } module_init(riowd_init); -module_exit(riowd_cleanup); +module_exit(riowd_exit); diff --git a/drivers/watchdog/w83697ug_wdt.c b/drivers/watchdog/w83697ug_wdt.c new file mode 100644 index 00000000000..c73b5e2919c --- /dev/null +++ b/drivers/watchdog/w83697ug_wdt.c @@ -0,0 +1,392 @@ +/* + * w83697ug/uf WDT driver + * + * (c) Copyright 2008 Flemming Fransen <ff@nrvissing.net> + * reused original code to supoprt w83697ug/uf. + * + * Based on w83627hf_wdt.c which is based on advantechwdt.c + * which is based on wdt.c. + * Original copyright messages: + * + * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> + * added support for W83627THF. + * + * (c) Copyright 2003 Pádraig Brady <P@draigBrady.com> + * + * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl> + * + * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved. + * http://www.redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide + * warranty for any of this software. This material is provided + * "AS-IS" and at no charge. + * + * (c) Copyright 1995 Alan Cox <alan@redhat.com> + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/fs.h> +#include <linux/ioport.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/uaccess.h> + +#include <asm/system.h> + +#define WATCHDOG_NAME "w83697ug/uf WDT" +#define PFX WATCHDOG_NAME ": " +#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ + +static unsigned long wdt_is_open; +static char expect_close; +static DEFINE_SPINLOCK(io_lock); + +static int wdt_io = 0x2e; +module_param(wdt_io, int, 0); +MODULE_PARM_DESC(wdt_io, "w83697ug/uf WDT io port (default 0x2e)"); + +static int timeout = WATCHDOG_TIMEOUT; /* in seconds */ +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, + "Watchdog timeout in seconds. 1<= timeout <=255 (default=" + __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +/* + * Kernel methods. + */ + +#define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ +#define WDT_EFIR (wdt_io+0) /* Extended Function Index Register + (same as EFER) */ +#define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ + +static void w83697ug_select_wd_register(void) +{ + unsigned char c; + unsigned char version; + + outb_p(0x87, WDT_EFER); /* Enter extended function mode */ + outb_p(0x87, WDT_EFER); /* Again according to manual */ + + outb(0x20, WDT_EFER); /* check chip version */ + version = inb(WDT_EFDR); + + if (version == 0x68) { /* W83697UG */ + printk(KERN_INFO PFX "Watchdog chip version 0x%02x = " + "W83697UG/UF found at 0x%04x\n", version, wdt_io); + + outb_p(0x2b, WDT_EFER); + c = inb_p(WDT_EFDR); /* select WDT0 */ + c &= ~0x04; + outb_p(0x2b, WDT_EFER); + outb_p(c, WDT_EFDR); /* set pin118 to WDT0 */ + + } else { + printk(KERN_ERR PFX "No W83697UG/UF could be found\n"); + return -EIO; + } + + outb_p(0x07, WDT_EFER); /* point to logical device number reg */ + outb_p(0x08, WDT_EFDR); /* select logical device 8 (GPIO2) */ + outb_p(0x30, WDT_EFER); /* select CR30 */ + c = inb_p(WDT_EFDR); + outb_p(c || 0x01, WDT_EFDR); /* set bit 0 to activate GPIO2 */ +} + +static void w83697ug_unselect_wd_register(void) +{ + outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ +} + +static void w83697ug_init(void) +{ + unsigned char t; + + w83697ug_select_wd_register(); + + outb_p(0xF6, WDT_EFER); /* Select CRF6 */ + t = inb_p(WDT_EFDR); /* read CRF6 */ + if (t != 0) { + printk(KERN_INFO PFX "Watchdog already running." + " Resetting timeout to %d sec\n", timeout); + outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */ + } + outb_p(0xF5, WDT_EFER); /* Select CRF5 */ + t = inb_p(WDT_EFDR); /* read CRF5 */ + t &= ~0x0C; /* set second mode & + disable keyboard turning off watchdog */ + outb_p(t, WDT_EFDR); /* Write back to CRF5 */ + + w83697ug_unselect_wd_register(); +} + +static void wdt_ctrl(int timeout) +{ + spin_lock(&io_lock); + + w83697ug_select_wd_register(); + + outb_p(0xF4, WDT_EFER); /* Select CRF4 */ + outb_p(timeout, WDT_EFDR); /* Write Timeout counter to CRF4 */ + + w83697ug_unselect_wd_register(); + + spin_unlock(&io_lock); +} + +static int wdt_ping(void) +{ + wdt_ctrl(timeout); + return 0; +} + +static int wdt_disable(void) +{ + wdt_ctrl(0); + return 0; +} + +static int wdt_set_heartbeat(int t) +{ + if (t < 1 || t > 255) + return -EINVAL; + + timeout = t; + return 0; +} + +static ssize_t wdt_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count) { + if (!nowayout) { + size_t i; + + expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + expect_close = 42; + } + } + wdt_ping(); + } + return count; +} + +static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_timeout; + static const struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT | + WDIOF_MAGICCLOSE, + .firmware_version = 1, + .identity = "W83697UG WDT", + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &ident, sizeof(ident))) + return -EFAULT; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, p)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + wdt_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + wdt_ping(); + retval = 0; + } + + return retval; + } + + case WDIOC_KEEPALIVE: + wdt_ping(); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, p)) + return -EFAULT; + if (wdt_set_heartbeat(new_timeout)) + return -EINVAL; + wdt_ping(); + /* Fall */ + + case WDIOC_GETTIMEOUT: + return put_user(timeout, p); + + default: + return -ENOTTY; + } + return 0; +} + +static int wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* + * Activate + */ + + wdt_ping(); + return nonseekable_open(inode, file); +} + +static int wdt_close(struct inode *inode, struct file *file) +{ + if (expect_close == 42) + wdt_disable(); + else { + printk(KERN_CRIT PFX + "Unexpected close, not stopping watchdog!\n"); + wdt_ping(); + } + expect_close = 0; + clear_bit(0, &wdt_is_open); + return 0; +} + +/* + * Notifier for system down + */ + +static int wdt_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + wdt_disable(); /* Turn the WDT off */ + + return NOTIFY_DONE; +} + +/* + * Kernel Interfaces + */ + +static const struct file_operations wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdt_write, + .unlocked_ioctl = wdt_ioctl, + .open = wdt_open, + .release = wdt_close, +}; + +static struct miscdevice wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &wdt_fops, +}; + +/* + * The WDT needs to learn about soft shutdowns in order to + * turn the timebomb registers off. + */ + +static struct notifier_block wdt_notifier = { + .notifier_call = wdt_notify_sys, +}; + +static int __init wdt_init(void) +{ + int ret; + + printk(KERN_INFO "WDT driver for the Winbond(TM) W83697UG/UF Super I/O chip initialising.\n"); + + if (wdt_set_heartbeat(timeout)) { + wdt_set_heartbeat(WATCHDOG_TIMEOUT); + printk(KERN_INFO PFX + "timeout value must be 1<=timeout<=255, using %d\n", + WATCHDOG_TIMEOUT); + } + + if (!request_region(wdt_io, 1, WATCHDOG_NAME)) { + printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", + wdt_io); + ret = -EIO; + goto out; + } + + w83697ug_init(); + + ret = register_reboot_notifier(&wdt_notifier); + if (ret != 0) { + printk(KERN_ERR PFX + "cannot register reboot notifier (err=%d)\n", ret); + goto unreg_regions; + } + + ret = misc_register(&wdt_miscdev); + if (ret != 0) { + printk(KERN_ERR PFX + "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + goto unreg_reboot; + } + + printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n", + timeout, nowayout); + +out: + return ret; +unreg_reboot: + unregister_reboot_notifier(&wdt_notifier); +unreg_regions: + release_region(wdt_io, 1); + goto out; +} + +static void __exit wdt_exit(void) +{ + misc_deregister(&wdt_miscdev); + unregister_reboot_notifier(&wdt_notifier); + release_region(wdt_io, 1); +} + +module_init(wdt_init); +module_exit(wdt_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Flemming Frandsen <ff@nrvissing.net>"); +MODULE_DESCRIPTION("w83697ug/uf WDT driver"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 363286c5429..d2a8fdf0e19 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -1,4 +1,5 @@ obj-y += grant-table.o features.o events.o manage.o obj-y += xenbus/ +obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o obj-$(CONFIG_XEN_XENCOMM) += xencomm.o obj-$(CONFIG_XEN_BALLOON) += balloon.o diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 2e15da5459c..8c83abc7340 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -53,7 +53,6 @@ #include <asm/tlb.h> #include <xen/interface/memory.h> -#include <xen/balloon.h> #include <xen/xenbus.h> #include <xen/features.h> #include <xen/page.h> @@ -226,9 +225,8 @@ static int increase_reservation(unsigned long nr_pages) } set_xen_guest_handle(reservation.extent_start, frame_list); - reservation.nr_extents = nr_pages; - rc = HYPERVISOR_memory_op( - XENMEM_populate_physmap, &reservation); + reservation.nr_extents = nr_pages; + rc = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); if (rc < nr_pages) { if (rc > 0) { int ret; @@ -236,7 +234,7 @@ static int increase_reservation(unsigned long nr_pages) /* We hit the Xen hard limit: reprobe. */ reservation.nr_extents = rc; ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, - &reservation); + &reservation); BUG_ON(ret != rc); } if (rc >= 0) @@ -420,7 +418,7 @@ static int __init balloon_init(void) unsigned long pfn; struct page *page; - if (!is_running_on_xen()) + if (!xen_pv_domain()) return -ENODEV; pr_info("xen_balloon: Initialising balloon driver.\n"); @@ -464,136 +462,13 @@ static void balloon_exit(void) module_exit(balloon_exit); -static void balloon_update_driver_allowance(long delta) -{ - unsigned long flags; - - spin_lock_irqsave(&balloon_lock, flags); - balloon_stats.driver_pages += delta; - spin_unlock_irqrestore(&balloon_lock, flags); -} - -static int dealloc_pte_fn( - pte_t *pte, struct page *pmd_page, unsigned long addr, void *data) -{ - unsigned long mfn = pte_mfn(*pte); - int ret; - struct xen_memory_reservation reservation = { - .nr_extents = 1, - .extent_order = 0, - .domid = DOMID_SELF - }; - set_xen_guest_handle(reservation.extent_start, &mfn); - set_pte_at(&init_mm, addr, pte, __pte_ma(0ull)); - set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY); - ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); - BUG_ON(ret != 1); - return 0; -} - -static struct page **alloc_empty_pages_and_pagevec(int nr_pages) -{ - unsigned long vaddr, flags; - struct page *page, **pagevec; - int i, ret; - - pagevec = kmalloc(sizeof(page) * nr_pages, GFP_KERNEL); - if (pagevec == NULL) - return NULL; - - for (i = 0; i < nr_pages; i++) { - page = pagevec[i] = alloc_page(GFP_KERNEL); - if (page == NULL) - goto err; - - vaddr = (unsigned long)page_address(page); - - scrub_page(page); - - spin_lock_irqsave(&balloon_lock, flags); - - if (xen_feature(XENFEAT_auto_translated_physmap)) { - unsigned long gmfn = page_to_pfn(page); - struct xen_memory_reservation reservation = { - .nr_extents = 1, - .extent_order = 0, - .domid = DOMID_SELF - }; - set_xen_guest_handle(reservation.extent_start, &gmfn); - ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, - &reservation); - if (ret == 1) - ret = 0; /* success */ - } else { - ret = apply_to_page_range(&init_mm, vaddr, PAGE_SIZE, - dealloc_pte_fn, NULL); - } - - if (ret != 0) { - spin_unlock_irqrestore(&balloon_lock, flags); - __free_page(page); - goto err; - } - - totalram_pages = --balloon_stats.current_pages; - - spin_unlock_irqrestore(&balloon_lock, flags); - } - - out: - schedule_work(&balloon_worker); - flush_tlb_all(); - return pagevec; - - err: - spin_lock_irqsave(&balloon_lock, flags); - while (--i >= 0) - balloon_append(pagevec[i]); - spin_unlock_irqrestore(&balloon_lock, flags); - kfree(pagevec); - pagevec = NULL; - goto out; -} - -static void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages) -{ - unsigned long flags; - int i; - - if (pagevec == NULL) - return; - - spin_lock_irqsave(&balloon_lock, flags); - for (i = 0; i < nr_pages; i++) { - BUG_ON(page_count(pagevec[i]) != 1); - balloon_append(pagevec[i]); - } - spin_unlock_irqrestore(&balloon_lock, flags); - - kfree(pagevec); - - schedule_work(&balloon_worker); -} - -static void balloon_release_driver_page(struct page *page) -{ - unsigned long flags; - - spin_lock_irqsave(&balloon_lock, flags); - balloon_append(page); - balloon_stats.driver_pages--; - spin_unlock_irqrestore(&balloon_lock, flags); - - schedule_work(&balloon_worker); -} - - -#define BALLOON_SHOW(name, format, args...) \ - static ssize_t show_##name(struct sys_device *dev, \ - char *buf) \ - { \ - return sprintf(buf, format, ##args); \ - } \ +#define BALLOON_SHOW(name, format, args...) \ + static ssize_t show_##name(struct sys_device *dev, \ + struct sysdev_attribute *attr, \ + char *buf) \ + { \ + return sprintf(buf, format, ##args); \ + } \ static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) BALLOON_SHOW(current_kb, "%lu\n", PAGES2KB(balloon_stats.current_pages)); @@ -604,7 +479,8 @@ BALLOON_SHOW(hard_limit_kb, (balloon_stats.hard_limit!=~0UL) ? PAGES2KB(balloon_stats.hard_limit) : 0); BALLOON_SHOW(driver_kb, "%lu\n", PAGES2KB(balloon_stats.driver_pages)); -static ssize_t show_target_kb(struct sys_device *dev, char *buf) +static ssize_t show_target_kb(struct sys_device *dev, struct sysdev_attribute *attr, + char *buf) { return sprintf(buf, "%lu\n", PAGES2KB(balloon_stats.target_pages)); } @@ -614,19 +490,14 @@ static ssize_t store_target_kb(struct sys_device *dev, const char *buf, size_t count) { - char memstring[64], *endchar; + char *endchar; unsigned long long target_bytes; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (count <= 1) - return -EBADMSG; /* runt */ - if (count > sizeof(memstring)) - return -EFBIG; /* too long */ - strcpy(memstring, buf); + target_bytes = memparse(buf, &endchar); - target_bytes = memparse(memstring, &endchar); balloon_set_new_target(target_bytes >> PAGE_SHIFT); return count; @@ -694,20 +565,4 @@ static int register_balloon(struct sys_device *sysdev) return error; } -static void unregister_balloon(struct sys_device *sysdev) -{ - int i; - - sysfs_remove_group(&sysdev->kobj, &balloon_info_group); - for (i = 0; i < ARRAY_SIZE(balloon_attrs); i++) - sysdev_remove_file(sysdev, balloon_attrs[i]); - sysdev_unregister(sysdev); - sysdev_class_unregister(&balloon_sysdev_class); -} - -static void balloon_sysfs_exit(void) -{ - unregister_balloon(&balloon_sysdev); -} - MODULE_LICENSE("GPL"); diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c new file mode 100644 index 00000000000..565280ec1c6 --- /dev/null +++ b/drivers/xen/cpu_hotplug.c @@ -0,0 +1,90 @@ +#include <linux/notifier.h> + +#include <xen/xenbus.h> + +#include <asm-x86/xen/hypervisor.h> +#include <asm/cpu.h> + +static void enable_hotplug_cpu(int cpu) +{ + if (!cpu_present(cpu)) + arch_register_cpu(cpu); + + cpu_set(cpu, cpu_present_map); +} + +static void disable_hotplug_cpu(int cpu) +{ + if (cpu_present(cpu)) + arch_unregister_cpu(cpu); + + cpu_clear(cpu, cpu_present_map); +} + +static void vcpu_hotplug(unsigned int cpu) +{ + int err; + char dir[32], state[32]; + + if (!cpu_possible(cpu)) + return; + + sprintf(dir, "cpu/%u", cpu); + err = xenbus_scanf(XBT_NIL, dir, "availability", "%s", state); + if (err != 1) { + printk(KERN_ERR "XENBUS: Unable to read cpu state\n"); + return; + } + + if (strcmp(state, "online") == 0) { + enable_hotplug_cpu(cpu); + } else if (strcmp(state, "offline") == 0) { + (void)cpu_down(cpu); + disable_hotplug_cpu(cpu); + } else { + printk(KERN_ERR "XENBUS: unknown state(%s) on CPU%d\n", + state, cpu); + } +} + +static void handle_vcpu_hotplug_event(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + unsigned int cpu; + char *cpustr; + const char *node = vec[XS_WATCH_PATH]; + + cpustr = strstr(node, "cpu/"); + if (cpustr != NULL) { + sscanf(cpustr, "cpu/%u", &cpu); + vcpu_hotplug(cpu); + } +} + +static int setup_cpu_watcher(struct notifier_block *notifier, + unsigned long event, void *data) +{ + static struct xenbus_watch cpu_watch = { + .node = "cpu", + .callback = handle_vcpu_hotplug_event}; + + (void)register_xenbus_watch(&cpu_watch); + + return NOTIFY_DONE; +} + +static int __init setup_vcpu_hotplug_event(void) +{ + static struct notifier_block xsn_cpu = { + .notifier_call = setup_cpu_watcher }; + + if (!xen_pv_domain()) + return -ENODEV; + + register_xenstore_notifier(&xsn_cpu); + + return 0; +} + +arch_initcall(setup_vcpu_hotplug_event); + diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 0e0c28574af..c3290bc186a 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -84,17 +84,6 @@ static int irq_bindcount[NR_IRQS]; /* Xen will never allocate port zero for any purpose. */ #define VALID_EVTCHN(chn) ((chn) != 0) -/* - * Force a proper event-channel callback from Xen after clearing the - * callback mask. We do this in a very simple manner, by making a call - * down into Xen. The pending flag will be checked by Xen on return. - */ -void force_evtchn_callback(void) -{ - (void)HYPERVISOR_xen_version(0, NULL); -} -EXPORT_SYMBOL_GPL(force_evtchn_callback); - static struct irq_chip xen_dynamic_chip; /* Constructor for packed IRQ information. */ @@ -175,6 +164,12 @@ static inline void set_evtchn(int port) sync_set_bit(port, &s->evtchn_pending[0]); } +static inline int test_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + return sync_test_bit(port, &s->evtchn_pending[0]); +} + /** * notify_remote_via_irq - send event to remote end of event channel via irq @@ -365,6 +360,10 @@ static void unbind_from_irq(unsigned int irq) per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) [index_from_irq(irq)] = -1; break; + case IRQT_IPI: + per_cpu(ipi_to_irq, cpu_from_evtchn(evtchn)) + [index_from_irq(irq)] = -1; + break; default: break; } @@ -743,6 +742,25 @@ void xen_clear_irq_pending(int irq) clear_evtchn(evtchn); } +void xen_set_irq_pending(int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + set_evtchn(evtchn); +} + +bool xen_test_irq_pending(int irq) +{ + int evtchn = evtchn_from_irq(irq); + bool ret = false; + + if (VALID_EVTCHN(evtchn)) + ret = test_evtchn(evtchn); + + return ret; +} + /* Poll waiting for an irq to become pending. In the usual case, the irq will be disabled so it won't deliver an interrupt. */ void xen_poll_irq(int irq) diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index e9e11168616..06592b9da83 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -508,7 +508,7 @@ static int __devinit gnttab_init(void) unsigned int max_nr_glist_frames, nr_glist_frames; unsigned int nr_init_grefs; - if (!is_running_on_xen()) + if (!xen_domain()) return -ENODEV; nr_grant_frames = 1; diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c index 57ceb5346b7..7f24a98a446 100644 --- a/drivers/xen/xenbus/xenbus_probe.c +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -814,7 +814,7 @@ static int __init xenbus_probe_init(void) DPRINTK(""); err = -ENODEV; - if (!is_running_on_xen()) + if (!xen_domain()) goto out_error; /* Register ourselves with the kernel bus subsystem */ @@ -829,7 +829,7 @@ static int __init xenbus_probe_init(void) /* * Domain0 doesn't have a store_evtchn or store_mfn yet. */ - if (is_initial_xendomain()) { + if (xen_initial_domain()) { /* dom0 not yet supported */ } else { xenstored_ready = 1; @@ -846,7 +846,7 @@ static int __init xenbus_probe_init(void) goto out_unreg_back; } - if (!is_initial_xendomain()) + if (!xen_initial_domain()) xenbus_probe(NULL); return 0; @@ -937,7 +937,7 @@ static void wait_for_devices(struct xenbus_driver *xendrv) unsigned long timeout = jiffies + 10*HZ; struct device_driver *drv = xendrv ? &xendrv->driver : NULL; - if (!ready_to_wait_for_devices || !is_running_on_xen()) + if (!ready_to_wait_for_devices || !xen_domain()) return; while (exists_disconnected_device(drv)) { diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 047c791427a..c061c3f18e7 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -55,7 +55,7 @@ enum { Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_debug, "debug=%x"}, {Opt_dfltuid, "dfltuid=%u"}, {Opt_dfltgid, "dfltgid=%u"}, diff --git a/fs/Kconfig b/fs/Kconfig index 40183d94b68..f54a157a029 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -170,8 +170,8 @@ config EXT4DEV_COMPAT help Starting with 2.6.28, the name of the ext4 filesystem was renamed from ext4dev to ext4. Unfortunately there are some - lagecy userspace programs (such as klibc's fstype) have - "ext4dev" hardcoded. + legacy userspace programs (such as klibc's fstype) have + "ext4dev" hardcoded. To enable backwards compatibility so that systems that are still expecting to mount ext4 filesystems using ext4dev, diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt index 4a551af6f3f..17c9c5ec14c 100644 --- a/fs/Kconfig.binfmt +++ b/fs/Kconfig.binfmt @@ -59,10 +59,12 @@ config BINFMT_SHARED_FLAT help Support FLAT shared libraries +config HAVE_AOUT + def_bool n + config BINFMT_AOUT tristate "Kernel support for a.out and ECOFF binaries" - depends on ARCH_SUPPORTS_AOUT && \ - (X86_32 || ALPHA || ARM || M68K) + depends on HAVE_AOUT ---help--- A.out (Assembler.OUTput) is a set of formats for libraries and executables used in the earliest versions of UNIX. Linux used diff --git a/fs/adfs/super.c b/fs/adfs/super.c index 26f3b43726b..7f83a46f2b7 100644 --- a/fs/adfs/super.c +++ b/fs/adfs/super.c @@ -157,7 +157,7 @@ static int adfs_show_options(struct seq_file *seq, struct vfsmount *mnt) enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err}; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_ownmask, "ownmask=%o"}, diff --git a/fs/affs/super.c b/fs/affs/super.c index 3a89094f93d..8989c93193e 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -135,7 +135,7 @@ enum { Opt_verbose, Opt_volume, Opt_ignore, Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bs, "bs=%u"}, {Opt_mode, "mode=%o"}, {Opt_mufs, "mufs"}, diff --git a/fs/afs/super.c b/fs/afs/super.c index 250d8c4d66e..aee239a048c 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -64,7 +64,7 @@ enum { afs_opt_vol, }; -static match_table_t afs_options_list = { +static const match_table_t afs_options_list = { { afs_opt_cell, "cell=%s" }, { afs_opt_rwpath, "rwpath" }, { afs_opt_vol, "vol=%s" }, diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index dda510d31f8..b70eea1e8c5 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -59,7 +59,7 @@ static const struct super_operations autofs_sops = { enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto}; -static match_table_t autofs_tokens = { +static const match_table_t autofs_tokens = { {Opt_fd, "fd=%u"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c index 7bb3e5ba053..45d55819203 100644 --- a/fs/autofs4/inode.c +++ b/fs/autofs4/inode.c @@ -213,7 +213,7 @@ static const struct super_operations autofs4_sops = { enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto, Opt_indirect, Opt_direct, Opt_offset}; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_fd, "fd=%u"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 740f53672a8..9286b2af893 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -650,7 +650,7 @@ enum { Opt_uid, Opt_gid, Opt_charset, Opt_debug, Opt_err, }; -static match_table_t befs_tokens = { +static const match_table_t befs_tokens = { {Opt_uid, "uid=%d"}, {Opt_gid, "gid=%d"}, {Opt_charset, "iocharset=%s"}, diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 654d972a88f..88786ba02d2 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -311,8 +311,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , buffer->Flags2 = SMBFLG2_KNOWS_LONG_NAMES; buffer->Pid = cpu_to_le16((__u16)current->tgid); buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); - spin_lock(&GlobalMid_Lock); - spin_unlock(&GlobalMid_Lock); if (treeCon) { buffer->Tid = treeCon->tid; if (treeCon->ses) { diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 08e28c9bb41..3dbe2169cf3 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -26,8 +26,7 @@ #include <linux/debugfs.h> #include <linux/fsnotify.h> #include <linux/string.h> - -#define DEBUGFS_MAGIC 0x64626720 +#include <linux/magic.h> static struct vfsmount *debugfs_mount; static int debugfs_mount_count; diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c index 488eb424f66..4a714f6c1be 100644 --- a/fs/devpts/inode.c +++ b/fs/devpts/inode.c @@ -27,6 +27,7 @@ #define DEVPTS_SUPER_MAGIC 0x1cd1 #define DEVPTS_DEFAULT_MODE 0600 +#define PTMX_MINOR 2 extern int pty_limit; /* Config limit on Unix98 ptys */ static DEFINE_IDA(allocated_ptys); @@ -48,7 +49,7 @@ enum { Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_mode, "mode=%o"}, @@ -169,15 +170,7 @@ static struct file_system_type devpts_fs_type = { * to the System V naming convention */ -static struct dentry *get_node(int num) -{ - char s[12]; - struct dentry *root = devpts_root; - mutex_lock(&root->d_inode->i_mutex); - return lookup_one_len(s, root, sprintf(s, "%d", num)); -} - -int devpts_new_index(void) +int devpts_new_index(struct inode *ptmx_inode) { int index; int ida_ret; @@ -205,20 +198,21 @@ retry: return index; } -void devpts_kill_index(int idx) +void devpts_kill_index(struct inode *ptmx_inode, int idx) { mutex_lock(&allocated_ptys_lock); ida_remove(&allocated_ptys, idx); mutex_unlock(&allocated_ptys_lock); } -int devpts_pty_new(struct tty_struct *tty) +int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty) { int number = tty->index; /* tty layer puts index from devpts_new_index() in here */ struct tty_driver *driver = tty->driver; dev_t device = MKDEV(driver->major, driver->minor_start+number); struct dentry *dentry; struct inode *inode = new_inode(devpts_mnt->mnt_sb); + char s[12]; /* We're supposed to be given the slave end of a pty */ BUG_ON(driver->type != TTY_DRIVER_TYPE_PTY); @@ -233,10 +227,15 @@ int devpts_pty_new(struct tty_struct *tty) inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; init_special_inode(inode, S_IFCHR|config.mode, device); inode->i_private = tty; + tty->driver_data = inode; - dentry = get_node(number); - if (!IS_ERR(dentry) && !dentry->d_inode) { - d_instantiate(dentry, inode); + sprintf(s, "%d", number); + + mutex_lock(&devpts_root->d_inode->i_mutex); + + dentry = d_alloc_name(devpts_root, s); + if (!IS_ERR(dentry)) { + d_add(dentry, inode); fsnotify_create(devpts_root->d_inode, dentry); } @@ -245,36 +244,31 @@ int devpts_pty_new(struct tty_struct *tty) return 0; } -struct tty_struct *devpts_get_tty(int number) +struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number) { - struct dentry *dentry = get_node(number); - struct tty_struct *tty; - - tty = NULL; - if (!IS_ERR(dentry)) { - if (dentry->d_inode) - tty = dentry->d_inode->i_private; - dput(dentry); - } + BUG_ON(pts_inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); - mutex_unlock(&devpts_root->d_inode->i_mutex); - - return tty; + if (pts_inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) + return (struct tty_struct *)pts_inode->i_private; + return NULL; } -void devpts_pty_kill(int number) +void devpts_pty_kill(struct tty_struct *tty) { - struct dentry *dentry = get_node(number); + struct inode *inode = tty->driver_data; + struct dentry *dentry; - if (!IS_ERR(dentry)) { - struct inode *inode = dentry->d_inode; - if (inode) { - inode->i_nlink--; - d_delete(dentry); - dput(dentry); - } + BUG_ON(inode->i_rdev == MKDEV(TTYAUX_MAJOR, PTMX_MINOR)); + + mutex_lock(&devpts_root->d_inode->i_mutex); + + dentry = d_find_alias(inode); + if (dentry && !IS_ERR(dentry)) { + inode->i_nlink--; + d_delete(dentry); dput(dentry); } + mutex_unlock(&devpts_root->d_inode->i_mutex); } diff --git a/fs/dquot.c b/fs/dquot.c index 8ec4d6cc763..ad7e59003e0 100644 --- a/fs/dquot.c +++ b/fs/dquot.c @@ -895,10 +895,9 @@ static void print_warning(struct dquot *dquot, const int warntype) warntype == QUOTA_NL_BSOFTBELOW || !need_print_warning(dquot)) return; - mutex_lock(&tty_mutex); tty = get_current_tty(); if (!tty) - goto out_lock; + return; tty_write_message(tty, dquot->dq_sb->s_id); if (warntype == QUOTA_NL_ISOFTWARN || warntype == QUOTA_NL_BSOFTWARN) tty_write_message(tty, ": warning, "); @@ -926,8 +925,7 @@ static void print_warning(struct dquot *dquot, const int warntype) break; } tty_write_message(tty, msg); -out_lock: - mutex_unlock(&tty_mutex); + tty_kref_put(tty); } #endif diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 448dfd597b5..8ebe9a5d1d9 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -211,7 +211,7 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata, ecryptfs_opt_encrypted_view, ecryptfs_opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {ecryptfs_opt_sig, "sig=%s"}, {ecryptfs_opt_ecryptfs_sig, "ecryptfs_sig=%s"}, {ecryptfs_opt_cipher, "cipher=%s"}, diff --git a/fs/efs/super.c b/fs/efs/super.c index 567b134fa1f..73b19cfc91f 100644 --- a/fs/efs/super.c +++ b/fs/efs/super.c @@ -341,8 +341,6 @@ static int efs_statfs(struct dentry *dentry, struct kstatfs *buf) { sb->inode_blocks * (EFS_BLOCKSIZE / sizeof(struct efs_dinode)); buf->f_ffree = sb->inode_free; /* free inodes */ - buf->f_fsid.val[0] = (sb->fs_magic >> 16) & 0xffff; /* fs ID */ - buf->f_fsid.val[1] = sb->fs_magic & 0xffff; /* fs ID */ buf->f_namelen = EFS_MAXNAMELEN; /* max filename length */ return 0; diff --git a/fs/ext2/super.c b/fs/ext2/super.c index fd88c7b43e6..647cd888ac8 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -393,7 +393,7 @@ enum { Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, diff --git a/fs/ext3/super.c b/fs/ext3/super.c index f38a5afc39a..399a96a6c55 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -760,7 +760,7 @@ enum { Opt_grpquota }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index f46a513a515..6690a41cdd9 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -540,6 +540,8 @@ do { \ #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */ #define EXT4_MOUNT_I_VERSION 0x2000000 /* i_version support */ #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ +#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ + /* Compatibility, for having both ext2_fs.h and ext4_fs.h included at once */ #ifndef _LINUX_EXT2_FS_H #define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index ea27eaa0cfe..dc99b4776d5 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -192,7 +192,7 @@ setversion_out: case EXT4_IOC_GROUP_EXTEND: { ext4_fsblk_t n_blocks_count; struct super_block *sb = inode->i_sb; - int err; + int err, err2; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; @@ -206,8 +206,10 @@ setversion_out: err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); - jbd2_journal_flush(EXT4_SB(sb)->s_journal); + err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); + if (err == 0) + err = err2; mnt_drop_write(filp->f_path.mnt); return err; @@ -215,7 +217,7 @@ setversion_out: case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; struct super_block *sb = inode->i_sb; - int err; + int err, err2; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; @@ -230,8 +232,10 @@ setversion_out: err = ext4_group_add(sb, &input); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); - jbd2_journal_flush(EXT4_SB(sb)->s_journal); + err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); + if (err == 0) + err = err2; mnt_drop_write(filp->f_path.mnt); return err; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 0e661c56966..dea8f13c2fd 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -507,7 +507,8 @@ static void ext4_put_super(struct super_block *sb) ext4_mb_release(sb); ext4_ext_release(sb); ext4_xattr_put_super(sb); - jbd2_journal_destroy(sbi->s_journal); + if (jbd2_journal_destroy(sbi->s_journal) < 0) + ext4_abort(sb, __func__, "Couldn't clean up the journal"); sbi->s_journal = NULL; if (!(sb->s_flags & MS_RDONLY)) { EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); @@ -777,6 +778,9 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_printf(seq, ",inode_readahead_blks=%u", sbi->s_inode_readahead_blks); + if (test_opt(sb, DATA_ERR_ABORT)) + seq_puts(seq, ",data_err=abort"); + ext4_show_quota_options(seq, sb); return 0; } @@ -906,6 +910,7 @@ enum { Opt_commit, Opt_journal_update, Opt_journal_inum, Opt_journal_dev, Opt_journal_checksum, Opt_journal_async_commit, Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback, + Opt_data_err_abort, Opt_data_err_ignore, Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, @@ -914,7 +919,7 @@ enum { Opt_inode_readahead_blks }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_bsd_df, "bsddf"}, {Opt_minix_df, "minixdf"}, {Opt_grpid, "grpid"}, @@ -952,6 +957,8 @@ static match_table_t tokens = { {Opt_data_journal, "data=journal"}, {Opt_data_ordered, "data=ordered"}, {Opt_data_writeback, "data=writeback"}, + {Opt_data_err_abort, "data_err=abort"}, + {Opt_data_err_ignore, "data_err=ignore"}, {Opt_offusrjquota, "usrjquota="}, {Opt_usrjquota, "usrjquota=%s"}, {Opt_offgrpjquota, "grpjquota="}, @@ -1186,6 +1193,12 @@ static int parse_options(char *options, struct super_block *sb, sbi->s_mount_opt |= data_opt; } break; + case Opt_data_err_abort: + set_opt(sbi->s_mount_opt, DATA_ERR_ABORT); + break; + case Opt_data_err_ignore: + clear_opt(sbi->s_mount_opt, DATA_ERR_ABORT); + break; #ifdef CONFIG_QUOTA case Opt_usrjquota: qtype = USRQUOTA; @@ -2218,6 +2231,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } +#ifdef CONFIG_PROC_FS if (ext4_proc_root) sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root); @@ -2225,6 +2239,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) proc_create_data("inode_readahead_blks", 0644, sbi->s_proc, &ext4_ui_proc_fops, &sbi->s_inode_readahead_blks); +#endif bgl_lock_init(&sbi->s_blockgroup_lock); @@ -2534,6 +2549,10 @@ static void ext4_init_journal_params(struct super_block *sb, journal_t *journal) journal->j_flags |= JBD2_BARRIER; else journal->j_flags &= ~JBD2_BARRIER; + if (test_opt(sb, DATA_ERR_ABORT)) + journal->j_flags |= JBD2_ABORT_ON_SYNCDATA_ERR; + else + journal->j_flags &= ~JBD2_ABORT_ON_SYNCDATA_ERR; spin_unlock(&journal->j_state_lock); } @@ -2853,7 +2872,9 @@ static void ext4_mark_recovery_complete(struct super_block *sb, journal_t *journal = EXT4_SB(sb)->s_journal; jbd2_journal_lock_updates(journal); - jbd2_journal_flush(journal); + if (jbd2_journal_flush(journal) < 0) + goto out; + lock_super(sb); if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER) && sb->s_flags & MS_RDONLY) { @@ -2862,6 +2883,8 @@ static void ext4_mark_recovery_complete(struct super_block *sb, ext4_commit_super(sb, es, 1); } unlock_super(sb); + +out: jbd2_journal_unlock_updates(journal); } @@ -2962,7 +2985,13 @@ static void ext4_write_super_lockfs(struct super_block *sb) /* Now we set up the journal barrier. */ jbd2_journal_lock_updates(journal); - jbd2_journal_flush(journal); + + /* + * We don't want to clear needs_recovery flag when we failed + * to flush the journal. + */ + if (jbd2_journal_flush(journal) < 0) + return; /* Journal blocked and flushed, clear needs_recovery flag. */ EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); @@ -3402,8 +3431,12 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, * otherwise be livelocked... */ jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal); - jbd2_journal_flush(EXT4_SB(sb)->s_journal); + err = jbd2_journal_flush(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); + if (err) { + path_put(&nd.path); + return err; + } } err = vfs_quota_on_path(sb, type, format_id, &nd.path); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 80ff3381fa2..d12cdf2a040 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -855,7 +855,7 @@ enum { Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, }; -static match_table_t fat_tokens = { +static const match_table_t fat_tokens = { {Opt_check_r, "check=relaxed"}, {Opt_check_s, "check=strict"}, {Opt_check_n, "check=normal"}, @@ -890,14 +890,14 @@ static match_table_t fat_tokens = { {Opt_tz_utc, "tz=UTC"}, {Opt_err, NULL}, }; -static match_table_t msdos_tokens = { +static const match_table_t msdos_tokens = { {Opt_nodots, "nodots"}, {Opt_nodots, "dotsOK=no"}, {Opt_dots, "dots"}, {Opt_dots, "dotsOK=yes"}, {Opt_err, NULL} }; -static match_table_t vfat_tokens = { +static const match_table_t vfat_tokens = { {Opt_charset, "iocharset=%s"}, {Opt_shortname_lower, "shortname=lower"}, {Opt_shortname_win95, "shortname=win95"}, diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index d2249f174e2..6a84388cacf 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -354,7 +354,7 @@ enum { OPT_ERR }; -static match_table_t tokens = { +static const match_table_t tokens = { {OPT_FD, "fd=%u"}, {OPT_ROOTMODE, "rootmode=%o"}, {OPT_USER_ID, "user_id=%u"}, diff --git a/fs/gfs2/mount.c b/fs/gfs2/mount.c index df48333e6f0..f96eb90a2cf 100644 --- a/fs/gfs2/mount.c +++ b/fs/gfs2/mount.c @@ -46,7 +46,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_lockproto, "lockproto=%s"}, {Opt_locktable, "locktable=%s"}, {Opt_hostdata, "hostdata=%s"}, diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 4abb1047c68..3c7c7637719 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -173,7 +173,7 @@ enum { opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { { opt_uid, "uid=%u" }, { opt_gid, "gid=%u" }, { opt_umask, "umask=%o" }, diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c index 9997cbf8beb..9699c56d323 100644 --- a/fs/hfsplus/options.c +++ b/fs/hfsplus/options.c @@ -25,7 +25,7 @@ enum { opt_force, opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { { opt_creator, "creator=%s" }, { opt_type, "type=%s" }, { opt_umask, "umask=%o" }, diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index b8ae9c90ada..29ad461d568 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -215,7 +215,7 @@ enum { Opt_timeshift, Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_help, "help"}, {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 3f58923fb39..61edc701b0e 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -57,7 +57,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_size, "size=%s"}, {Opt_nr_inodes, "nr_inodes=%s"}, {Opt_mode, "mode=%o"}, diff --git a/fs/ioctl.c b/fs/ioctl.c index 33a6b7ecb8b..d152856c371 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -226,6 +226,8 @@ static int ioctl_fiemap(struct file *filp, unsigned long arg) return error; } +#ifdef CONFIG_BLOCK + #define blk_to_logical(inode, blk) (blk << (inode)->i_blkbits) #define logical_to_blk(inode, offset) (offset >> (inode)->i_blkbits); @@ -342,6 +344,8 @@ int generic_block_fiemap(struct inode *inode, } EXPORT_SYMBOL(generic_block_fiemap); +#endif /* CONFIG_BLOCK */ + static int file_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 26948a6033b..3f8af0f1505 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -310,7 +310,7 @@ enum { Opt_nocompress, Opt_hide, Opt_showassoc, Opt_dmode, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_norock, "norock"}, {Opt_nojoliet, "nojoliet"}, {Opt_unhide, "unhide"}, diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 42895d36945..9203c3332f1 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -94,7 +94,8 @@ static int __try_to_free_cp_buf(struct journal_head *jh) int ret = 0; struct buffer_head *bh = jh2bh(jh); - if (jh->b_jlist == BJ_None && !buffer_locked(bh) && !buffer_dirty(bh)) { + if (jh->b_jlist == BJ_None && !buffer_locked(bh) && + !buffer_dirty(bh) && !buffer_write_io_error(bh)) { JBUFFER_TRACE(jh, "remove from checkpoint list"); ret = __jbd2_journal_remove_checkpoint(jh) + 1; jbd_unlock_bh_state(bh); @@ -176,21 +177,25 @@ static void jbd_sync_bh(journal_t *journal, struct buffer_head *bh) * buffers. Note that we take the buffers in the opposite ordering * from the one in which they were submitted for IO. * + * Return 0 on success, and return <0 if some buffers have failed + * to be written out. + * * Called with j_list_lock held. */ -static void __wait_cp_io(journal_t *journal, transaction_t *transaction) +static int __wait_cp_io(journal_t *journal, transaction_t *transaction) { struct journal_head *jh; struct buffer_head *bh; tid_t this_tid; int released = 0; + int ret = 0; this_tid = transaction->t_tid; restart: /* Did somebody clean up the transaction in the meanwhile? */ if (journal->j_checkpoint_transactions != transaction || transaction->t_tid != this_tid) - return; + return ret; while (!released && transaction->t_checkpoint_io_list) { jh = transaction->t_checkpoint_io_list; bh = jh2bh(jh); @@ -210,6 +215,9 @@ restart: spin_lock(&journal->j_list_lock); goto restart; } + if (unlikely(buffer_write_io_error(bh))) + ret = -EIO; + /* * Now in whatever state the buffer currently is, we know that * it has been written out and so we can drop it from the list @@ -219,6 +227,8 @@ restart: jbd2_journal_remove_journal_head(bh); __brelse(bh); } + + return ret; } #define NR_BATCH 64 @@ -242,7 +252,8 @@ __flush_batch(journal_t *journal, struct buffer_head **bhs, int *batch_count) * Try to flush one buffer from the checkpoint list to disk. * * Return 1 if something happened which requires us to abort the current - * scan of the checkpoint list. + * scan of the checkpoint list. Return <0 if the buffer has failed to + * be written out. * * Called with j_list_lock held and drops it if 1 is returned * Called under jbd_lock_bh_state(jh2bh(jh)), and drops it @@ -274,6 +285,9 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, jbd2_log_wait_commit(journal, tid); ret = 1; } else if (!buffer_dirty(bh)) { + ret = 1; + if (unlikely(buffer_write_io_error(bh))) + ret = -EIO; J_ASSERT_JH(jh, !buffer_jbddirty(bh)); BUFFER_TRACE(bh, "remove from checkpoint"); __jbd2_journal_remove_checkpoint(jh); @@ -281,7 +295,6 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, jbd_unlock_bh_state(bh); jbd2_journal_remove_journal_head(bh); __brelse(bh); - ret = 1; } else { /* * Important: we are about to write the buffer, and @@ -314,6 +327,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, * to disk. We submit larger chunks of data at once. * * The journal should be locked before calling this function. + * Called with j_checkpoint_mutex held. */ int jbd2_log_do_checkpoint(journal_t *journal) { @@ -339,6 +353,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) * OK, we need to start writing disk blocks. Take one transaction * and write it. */ + result = 0; spin_lock(&journal->j_list_lock); if (!journal->j_checkpoint_transactions) goto out; @@ -357,7 +372,7 @@ restart: int batch_count = 0; struct buffer_head *bhs[NR_BATCH]; struct journal_head *jh; - int retry = 0; + int retry = 0, err; while (!retry && transaction->t_checkpoint_list) { struct buffer_head *bh; @@ -371,6 +386,8 @@ restart: } retry = __process_buffer(journal, jh, bhs, &batch_count, transaction); + if (retry < 0 && !result) + result = retry; if (!retry && (need_resched() || spin_needbreak(&journal->j_list_lock))) { spin_unlock(&journal->j_list_lock); @@ -395,14 +412,18 @@ restart: * Now we have cleaned up the first transaction's checkpoint * list. Let's clean up the second one */ - __wait_cp_io(journal, transaction); + err = __wait_cp_io(journal, transaction); + if (!result) + result = err; } out: spin_unlock(&journal->j_list_lock); - result = jbd2_cleanup_journal_tail(journal); if (result < 0) - return result; - return 0; + jbd2_journal_abort(journal, result); + else + result = jbd2_cleanup_journal_tail(journal); + + return (result < 0) ? result : 0; } /* @@ -418,8 +439,9 @@ out: * This is the only part of the journaling code which really needs to be * aware of transaction aborts. Checkpointing involves writing to the * main filesystem area rather than to the journal, so it can proceed - * even in abort state, but we must not update the journal superblock if - * we have an abort error outstanding. + * even in abort state, but we must not update the super block if + * checkpointing may have failed. Otherwise, we would lose some metadata + * buffers which should be written-back to the filesystem. */ int jbd2_cleanup_journal_tail(journal_t *journal) @@ -428,6 +450,9 @@ int jbd2_cleanup_journal_tail(journal_t *journal) tid_t first_tid; unsigned long blocknr, freed; + if (is_journal_aborted(journal)) + return 1; + /* OK, work out the oldest transaction remaining in the log, and * the log block it starts at. * diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 0d3814a35ed..0abe02c4242 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -504,9 +504,10 @@ void jbd2_journal_commit_transaction(journal_t *journal) jh = commit_transaction->t_buffers; /* If we're in abort mode, we just un-journal the buffer and - release it for background writing. */ + release it. */ if (is_journal_aborted(journal)) { + clear_buffer_jbddirty(jh2bh(jh)); JBUFFER_TRACE(jh, "journal is aborting: refile"); jbd2_journal_refile_buffer(journal, jh); /* If that was the last one, we need to clean up @@ -683,6 +684,8 @@ start_journal_io: printk(KERN_WARNING "JBD2: Detected IO errors while flushing file data " "on %s\n", journal->j_devname); + if (journal->j_flags & JBD2_ABORT_ON_SYNCDATA_ERR) + jbd2_journal_abort(journal, err); err = 0; } @@ -783,6 +786,9 @@ wait_for_iobuf: /* AKPM: bforget here */ } + if (err) + jbd2_journal_abort(journal, err); + jbd_debug(3, "JBD: commit phase 5\n"); if (!JBD2_HAS_INCOMPAT_FEATURE(journal, @@ -881,6 +887,8 @@ restart_loop: if (buffer_jbddirty(bh)) { JBUFFER_TRACE(jh, "add to new checkpointing trans"); __jbd2_journal_insert_checkpoint(jh, commit_transaction); + if (is_journal_aborted(journal)) + clear_buffer_jbddirty(bh); JBUFFER_TRACE(jh, "refile for checkpoint writeback"); __jbd2_journal_refile_buffer(jh); jbd_unlock_bh_state(bh); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 01c3901c3a0..783de118de9 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1451,9 +1451,12 @@ recovery_error: * * Release a journal_t structure once it is no longer in use by the * journaled object. + * Return <0 if we couldn't clean up the journal. */ -void jbd2_journal_destroy(journal_t *journal) +int jbd2_journal_destroy(journal_t *journal) { + int err = 0; + /* Wait for the commit thread to wake up and die. */ journal_kill_thread(journal); @@ -1476,11 +1479,16 @@ void jbd2_journal_destroy(journal_t *journal) J_ASSERT(journal->j_checkpoint_transactions == NULL); spin_unlock(&journal->j_list_lock); - /* We can now mark the journal as empty. */ - journal->j_tail = 0; - journal->j_tail_sequence = ++journal->j_transaction_sequence; if (journal->j_sb_buffer) { - jbd2_journal_update_superblock(journal, 1); + if (!is_journal_aborted(journal)) { + /* We can now mark the journal as empty. */ + journal->j_tail = 0; + journal->j_tail_sequence = + ++journal->j_transaction_sequence; + jbd2_journal_update_superblock(journal, 1); + } else { + err = -EIO; + } brelse(journal->j_sb_buffer); } @@ -1492,6 +1500,8 @@ void jbd2_journal_destroy(journal_t *journal) jbd2_journal_destroy_revoke(journal); kfree(journal->j_wbuf); kfree(journal); + + return err; } @@ -1717,10 +1727,16 @@ int jbd2_journal_flush(journal_t *journal) spin_lock(&journal->j_list_lock); while (!err && journal->j_checkpoint_transactions != NULL) { spin_unlock(&journal->j_list_lock); + mutex_lock(&journal->j_checkpoint_mutex); err = jbd2_log_do_checkpoint(journal); + mutex_unlock(&journal->j_checkpoint_mutex); spin_lock(&journal->j_list_lock); } spin_unlock(&journal->j_list_lock); + + if (is_journal_aborted(journal)) + return -EIO; + jbd2_cleanup_journal_tail(journal); /* Finally, mark the journal as really needing no recovery. @@ -1742,7 +1758,7 @@ int jbd2_journal_flush(journal_t *journal) J_ASSERT(journal->j_head == journal->j_tail); J_ASSERT(journal->j_tail_sequence == journal->j_transaction_sequence); spin_unlock(&journal->j_state_lock); - return err; + return 0; } /** diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 058f50f65b7..73063285b13 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -225,7 +225,7 @@ do { \ */ int jbd2_journal_recover(journal_t *journal) { - int err; + int err, err2; journal_superblock_t * sb; struct recovery_info info; @@ -263,7 +263,10 @@ int jbd2_journal_recover(journal_t *journal) journal->j_transaction_sequence = ++info.end_transaction; jbd2_journal_clear_revoke(journal); - sync_blockdev(journal->j_fs_dev); + err2 = sync_blockdev(journal->j_fs_dev); + if (!err) + err = err2; + return err; } diff --git a/fs/jfs/super.c b/fs/jfs/super.c index 3630718be39..0dae345e481 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -199,7 +199,7 @@ enum { Opt_usrquota, Opt_grpquota, Opt_uid, Opt_gid, Opt_umask }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_integrity, "integrity"}, {Opt_nointegrity, "nointegrity"}, {Opt_iocharset, "iocharset=%s"}, diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 46763d1cd39..8478fc25dae 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -127,7 +127,7 @@ enum { Opt_err }; -static match_table_t __initdata tokens = { +static match_table_t __initconst tokens = { {Opt_port, "port=%u"}, {Opt_rsize, "rsize=%u"}, {Opt_wsize, "wsize=%u"}, diff --git a/fs/nfs/super.c b/fs/nfs/super.c index e9b20173fef..ffb697416cb 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -98,7 +98,7 @@ enum { Opt_err }; -static match_table_t nfs_mount_option_tokens = { +static const match_table_t nfs_mount_option_tokens = { { Opt_userspace, "bg" }, { Opt_userspace, "fg" }, { Opt_userspace, "retry=%s" }, @@ -163,7 +163,7 @@ enum { Opt_xprt_err }; -static match_table_t nfs_xprt_protocol_tokens = { +static const match_table_t nfs_xprt_protocol_tokens = { { Opt_xprt_udp, "udp" }, { Opt_xprt_tcp, "tcp" }, { Opt_xprt_rdma, "rdma" }, @@ -180,7 +180,7 @@ enum { Opt_sec_err }; -static match_table_t nfs_secflavor_tokens = { +static const match_table_t nfs_secflavor_tokens = { { Opt_sec_none, "none" }, { Opt_sec_none, "null" }, { Opt_sec_sys, "sys" }, diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 88255d3f52b..70334d85aff 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -157,7 +157,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_barrier, "barrier=%u"}, {Opt_err_panic, "errors=panic"}, {Opt_err_ro, "errors=remount-ro"}, diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index d29047b1b9b..cbf047a847c 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -346,7 +346,7 @@ enum { Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_uid, "uid=%u"}, {Opt_gid, "gid=%u"}, {Opt_umask, "umask=%o"}, diff --git a/fs/open.c b/fs/open.c index 07da9359481..5596049863b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1141,8 +1141,7 @@ EXPORT_SYMBOL(sys_close); asmlinkage long sys_vhangup(void) { if (capable(CAP_SYS_TTY_CONFIG)) { - /* XXX: this needs locking */ - tty_vhangup(current->signal->tty); + tty_vhangup_self(); return 0; } return -EPERM; diff --git a/fs/proc/Kconfig b/fs/proc/Kconfig index 73cd7a418f0..50f8f0600f0 100644 --- a/fs/proc/Kconfig +++ b/fs/proc/Kconfig @@ -57,3 +57,13 @@ config PROC_SYSCTL As it is generally a good thing, you should say Y here unless building a kernel for install/rescue disks or your system is very limited in memory. + +config PROC_PAGE_MONITOR + default y + depends on PROC_FS && MMU + bool "Enable /proc page monitoring" if EMBEDDED + help + Various /proc files exist to monitor process memory utilization: + /proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap, + /proc/kpagecount, and /proc/kpageflags. Disabling these + interfaces will reduce the size of the kernel by approximately 4kb. diff --git a/fs/proc/array.c b/fs/proc/array.c index 71c9be59c9c..f4bc0e78953 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -86,11 +86,6 @@ #include <asm/processor.h> #include "internal.h" -/* Gcc optimizes away "strlen(x)" for constant x */ -#define ADDBUF(buffer, string) \ -do { memcpy(buffer, string, strlen(string)); \ - buffer += strlen(string); } while (0) - static inline void task_name(struct seq_file *m, struct task_struct *p) { int i; @@ -261,7 +256,6 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p) sigemptyset(&ignored); sigemptyset(&caught); - rcu_read_lock(); if (lock_task_sighand(p, &flags)) { pending = p->pending.signal; shpending = p->signal->shared_pending.signal; @@ -272,7 +266,6 @@ static inline void task_sig(struct seq_file *m, struct task_struct *p) qlim = p->signal->rlim[RLIMIT_SIGPENDING].rlim_cur; unlock_task_sighand(p, &flags); } - rcu_read_unlock(); seq_printf(m, "Threads:\t%d\n", num_threads); seq_printf(m, "SigQ:\t%lu/%lu\n", qsize, qlim); diff --git a/fs/proc/base.c b/fs/proc/base.c index a28840b11b8..b5918ae8ca7 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -148,9 +148,6 @@ static unsigned int pid_entry_count_dirs(const struct pid_entry *entries, return count; } -int maps_protect; -EXPORT_SYMBOL(maps_protect); - static struct fs_struct *get_fs_struct(struct task_struct *task) { struct fs_struct *fs; @@ -164,7 +161,6 @@ static struct fs_struct *get_fs_struct(struct task_struct *task) static int get_nr_threads(struct task_struct *tsk) { - /* Must be called with the rcu_read_lock held */ unsigned long flags; int count = 0; @@ -471,14 +467,10 @@ static int proc_pid_limits(struct task_struct *task, char *buffer) struct rlimit rlim[RLIM_NLIMITS]; - rcu_read_lock(); - if (!lock_task_sighand(task,&flags)) { - rcu_read_unlock(); + if (!lock_task_sighand(task, &flags)) return 0; - } memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS); unlock_task_sighand(task, &flags); - rcu_read_unlock(); /* * print the file header @@ -2443,6 +2435,13 @@ static int proc_tgid_io_accounting(struct task_struct *task, char *buffer) } #endif /* CONFIG_TASK_IO_ACCOUNTING */ +static int proc_pid_personality(struct seq_file *m, struct pid_namespace *ns, + struct pid *pid, struct task_struct *task) +{ + seq_printf(m, "%08x\n", task->personality); + return 0; +} + /* * Thread groups */ @@ -2459,6 +2458,7 @@ static const struct pid_entry tgid_base_stuff[] = { REG("environ", S_IRUSR, environ), INF("auxv", S_IRUSR, pid_auxv), ONE("status", S_IRUGO, pid_status), + ONE("personality", S_IRUSR, pid_personality), INF("limits", S_IRUSR, pid_limits), #ifdef CONFIG_SCHED_DEBUG REG("sched", S_IRUGO|S_IWUSR, pid_sched), @@ -2794,6 +2794,7 @@ static const struct pid_entry tid_base_stuff[] = { REG("environ", S_IRUSR, environ), INF("auxv", S_IRUSR, pid_auxv), ONE("status", S_IRUGO, pid_status), + ONE("personality", S_IRUSR, pid_personality), INF("limits", S_IRUSR, pid_limits), #ifdef CONFIG_SCHED_DEBUG REG("sched", S_IRUGO|S_IWUSR, pid_sched), @@ -3088,9 +3089,7 @@ static int proc_task_getattr(struct vfsmount *mnt, struct dentry *dentry, struct generic_fillattr(inode, stat); if (p) { - rcu_read_lock(); stat->nlink += get_nr_threads(p); - rcu_read_unlock(); put_task_struct(p); } diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 8bb03f056c2..c6b4fa7e3b4 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -342,7 +342,7 @@ static int proc_reg_open(struct inode *inode, struct file *file) if (!pde->proc_fops) { spin_unlock(&pde->pde_unload_lock); kfree(pdeo); - return rv; + return -EINVAL; } pde->pde_users++; open = pde->proc_fops->open; diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 442202314d5..3bfb7b8747b 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -45,8 +45,6 @@ do { \ extern int nommu_vma_show(struct seq_file *, struct vm_area_struct *); #endif -extern int maps_protect; - extern int proc_tid_stat(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task); extern int proc_tgid_stat(struct seq_file *m, struct pid_namespace *ns, diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 29e20c6b1f7..66c1ab87656 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -68,7 +68,6 @@ extern int get_hardware_list(char *); extern int get_stram_list(char *); extern int get_exec_domain_list(char *); -extern int get_dma_list(char *); static int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len) diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index f9a8b892718..945a81043ba 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -66,7 +66,7 @@ static struct ctl_table *find_in_table(struct ctl_table *p, struct qstr *name) return NULL; } -struct ctl_table_header *grab_header(struct inode *inode) +static struct ctl_table_header *grab_header(struct inode *inode) { if (PROC_I(inode)->sysctl) return sysctl_head_grab(PROC_I(inode)->sysctl); @@ -395,10 +395,10 @@ static struct dentry_operations proc_sys_dentry_operations = { .d_compare = proc_sys_compare, }; -static struct proc_dir_entry *proc_sys_root; - int proc_sys_init(void) { + struct proc_dir_entry *proc_sys_root; + proc_sys_root = proc_mkdir("sys", NULL); proc_sys_root->proc_iops = &proc_sys_dir_operations; proc_sys_root->proc_fops = &proc_sys_dir_file_operations; diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 73d1891ee62..4806830ea2a 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -210,9 +210,6 @@ static int show_map(struct seq_file *m, void *v) dev_t dev = 0; int len; - if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ)) - return -EACCES; - if (file) { struct inode *inode = vma->vm_file->f_path.dentry->d_inode; dev = inode->i_sb->s_dev; @@ -742,22 +739,11 @@ const struct file_operations proc_pagemap_operations = { #ifdef CONFIG_NUMA extern int show_numa_map(struct seq_file *m, void *v); -static int show_numa_map_checked(struct seq_file *m, void *v) -{ - struct proc_maps_private *priv = m->private; - struct task_struct *task = priv->task; - - if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ)) - return -EACCES; - - return show_numa_map(m, v); -} - static const struct seq_operations proc_pid_numa_maps_op = { .start = m_start, .next = m_next, .stop = m_stop, - .show = show_numa_map_checked + .show = show_numa_map, }; static int numa_maps_open(struct inode *inode, struct file *file) diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 5d84e7121df..219bd79ea89 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -110,11 +110,6 @@ int task_statm(struct mm_struct *mm, int *shared, int *text, static int show_map(struct seq_file *m, void *_vml) { struct vm_list_struct *vml = _vml; - struct proc_maps_private *priv = m->private; - struct task_struct *task = priv->task; - - if (maps_protect && !ptrace_may_access(task, PTRACE_MODE_READ)) - return -EACCES; return nommu_vma_show(m, vml->vma); } diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 9ac0f5e064e..841368b87a2 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -165,14 +165,8 @@ static ssize_t read_vmcore(struct file *file, char __user *buffer, return acc; } -static int open_vmcore(struct inode *inode, struct file *filp) -{ - return 0; -} - const struct file_operations proc_vmcore_operations = { .read = read_vmcore, - .open = open_vmcore, }; static struct vmcore* __init get_new_element(void) diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 3f4902060c7..9a9220333b3 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -848,7 +848,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_fast_unmount, "fast_unmount"}, {Opt_norm_unmount, "norm_unmount"}, {Opt_err, NULL}, diff --git a/fs/udf/super.c b/fs/udf/super.c index 5698bbf83bb..e25e7010627 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -369,7 +369,7 @@ enum { Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_novrs, "novrs"}, {Opt_nostrict, "nostrict"}, {Opt_bs, "bs=%u"}, diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 3141969b456..e65212dfb60 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -309,7 +309,7 @@ enum { Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_type_old, "ufstype=old"}, {Opt_type_sunx86, "ufstype=sunx86"}, {Opt_type_sun, "ufstype=sun"}, @@ -1233,7 +1233,7 @@ static int ufs_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct ufs_sb_info *sbi = UFS_SB(vfs->mnt_sb); unsigned mval = sbi->s_mount_opt & UFS_MOUNT_UFSTYPE; - struct match_token *tp = tokens; + const struct match_token *tp = tokens; while (tp->token != Opt_onerror_panic && tp->token != mval) ++tp; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 18d3c848783..7227b2efef2 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -158,7 +158,7 @@ enum { Opt_barrier, Opt_nobarrier, Opt_err }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_barrier, "barrier"}, {Opt_nobarrier, "nobarrier"}, {Opt_err, NULL} diff --git a/include/asm-cris/a.out.h b/include/asm-cris/a.out.h deleted file mode 100644 index c82e9f9b75f..00000000000 --- a/include/asm-cris/a.out.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __CRIS_A_OUT_H__ -#define __CRIS_A_OUT_H__ - -/* we don't support a.out binaries on Linux/CRIS anyway, so this is - * not really used but still needed because binfmt_elf.c for some reason - * wants to know about a.out even if there is no interpreter available... - */ - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h index 8786e01e0db..969570167e9 100644 --- a/include/asm-generic/siginfo.h +++ b/include/asm-generic/siginfo.h @@ -199,6 +199,8 @@ typedef struct siginfo { */ #define TRAP_BRKPT (__SI_FAULT|1) /* process breakpoint */ #define TRAP_TRACE (__SI_FAULT|2) /* process trace trap */ +#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */ +#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint/watchpoint */ #define NSIGTRAP 2 /* diff --git a/include/asm-generic/statfs.h b/include/asm-generic/statfs.h index 1d01043e797..6129d680214 100644 --- a/include/asm-generic/statfs.h +++ b/include/asm-generic/statfs.h @@ -6,33 +6,64 @@ typedef __kernel_fsid_t fsid_t; #endif +/* + * Most 64-bit platforms use 'long', while most 32-bit platforms use '__u32'. + * Yes, they differ in signedness as well as size. + * Special cases can override it for themselves -- except for S390x, which + * is just a little too special for us. And MIPS, which I'm not touching + * with a 10' pole. + */ +#ifndef __statfs_word +#if BITS_PER_LONG == 64 +#define __statfs_word long +#else +#define __statfs_word __u32 +#endif +#endif + struct statfs { - __u32 f_type; - __u32 f_bsize; - __u32 f_blocks; - __u32 f_bfree; - __u32 f_bavail; - __u32 f_files; - __u32 f_ffree; + __statfs_word f_type; + __statfs_word f_bsize; + __statfs_word f_blocks; + __statfs_word f_bfree; + __statfs_word f_bavail; + __statfs_word f_files; + __statfs_word f_ffree; __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; + __statfs_word f_namelen; + __statfs_word f_frsize; + __statfs_word f_spare[5]; }; +/* + * ARM needs to avoid the 32-bit padding at the end, for consistency + * between EABI and OABI + */ +#ifndef ARCH_PACK_STATFS64 +#define ARCH_PACK_STATFS64 +#endif + struct statfs64 { - __u32 f_type; - __u32 f_bsize; + __statfs_word f_type; + __statfs_word f_bsize; __u64 f_blocks; __u64 f_bfree; __u64 f_bavail; __u64 f_files; __u64 f_ffree; __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; + __statfs_word f_namelen; + __statfs_word f_frsize; + __statfs_word f_spare[5]; +} ARCH_PACK_STATFS64; + +/* + * IA64 and x86_64 need to avoid the 32-bit padding at the end, + * to be compatible with the i386 ABI + */ +#ifndef ARCH_PACK_COMPAT_STATFS64 +#define ARCH_PACK_COMPAT_STATFS64 +#endif struct compat_statfs64 { __u32 f_type; @@ -46,6 +77,6 @@ struct compat_statfs64 { __u32 f_namelen; __u32 f_frsize; __u32 f_spare[5]; -}; +} ARCH_PACK_COMPAT_STATFS64; #endif diff --git a/include/asm-m32r/a.out.h b/include/asm-m32r/a.out.h deleted file mode 100644 index ab150f5c166..00000000000 --- a/include/asm-m32r/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _ASM_M32R_A_OUT_H -#define _ASM_M32R_A_OUT_H - -struct exec -{ - unsigned long a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* _ASM_M32R_A_OUT_H */ diff --git a/include/asm-parisc/a.out.h b/include/asm-parisc/a.out.h deleted file mode 100644 index eb04e34c5bb..00000000000 --- a/include/asm-parisc/a.out.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __PARISC_A_OUT_H__ -#define __PARISC_A_OUT_H__ - -struct exec -{ - unsigned int a_info; /* Use macros N_MAGIC, etc for access */ - unsigned a_text; /* length of text, in bytes */ - unsigned a_data; /* length of data, in bytes */ - unsigned a_bss; /* length of uninitialized data area for file, in bytes */ - unsigned a_syms; /* length of symbol table data in file, in bytes */ - unsigned a_entry; /* start address */ - unsigned a_trsize; /* length of relocation info for text, in bytes */ - unsigned a_drsize; /* length of relocation info for data, in bytes */ -}; - -#define N_TRSIZE(a) ((a).a_trsize) -#define N_DRSIZE(a) ((a).a_drsize) -#define N_SYMSIZE(a) ((a).a_syms) - -#endif /* __A_OUT_GNU_H__ */ diff --git a/include/asm-parisc/siginfo.h b/include/asm-parisc/siginfo.h index d4909f55fe3..d7034728f37 100644 --- a/include/asm-parisc/siginfo.h +++ b/include/asm-parisc/siginfo.h @@ -3,11 +3,6 @@ #include <asm-generic/siginfo.h> -/* - * SIGTRAP si_codes - */ -#define TRAP_BRANCH (__SI_FAULT|3) /* process taken branch trap */ -#define TRAP_HWBKPT (__SI_FAULT|4) /* hardware breakpoint or watchpoint */ #undef NSIGTRAP #define NSIGTRAP 4 diff --git a/include/asm-parisc/statfs.h b/include/asm-parisc/statfs.h index 1d2b8130b23..324bea905dc 100644 --- a/include/asm-parisc/statfs.h +++ b/include/asm-parisc/statfs.h @@ -1,58 +1,7 @@ #ifndef _PARISC_STATFS_H #define _PARISC_STATFS_H -#ifndef __KERNEL_STRICT_NAMES - -#include <linux/types.h> - -typedef __kernel_fsid_t fsid_t; - -#endif - -/* - * It appears that PARISC could be 64 _or_ 32 bit. - * 64-bit fields must be explicitly 64-bit in statfs64. - */ -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct statfs64 { - long f_type; - long f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -}; +#define __statfs_word long +#include <asm-generic/statfs.h> #endif diff --git a/include/asm-x86/bios_ebda.h b/include/asm-x86/bios_ebda.h index ec42ed87459..79b4b88505d 100644 --- a/include/asm-x86/bios_ebda.h +++ b/include/asm-x86/bios_ebda.h @@ -16,4 +16,21 @@ static inline unsigned int get_bios_ebda(void) void reserve_ebda_region(void); +#ifdef CONFIG_X86_CHECK_BIOS_CORRUPTION +/* + * This is obviously not a great place for this, but we want to be + * able to scatter it around anywhere in the kernel. + */ +void check_for_bios_corruption(void); +void start_periodic_check_for_corruption(void); +#else +static inline void check_for_bios_corruption(void) +{ +} + +static inline void start_periodic_check_for_corruption(void) +{ +} +#endif + #endif /* ASM_X86__BIOS_EBDA_H */ diff --git a/include/asm-x86/boot.h b/include/asm-x86/boot.h index 825de5dc867..1d63bd5d594 100644 --- a/include/asm-x86/boot.h +++ b/include/asm-x86/boot.h @@ -2,9 +2,7 @@ #define ASM_X86__BOOT_H /* Don't touch these, unless you really know what you're doing. */ -#define DEF_INITSEG 0x9000 #define DEF_SYSSEG 0x1000 -#define DEF_SETUPSEG 0x9020 #define DEF_SYSSIZE 0x7F00 /* Internal svga startup constants */ diff --git a/include/asm-x86/desc.h b/include/asm-x86/desc.h index b73fea54def..f06adac7938 100644 --- a/include/asm-x86/desc.h +++ b/include/asm-x86/desc.h @@ -24,6 +24,11 @@ static inline void fill_ldt(struct desc_struct *desc, desc->d = info->seg_32bit; desc->g = info->limit_in_pages; desc->base2 = (info->base_addr & 0xff000000) >> 24; + /* + * Don't allow setting of the lm bit. It is useless anyway + * because 64bit system calls require __USER_CS: + */ + desc->l = 0; } extern struct desc_ptr idt_descr; @@ -97,7 +102,15 @@ static inline int desc_empty(const void *ptr) native_write_gdt_entry(dt, entry, desc, type) #define write_idt_entry(dt, entry, g) \ native_write_idt_entry(dt, entry, g) -#endif + +static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries) +{ +} + +static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries) +{ +} +#endif /* CONFIG_PARAVIRT */ static inline void native_write_idt_entry(gate_desc *idt, int entry, const gate_desc *gate) @@ -338,20 +351,16 @@ static inline void set_system_intr_gate(unsigned int n, void *addr) _set_gate(n, GATE_INTERRUPT, addr, 0x3, 0, __KERNEL_CS); } -static inline void set_trap_gate(unsigned int n, void *addr) +static inline void set_system_trap_gate(unsigned int n, void *addr) { BUG_ON((unsigned)n > 0xFF); - _set_gate(n, GATE_TRAP, addr, 0, 0, __KERNEL_CS); + _set_gate(n, GATE_TRAP, addr, 0x3, 0, __KERNEL_CS); } -static inline void set_system_gate(unsigned int n, void *addr) +static inline void set_trap_gate(unsigned int n, void *addr) { BUG_ON((unsigned)n > 0xFF); -#ifdef CONFIG_X86_32 - _set_gate(n, GATE_TRAP, addr, 0x3, 0, __KERNEL_CS); -#else - _set_gate(n, GATE_INTERRUPT, addr, 0x3, 0, __KERNEL_CS); -#endif + _set_gate(n, GATE_TRAP, addr, 0, 0, __KERNEL_CS); } static inline void set_task_gate(unsigned int n, unsigned int gdt_entry) @@ -366,7 +375,7 @@ static inline void set_intr_gate_ist(int n, void *addr, unsigned ist) _set_gate(n, GATE_INTERRUPT, addr, 0, ist, __KERNEL_CS); } -static inline void set_system_gate_ist(int n, void *addr, unsigned ist) +static inline void set_system_intr_gate_ist(int n, void *addr, unsigned ist) { BUG_ON((unsigned)n > 0xFF); _set_gate(n, GATE_INTERRUPT, addr, 0x3, ist, __KERNEL_CS); diff --git a/include/asm-x86/es7000/mpparse.h b/include/asm-x86/es7000/mpparse.h index 7b5c889d8e7..ed5a3caae14 100644 --- a/include/asm-x86/es7000/mpparse.h +++ b/include/asm-x86/es7000/mpparse.h @@ -5,6 +5,7 @@ extern int parse_unisys_oem (char *oemptr); extern int find_unisys_acpi_oem_table(unsigned long *oem_addr); +extern void unmap_unisys_acpi_oem_table(unsigned long oem_addr); extern void setup_unisys(void); #ifndef CONFIG_X86_GENERICARCH diff --git a/include/asm-x86/fixmap_32.h b/include/asm-x86/fixmap_32.h index 784e3e75986..8844002da0e 100644 --- a/include/asm-x86/fixmap_32.h +++ b/include/asm-x86/fixmap_32.h @@ -94,10 +94,10 @@ enum fixed_addresses { * can have a single pgd entry and a single pte table: */ #define NR_FIX_BTMAPS 64 -#define FIX_BTMAPS_NESTING 4 +#define FIX_BTMAPS_SLOTS 4 FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 256 - (__end_of_permanent_fixed_addresses & 255), - FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_NESTING - 1, + FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_SLOTS - 1, FIX_WP_TEST, #ifdef CONFIG_ACPI FIX_ACPI_BEGIN, diff --git a/include/asm-x86/fixmap_64.h b/include/asm-x86/fixmap_64.h index dafb24bc042..dab4751d130 100644 --- a/include/asm-x86/fixmap_64.h +++ b/include/asm-x86/fixmap_64.h @@ -49,6 +49,7 @@ enum fixed_addresses { #ifdef CONFIG_PARAVIRT FIX_PARAVIRT_BOOTMAP, #endif + __end_of_permanent_fixed_addresses, #ifdef CONFIG_ACPI FIX_ACPI_BEGIN, FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1, @@ -56,19 +57,18 @@ enum fixed_addresses { #ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT FIX_OHCI1394_BASE, #endif - __end_of_permanent_fixed_addresses, /* * 256 temporary boot-time mappings, used by early_ioremap(), * before ioremap() is functional. * - * We round it up to the next 512 pages boundary so that we + * We round it up to the next 256 pages boundary so that we * can have a single pgd entry and a single pte table: */ #define NR_FIX_BTMAPS 64 -#define FIX_BTMAPS_NESTING 4 - FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 512 - - (__end_of_permanent_fixed_addresses & 511), - FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_NESTING - 1, +#define FIX_BTMAPS_SLOTS 4 + FIX_BTMAP_END = __end_of_permanent_fixed_addresses + 256 - + (__end_of_permanent_fixed_addresses & 255), + FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_SLOTS - 1, __end_of_fixed_addresses }; diff --git a/include/asm-x86/io.h b/include/asm-x86/io.h index 72b7719523b..a233f835e0b 100644 --- a/include/asm-x86/io.h +++ b/include/asm-x86/io.h @@ -5,20 +5,6 @@ #include <linux/compiler.h> -/* - * early_ioremap() and early_iounmap() are for temporary early boot-time - * mappings, before the real ioremap() is functional. - * A boot-time mapping is currently limited to at most 16 pages. - */ -#ifndef __ASSEMBLY__ -extern void early_ioremap_init(void); -extern void early_ioremap_clear(void); -extern void early_ioremap_reset(void); -extern void *early_ioremap(unsigned long offset, unsigned long size); -extern void early_iounmap(void *addr, unsigned long size); -extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys); -#endif - #define build_mmio_read(name, size, type, reg, barrier) \ static inline type name(const volatile void __iomem *addr) \ { type ret; asm volatile("mov" size " %1,%0":reg (ret) \ @@ -97,6 +83,7 @@ extern void early_ioremap_init(void); extern void early_ioremap_clear(void); extern void early_ioremap_reset(void); extern void *early_ioremap(unsigned long offset, unsigned long size); +extern void *early_memremap(unsigned long offset, unsigned long size); extern void early_iounmap(void *addr, unsigned long size); extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys); diff --git a/include/asm-x86/io_64.h b/include/asm-x86/io_64.h index 64429e9431a..ee6e086b7df 100644 --- a/include/asm-x86/io_64.h +++ b/include/asm-x86/io_64.h @@ -165,9 +165,6 @@ static inline void *phys_to_virt(unsigned long address) #include <asm-generic/iomap.h> -extern void *early_ioremap(unsigned long addr, unsigned long size); -extern void early_iounmap(void *addr, unsigned long size); - /* * This one maps high address device memory and turns off caching for that area. * it's useful if some control registers are in such an area and write combining diff --git a/include/asm-x86/ioctls.h b/include/asm-x86/ioctls.h index 33660351239..06752a64904 100644 --- a/include/asm-x86/ioctls.h +++ b/include/asm-x86/ioctls.h @@ -51,9 +51,15 @@ #define TCSETS2 _IOW('T', 0x2B, struct termios2) #define TCSETSW2 _IOW('T', 0x2C, struct termios2) #define TCSETSF2 _IOW('T', 0x2D, struct termios2) +#define TIOCGRS485 0x542E +#define TIOCSRS485 0x542F #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TCGETX 0x5432 /* SYS5 TCGETX compatibility */ +#define TCSETX 0x5433 +#define TCSETXF 0x5434 +#define TCSETXW 0x5435 #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 diff --git a/include/asm-x86/irqflags.h b/include/asm-x86/irqflags.h index 424acb48cd6..2bdab21f089 100644 --- a/include/asm-x86/irqflags.h +++ b/include/asm-x86/irqflags.h @@ -166,27 +166,6 @@ static inline int raw_irqs_disabled(void) return raw_irqs_disabled_flags(flags); } -/* - * makes the traced hardirq state match with the machine state - * - * should be a rarely used function, only in places where its - * otherwise impossible to know the irq state, like in traps. - */ -static inline void trace_hardirqs_fixup_flags(unsigned long flags) -{ - if (raw_irqs_disabled_flags(flags)) - trace_hardirqs_off(); - else - trace_hardirqs_on(); -} - -static inline void trace_hardirqs_fixup(void) -{ - unsigned long flags = __raw_local_save_flags(); - - trace_hardirqs_fixup_flags(flags); -} - #else #ifdef CONFIG_X86_64 diff --git a/include/asm-x86/kdebug.h b/include/asm-x86/kdebug.h index 5ec3ad3e825..fbbab66ee9d 100644 --- a/include/asm-x86/kdebug.h +++ b/include/asm-x86/kdebug.h @@ -27,10 +27,9 @@ extern void printk_address(unsigned long address, int reliable); extern void die(const char *, struct pt_regs *,long); extern int __must_check __die(const char *, struct pt_regs *, long); extern void show_registers(struct pt_regs *regs); -extern void __show_registers(struct pt_regs *, int all); extern void show_trace(struct task_struct *t, struct pt_regs *regs, unsigned long *sp, unsigned long bp); -extern void __show_regs(struct pt_regs *regs); +extern void __show_regs(struct pt_regs *regs, int all); extern void show_regs(struct pt_regs *regs); extern unsigned long oops_begin(void); extern void oops_end(unsigned long, struct pt_regs *, int signr); diff --git a/include/asm-x86/kprobes.h b/include/asm-x86/kprobes.h index bd8407863c1..8a0748d0103 100644 --- a/include/asm-x86/kprobes.h +++ b/include/asm-x86/kprobes.h @@ -82,15 +82,6 @@ struct kprobe_ctlblk { struct prev_kprobe prev_kprobe; }; -/* trap3/1 are intr gates for kprobes. So, restore the status of IF, - * if necessary, before executing the original int3/1 (trap) handler. - */ -static inline void restore_interrupts(struct pt_regs *regs) -{ - if (regs->flags & X86_EFLAGS_IF) - local_irq_enable(); -} - extern int kprobe_fault_handler(struct pt_regs *regs, int trapnr); extern int kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data); diff --git a/include/asm-x86/mach-default/mach_traps.h b/include/asm-x86/mach-default/mach_traps.h index de9ac3f5c4c..ff8778f26b8 100644 --- a/include/asm-x86/mach-default/mach_traps.h +++ b/include/asm-x86/mach-default/mach_traps.h @@ -7,12 +7,6 @@ #include <asm/mc146818rtc.h> -static inline void clear_mem_error(unsigned char reason) -{ - reason = (reason & 0xf) | 4; - outb(reason, 0x61); -} - static inline unsigned char get_nmi_reason(void) { return inb(0x61); diff --git a/include/asm-x86/microcode.h b/include/asm-x86/microcode.h new file mode 100644 index 00000000000..62c793bb70c --- /dev/null +++ b/include/asm-x86/microcode.h @@ -0,0 +1,47 @@ +#ifndef ASM_X86__MICROCODE_H +#define ASM_X86__MICROCODE_H + +struct cpu_signature { + unsigned int sig; + unsigned int pf; + unsigned int rev; +}; + +struct device; + +struct microcode_ops { + int (*request_microcode_user) (int cpu, const void __user *buf, size_t size); + int (*request_microcode_fw) (int cpu, struct device *device); + + void (*apply_microcode) (int cpu); + + int (*collect_cpu_info) (int cpu, struct cpu_signature *csig); + void (*microcode_fini_cpu) (int cpu); +}; + +struct ucode_cpu_info { + struct cpu_signature cpu_sig; + int valid; + void *mc; +}; +extern struct ucode_cpu_info ucode_cpu_info[]; + +#ifdef CONFIG_MICROCODE_INTEL +extern struct microcode_ops * __init init_intel_microcode(void); +#else +static inline struct microcode_ops * __init init_intel_microcode(void) +{ + return NULL; +} +#endif /* CONFIG_MICROCODE_INTEL */ + +#ifdef CONFIG_MICROCODE_AMD +extern struct microcode_ops * __init init_amd_microcode(void); +#else +static inline struct microcode_ops * __init init_amd_microcode(void) +{ + return NULL; +} +#endif + +#endif /* ASM_X86__MICROCODE_H */ diff --git a/include/asm-x86/mmzone_64.h b/include/asm-x86/mmzone_64.h index 626b03a1487..6480f3333b2 100644 --- a/include/asm-x86/mmzone_64.h +++ b/include/asm-x86/mmzone_64.h @@ -7,7 +7,7 @@ #ifdef CONFIG_NUMA -#define VIRTUAL_BUG_ON(x) +#include <linux/mmdebug.h> #include <asm/smp.h> @@ -29,7 +29,6 @@ static inline __attribute__((pure)) int phys_to_nid(unsigned long addr) { unsigned nid; VIRTUAL_BUG_ON(!memnodemap); - VIRTUAL_BUG_ON((addr >> memnode_shift) >= memnodemapsize); nid = memnodemap[addr >> memnode_shift]; VIRTUAL_BUG_ON(nid >= MAX_NUMNODES || !node_data[nid]); return nid; diff --git a/include/asm-x86/module.h b/include/asm-x86/module.h index 48dc3e0c07d..864f2005fc1 100644 --- a/include/asm-x86/module.h +++ b/include/asm-x86/module.h @@ -52,8 +52,6 @@ struct mod_arch_specific {}; #define MODULE_PROC_FAMILY "EFFICEON " #elif defined CONFIG_MWINCHIPC6 #define MODULE_PROC_FAMILY "WINCHIPC6 " -#elif defined CONFIG_MWINCHIP2 -#define MODULE_PROC_FAMILY "WINCHIP2 " #elif defined CONFIG_MWINCHIP3D #define MODULE_PROC_FAMILY "WINCHIP3D " #elif defined CONFIG_MCYRIXIII diff --git a/include/asm-x86/nmi.h b/include/asm-x86/nmi.h index d5e715f024d..a53f829a97c 100644 --- a/include/asm-x86/nmi.h +++ b/include/asm-x86/nmi.h @@ -15,10 +15,6 @@ */ int do_nmi_callback(struct pt_regs *regs, int cpu); -#ifdef CONFIG_X86_64 -extern void default_do_nmi(struct pt_regs *); -#endif - extern void die_nmi(char *str, struct pt_regs *regs, int do_panic); extern int check_nmi_watchdog(void); extern int nmi_watchdog_enabled; diff --git a/include/asm-x86/page.h b/include/asm-x86/page.h index c9157477675..d4f1d5791fc 100644 --- a/include/asm-x86/page.h +++ b/include/asm-x86/page.h @@ -179,6 +179,7 @@ static inline pteval_t native_pte_flags(pte_t pte) #endif /* CONFIG_PARAVIRT */ #define __pa(x) __phys_addr((unsigned long)(x)) +#define __pa_nodebug(x) __phys_addr_nodebug((unsigned long)(x)) /* __pa_symbol should be used for C visible symbols. This seems to be the official gcc blessed way to do such arithmetic. */ #define __pa_symbol(x) __pa(__phys_reloc_hide((unsigned long)(x))) @@ -188,9 +189,14 @@ static inline pteval_t native_pte_flags(pte_t pte) #define __boot_va(x) __va(x) #define __boot_pa(x) __pa(x) +/* + * virt_to_page(kaddr) returns a valid pointer if and only if + * virt_addr_valid(kaddr) returns true. + */ #define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) -#define virt_addr_valid(kaddr) pfn_valid(__pa(kaddr) >> PAGE_SHIFT) +extern bool __virt_addr_valid(unsigned long kaddr); +#define virt_addr_valid(kaddr) __virt_addr_valid((unsigned long) (kaddr)) #endif /* __ASSEMBLY__ */ diff --git a/include/asm-x86/page_32.h b/include/asm-x86/page_32.h index 72f7305682c..e8d80d1de23 100644 --- a/include/asm-x86/page_32.h +++ b/include/asm-x86/page_32.h @@ -20,6 +20,12 @@ #endif #define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER) +#define STACKFAULT_STACK 0 +#define DOUBLEFAULT_STACK 1 +#define NMI_STACK 0 +#define DEBUG_STACK 0 +#define MCE_STACK 0 +#define N_EXCEPTION_STACKS 1 #ifdef CONFIG_X86_PAE /* 44=32+12, the limit we can fit into an unsigned long pfn */ @@ -73,7 +79,12 @@ typedef struct page *pgtable_t; #endif #ifndef __ASSEMBLY__ -#define __phys_addr(x) ((x) - PAGE_OFFSET) +#define __phys_addr_nodebug(x) ((x) - PAGE_OFFSET) +#ifdef CONFIG_DEBUG_VIRTUAL +extern unsigned long __phys_addr(unsigned long); +#else +#define __phys_addr(x) __phys_addr_nodebug(x) +#endif #define __phys_reloc_hide(x) RELOC_HIDE((x), 0) #ifdef CONFIG_FLATMEM diff --git a/include/asm-x86/paravirt.h b/include/asm-x86/paravirt.h index d7d358a4399..8d6ae2f760d 100644 --- a/include/asm-x86/paravirt.h +++ b/include/asm-x86/paravirt.h @@ -124,6 +124,9 @@ struct pv_cpu_ops { int entrynum, const void *desc, int size); void (*write_idt_entry)(gate_desc *, int entrynum, const gate_desc *gate); + void (*alloc_ldt)(struct desc_struct *ldt, unsigned entries); + void (*free_ldt)(struct desc_struct *ldt, unsigned entries); + void (*load_sp0)(struct tss_struct *tss, struct thread_struct *t); void (*set_iopl_mask)(unsigned mask); @@ -325,6 +328,7 @@ struct pv_lock_ops { int (*spin_is_locked)(struct raw_spinlock *lock); int (*spin_is_contended)(struct raw_spinlock *lock); void (*spin_lock)(struct raw_spinlock *lock); + void (*spin_lock_flags)(struct raw_spinlock *lock, unsigned long flags); int (*spin_trylock)(struct raw_spinlock *lock); void (*spin_unlock)(struct raw_spinlock *lock); }; @@ -830,6 +834,16 @@ do { \ (aux) = __aux; \ } while (0) +static inline void paravirt_alloc_ldt(struct desc_struct *ldt, unsigned entries) +{ + PVOP_VCALL2(pv_cpu_ops.alloc_ldt, ldt, entries); +} + +static inline void paravirt_free_ldt(struct desc_struct *ldt, unsigned entries) +{ + PVOP_VCALL2(pv_cpu_ops.free_ldt, ldt, entries); +} + static inline void load_TR_desc(void) { PVOP_VCALL0(pv_cpu_ops.load_tr_desc); @@ -1394,6 +1408,12 @@ static __always_inline void __raw_spin_lock(struct raw_spinlock *lock) PVOP_VCALL1(pv_lock_ops.spin_lock, lock); } +static __always_inline void __raw_spin_lock_flags(struct raw_spinlock *lock, + unsigned long flags) +{ + PVOP_VCALL2(pv_lock_ops.spin_lock_flags, lock, flags); +} + static __always_inline int __raw_spin_trylock(struct raw_spinlock *lock) { return PVOP_CALL1(int, pv_lock_ops.spin_trylock, lock); diff --git a/include/asm-x86/pgtable.h b/include/asm-x86/pgtable.h index ed932453ef2..182f9d4c570 100644 --- a/include/asm-x86/pgtable.h +++ b/include/asm-x86/pgtable.h @@ -15,7 +15,7 @@ #define _PAGE_BIT_PAT 7 /* on 4KB pages */ #define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */ #define _PAGE_BIT_UNUSED1 9 /* available for programmer */ -#define _PAGE_BIT_UNUSED2 10 +#define _PAGE_BIT_IOMAP 10 /* flag used to indicate IO mapping */ #define _PAGE_BIT_UNUSED3 11 #define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */ #define _PAGE_BIT_SPECIAL _PAGE_BIT_UNUSED1 @@ -32,7 +32,7 @@ #define _PAGE_PSE (_AT(pteval_t, 1) << _PAGE_BIT_PSE) #define _PAGE_GLOBAL (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL) #define _PAGE_UNUSED1 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED1) -#define _PAGE_UNUSED2 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED2) +#define _PAGE_IOMAP (_AT(pteval_t, 1) << _PAGE_BIT_IOMAP) #define _PAGE_UNUSED3 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED3) #define _PAGE_PAT (_AT(pteval_t, 1) << _PAGE_BIT_PAT) #define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE) @@ -99,6 +99,11 @@ #define __PAGE_KERNEL_LARGE_NOCACHE (__PAGE_KERNEL | _PAGE_CACHE_UC | _PAGE_PSE) #define __PAGE_KERNEL_LARGE_EXEC (__PAGE_KERNEL_EXEC | _PAGE_PSE) +#define __PAGE_KERNEL_IO (__PAGE_KERNEL | _PAGE_IOMAP) +#define __PAGE_KERNEL_IO_NOCACHE (__PAGE_KERNEL_NOCACHE | _PAGE_IOMAP) +#define __PAGE_KERNEL_IO_UC_MINUS (__PAGE_KERNEL_UC_MINUS | _PAGE_IOMAP) +#define __PAGE_KERNEL_IO_WC (__PAGE_KERNEL_WC | _PAGE_IOMAP) + #define PAGE_KERNEL __pgprot(__PAGE_KERNEL) #define PAGE_KERNEL_RO __pgprot(__PAGE_KERNEL_RO) #define PAGE_KERNEL_EXEC __pgprot(__PAGE_KERNEL_EXEC) @@ -113,6 +118,11 @@ #define PAGE_KERNEL_VSYSCALL __pgprot(__PAGE_KERNEL_VSYSCALL) #define PAGE_KERNEL_VSYSCALL_NOCACHE __pgprot(__PAGE_KERNEL_VSYSCALL_NOCACHE) +#define PAGE_KERNEL_IO __pgprot(__PAGE_KERNEL_IO) +#define PAGE_KERNEL_IO_NOCACHE __pgprot(__PAGE_KERNEL_IO_NOCACHE) +#define PAGE_KERNEL_IO_UC_MINUS __pgprot(__PAGE_KERNEL_IO_UC_MINUS) +#define PAGE_KERNEL_IO_WC __pgprot(__PAGE_KERNEL_IO_WC) + /* xwr */ #define __P000 PAGE_NONE #define __P001 PAGE_READONLY @@ -196,7 +206,7 @@ static inline int pte_exec(pte_t pte) static inline int pte_special(pte_t pte) { - return pte_val(pte) & _PAGE_SPECIAL; + return pte_flags(pte) & _PAGE_SPECIAL; } static inline unsigned long pte_pfn(pte_t pte) diff --git a/include/asm-x86/processor.h b/include/asm-x86/processor.h index c7d35464a4b..ee7cbb30773 100644 --- a/include/asm-x86/processor.h +++ b/include/asm-x86/processor.h @@ -586,41 +586,6 @@ static inline void clear_in_cr4(unsigned long mask) write_cr4(cr4); } -struct microcode_header { - unsigned int hdrver; - unsigned int rev; - unsigned int date; - unsigned int sig; - unsigned int cksum; - unsigned int ldrver; - unsigned int pf; - unsigned int datasize; - unsigned int totalsize; - unsigned int reserved[3]; -}; - -struct microcode { - struct microcode_header hdr; - unsigned int bits[0]; -}; - -typedef struct microcode microcode_t; -typedef struct microcode_header microcode_header_t; - -/* microcode format is extended from prescott processors */ -struct extended_signature { - unsigned int sig; - unsigned int pf; - unsigned int cksum; -}; - -struct extended_sigtable { - unsigned int count; - unsigned int cksum; - unsigned int reserved[3]; - struct extended_signature sigs[0]; -}; - typedef struct { unsigned long seg; } mm_segment_t; diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h index d64a6109716..a2025525a15 100644 --- a/include/asm-x86/ptrace.h +++ b/include/asm-x86/ptrace.h @@ -174,13 +174,9 @@ extern unsigned long profile_pc(struct pt_regs *regs); extern unsigned long convert_ip_to_linear(struct task_struct *child, struct pt_regs *regs); - -#ifdef CONFIG_X86_32 extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, - int error_code); -#else + int error_code, int si_code); void signal_fault(struct pt_regs *regs, void __user *frame, char *where); -#endif extern long syscall_trace_enter(struct pt_regs *); extern void syscall_trace_leave(struct pt_regs *); diff --git a/include/asm-x86/segment.h b/include/asm-x86/segment.h index ea5f0a8686f..5d6e6945489 100644 --- a/include/asm-x86/segment.h +++ b/include/asm-x86/segment.h @@ -131,12 +131,6 @@ * Matching rules for certain types of segments. */ -/* Matches only __KERNEL_CS, ignoring PnP / USER / APM segments */ -#define SEGMENT_IS_KERNEL_CODE(x) (((x) & 0xfc) == GDT_ENTRY_KERNEL_CS * 8) - -/* Matches __KERNEL_CS and __USER_CS (they must be 2 entries apart) */ -#define SEGMENT_IS_FLAT_CODE(x) (((x) & 0xec) == GDT_ENTRY_KERNEL_CS * 8) - /* Matches PNP_CS32 and PNP_CS16 (they must be consecutive) */ #define SEGMENT_IS_PNP_CODE(x) (((x) & 0xf4) == GDT_ENTRY_PNPBIOS_BASE * 8) diff --git a/include/asm-x86/smp.h b/include/asm-x86/smp.h index 29324c10334..a6afc29f2dd 100644 --- a/include/asm-x86/smp.h +++ b/include/asm-x86/smp.h @@ -50,12 +50,16 @@ extern struct { struct smp_ops { void (*smp_prepare_boot_cpu)(void); void (*smp_prepare_cpus)(unsigned max_cpus); - int (*cpu_up)(unsigned cpu); void (*smp_cpus_done)(unsigned max_cpus); void (*smp_send_stop)(void); void (*smp_send_reschedule)(int cpu); + int (*cpu_up)(unsigned cpu); + int (*cpu_disable)(void); + void (*cpu_die)(unsigned int cpu); + void (*play_dead)(void); + void (*send_call_func_ipi)(cpumask_t mask); void (*send_call_func_single_ipi)(int cpu); }; @@ -94,6 +98,21 @@ static inline int __cpu_up(unsigned int cpu) return smp_ops.cpu_up(cpu); } +static inline int __cpu_disable(void) +{ + return smp_ops.cpu_disable(); +} + +static inline void __cpu_die(unsigned int cpu) +{ + smp_ops.cpu_die(cpu); +} + +static inline void play_dead(void) +{ + smp_ops.play_dead(); +} + static inline void smp_send_reschedule(int cpu) { smp_ops.smp_send_reschedule(cpu); @@ -109,15 +128,20 @@ static inline void arch_send_call_function_ipi(cpumask_t mask) smp_ops.send_call_func_ipi(mask); } +void cpu_disable_common(void); void native_smp_prepare_boot_cpu(void); void native_smp_prepare_cpus(unsigned int max_cpus); void native_smp_cpus_done(unsigned int max_cpus); int native_cpu_up(unsigned int cpunum); +int native_cpu_disable(void); +void native_cpu_die(unsigned int cpu); +void native_play_dead(void); +void play_dead_common(void); + void native_send_call_func_ipi(cpumask_t mask); void native_send_call_func_single_ipi(int cpu); -extern int __cpu_disable(void); -extern void __cpu_die(unsigned int cpu); +extern void prefill_possible_map(void); void smp_store_cpu_info(int id); #define cpu_physical_id(cpu) per_cpu(x86_cpu_to_apicid, cpu) @@ -127,15 +151,11 @@ static inline int num_booting_cpus(void) { return cpus_weight(cpu_callout_map); } -#endif /* CONFIG_SMP */ - -#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_CPU) -extern void prefill_possible_map(void); #else static inline void prefill_possible_map(void) { } -#endif +#endif /* CONFIG_SMP */ extern unsigned disabled_cpus __cpuinitdata; @@ -205,9 +225,5 @@ static inline int hard_smp_processor_id(void) #endif /* CONFIG_X86_LOCAL_APIC */ -#ifdef CONFIG_HOTPLUG_CPU -extern void cpu_uninit(void); -#endif - #endif /* __ASSEMBLY__ */ #endif /* ASM_X86__SMP_H */ diff --git a/include/asm-x86/spinlock.h b/include/asm-x86/spinlock.h index 93adae338ac..157ff7fab97 100644 --- a/include/asm-x86/spinlock.h +++ b/include/asm-x86/spinlock.h @@ -21,8 +21,10 @@ #ifdef CONFIG_X86_32 # define LOCK_PTR_REG "a" +# define REG_PTR_MODE "k" #else # define LOCK_PTR_REG "D" +# define REG_PTR_MODE "q" #endif #if defined(CONFIG_X86_32) && \ @@ -54,19 +56,7 @@ * much between them in performance though, especially as locks are out of line. */ #if (NR_CPUS < 256) -static inline int __ticket_spin_is_locked(raw_spinlock_t *lock) -{ - int tmp = ACCESS_ONCE(lock->slock); - - return (((tmp >> 8) & 0xff) != (tmp & 0xff)); -} - -static inline int __ticket_spin_is_contended(raw_spinlock_t *lock) -{ - int tmp = ACCESS_ONCE(lock->slock); - - return (((tmp >> 8) - tmp) & 0xff) > 1; -} +#define TICKET_SHIFT 8 static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock) { @@ -89,19 +79,17 @@ static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock) static __always_inline int __ticket_spin_trylock(raw_spinlock_t *lock) { - int tmp; - short new; + int tmp, new; - asm volatile("movw %2,%w0\n\t" + asm volatile("movzwl %2, %0\n\t" "cmpb %h0,%b0\n\t" + "leal 0x100(%" REG_PTR_MODE "0), %1\n\t" "jne 1f\n\t" - "movw %w0,%w1\n\t" - "incb %h1\n\t" LOCK_PREFIX "cmpxchgw %w1,%2\n\t" "1:" "sete %b1\n\t" "movzbl %b1,%0\n\t" - : "=&a" (tmp), "=Q" (new), "+m" (lock->slock) + : "=&a" (tmp), "=&q" (new), "+m" (lock->slock) : : "memory", "cc"); @@ -116,19 +104,7 @@ static __always_inline void __ticket_spin_unlock(raw_spinlock_t *lock) : "memory", "cc"); } #else -static inline int __ticket_spin_is_locked(raw_spinlock_t *lock) -{ - int tmp = ACCESS_ONCE(lock->slock); - - return (((tmp >> 16) & 0xffff) != (tmp & 0xffff)); -} - -static inline int __ticket_spin_is_contended(raw_spinlock_t *lock) -{ - int tmp = ACCESS_ONCE(lock->slock); - - return (((tmp >> 16) - tmp) & 0xffff) > 1; -} +#define TICKET_SHIFT 16 static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock) { @@ -146,7 +122,7 @@ static __always_inline void __ticket_spin_lock(raw_spinlock_t *lock) /* don't need lfence here, because loads are in-order */ "jmp 1b\n" "2:" - : "+Q" (inc), "+m" (lock->slock), "=r" (tmp) + : "+r" (inc), "+m" (lock->slock), "=&r" (tmp) : : "memory", "cc"); } @@ -160,13 +136,13 @@ static __always_inline int __ticket_spin_trylock(raw_spinlock_t *lock) "movl %0,%1\n\t" "roll $16, %0\n\t" "cmpl %0,%1\n\t" + "leal 0x00010000(%" REG_PTR_MODE "0), %1\n\t" "jne 1f\n\t" - "addl $0x00010000, %1\n\t" LOCK_PREFIX "cmpxchgl %1,%2\n\t" "1:" "sete %b1\n\t" "movzbl %b1,%0\n\t" - : "=&a" (tmp), "=r" (new), "+m" (lock->slock) + : "=&a" (tmp), "=&q" (new), "+m" (lock->slock) : : "memory", "cc"); @@ -182,7 +158,19 @@ static __always_inline void __ticket_spin_unlock(raw_spinlock_t *lock) } #endif -#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock) +static inline int __ticket_spin_is_locked(raw_spinlock_t *lock) +{ + int tmp = ACCESS_ONCE(lock->slock); + + return !!(((tmp >> TICKET_SHIFT) ^ tmp) & ((1 << TICKET_SHIFT) - 1)); +} + +static inline int __ticket_spin_is_contended(raw_spinlock_t *lock) +{ + int tmp = ACCESS_ONCE(lock->slock); + + return (((tmp >> TICKET_SHIFT) - tmp) & ((1 << TICKET_SHIFT) - 1)) > 1; +} #ifdef CONFIG_PARAVIRT /* @@ -272,6 +260,13 @@ static __always_inline void __raw_spin_unlock(raw_spinlock_t *lock) { __ticket_spin_unlock(lock); } + +static __always_inline void __raw_spin_lock_flags(raw_spinlock_t *lock, + unsigned long flags) +{ + __raw_spin_lock(lock); +} + #endif /* CONFIG_PARAVIRT */ static inline void __raw_spin_unlock_wait(raw_spinlock_t *lock) diff --git a/include/asm-x86/statfs.h b/include/asm-x86/statfs.h index 3f005bc3aa5..ca5dc19dd46 100644 --- a/include/asm-x86/statfs.h +++ b/include/asm-x86/statfs.h @@ -1,63 +1,12 @@ #ifndef ASM_X86__STATFS_H #define ASM_X86__STATFS_H -#ifdef __i386__ -#include <asm-generic/statfs.h> -#else - -#ifndef __KERNEL_STRICT_NAMES - -#include <linux/types.h> - -typedef __kernel_fsid_t fsid_t; - -#endif - /* - * This is ugly -- we're already 64-bit clean, so just duplicate the - * definitions. + * We need compat_statfs64 to be packed, because the i386 ABI won't + * add padding at the end to bring it to a multiple of 8 bytes, but + * the x86_64 ABI will. */ -struct statfs { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; - -struct statfs64 { - long f_type; - long f_bsize; - long f_blocks; - long f_bfree; - long f_bavail; - long f_files; - long f_ffree; - __kernel_fsid_t f_fsid; - long f_namelen; - long f_frsize; - long f_spare[5]; -}; +#define ARCH_PACK_COMPAT_STATFS64 __attribute__((packed,aligned(4))) -struct compat_statfs64 { - __u32 f_type; - __u32 f_bsize; - __u64 f_blocks; - __u64 f_bfree; - __u64 f_bavail; - __u64 f_files; - __u64 f_ffree; - __kernel_fsid_t f_fsid; - __u32 f_namelen; - __u32 f_frsize; - __u32 f_spare[5]; -} __attribute__((packed)); - -#endif /* !__i386__ */ +#include <asm-generic/statfs.h> #endif /* ASM_X86__STATFS_H */ diff --git a/include/asm-x86/system.h b/include/asm-x86/system.h index 34505dd7b24..b20c894660f 100644 --- a/include/asm-x86/system.h +++ b/include/asm-x86/system.h @@ -64,7 +64,10 @@ do { \ \ /* regparm parameters for __switch_to(): */ \ [prev] "a" (prev), \ - [next] "d" (next)); \ + [next] "d" (next) \ + \ + : /* reloaded segment registers */ \ + "memory"); \ } while (0) /* diff --git a/include/asm-x86/tlbflush.h b/include/asm-x86/tlbflush.h index ef68b76dc3c..3cdd08b5bdb 100644 --- a/include/asm-x86/tlbflush.h +++ b/include/asm-x86/tlbflush.h @@ -119,6 +119,10 @@ static inline void native_flush_tlb_others(const cpumask_t *cpumask, { } +static inline void reset_lazy_tlbstate(void) +{ +} + #else /* SMP */ #include <asm/smp.h> @@ -151,6 +155,12 @@ struct tlb_state { char __cacheline_padding[L1_CACHE_BYTES-8]; }; DECLARE_PER_CPU(struct tlb_state, cpu_tlbstate); + +void reset_lazy_tlbstate(void); +#else +static inline void reset_lazy_tlbstate(void) +{ +} #endif #endif /* SMP */ diff --git a/include/asm-x86/traps.h b/include/asm-x86/traps.h index 2ccebc6fb0b..6c3dc2c6575 100644 --- a/include/asm-x86/traps.h +++ b/include/asm-x86/traps.h @@ -1,7 +1,14 @@ #ifndef ASM_X86__TRAPS_H #define ASM_X86__TRAPS_H -/* Common in X86_32 and X86_64 */ +#include <asm/debugreg.h> + +#ifdef CONFIG_X86_32 +#define dotraplinkage +#else +#define dotraplinkage asmlinkage +#endif + asmlinkage void divide_error(void); asmlinkage void debug(void); asmlinkage void nmi(void); @@ -10,61 +17,65 @@ asmlinkage void overflow(void); asmlinkage void bounds(void); asmlinkage void invalid_op(void); asmlinkage void device_not_available(void); +#ifdef CONFIG_X86_64 +asmlinkage void double_fault(void); +#endif asmlinkage void coprocessor_segment_overrun(void); asmlinkage void invalid_TSS(void); asmlinkage void segment_not_present(void); asmlinkage void stack_segment(void); asmlinkage void general_protection(void); asmlinkage void page_fault(void); +asmlinkage void spurious_interrupt_bug(void); asmlinkage void coprocessor_error(void); -asmlinkage void simd_coprocessor_error(void); asmlinkage void alignment_check(void); -asmlinkage void spurious_interrupt_bug(void); #ifdef CONFIG_X86_MCE asmlinkage void machine_check(void); #endif /* CONFIG_X86_MCE */ +asmlinkage void simd_coprocessor_error(void); + +dotraplinkage void do_divide_error(struct pt_regs *, long); +dotraplinkage void do_debug(struct pt_regs *, long); +dotraplinkage void do_nmi(struct pt_regs *, long); +dotraplinkage void do_int3(struct pt_regs *, long); +dotraplinkage void do_overflow(struct pt_regs *, long); +dotraplinkage void do_bounds(struct pt_regs *, long); +dotraplinkage void do_invalid_op(struct pt_regs *, long); +dotraplinkage void do_device_not_available(struct pt_regs *, long); +dotraplinkage void do_coprocessor_segment_overrun(struct pt_regs *, long); +dotraplinkage void do_invalid_TSS(struct pt_regs *, long); +dotraplinkage void do_segment_not_present(struct pt_regs *, long); +dotraplinkage void do_stack_segment(struct pt_regs *, long); +dotraplinkage void do_general_protection(struct pt_regs *, long); +dotraplinkage void do_page_fault(struct pt_regs *, unsigned long); +dotraplinkage void do_spurious_interrupt_bug(struct pt_regs *, long); +dotraplinkage void do_coprocessor_error(struct pt_regs *, long); +dotraplinkage void do_alignment_check(struct pt_regs *, long); +#ifdef CONFIG_X86_MCE +dotraplinkage void do_machine_check(struct pt_regs *, long); +#endif +dotraplinkage void do_simd_coprocessor_error(struct pt_regs *, long); +#ifdef CONFIG_X86_32 +dotraplinkage void do_iret_error(struct pt_regs *, long); +#endif -void do_divide_error(struct pt_regs *, long); -void do_overflow(struct pt_regs *, long); -void do_bounds(struct pt_regs *, long); -void do_coprocessor_segment_overrun(struct pt_regs *, long); -void do_invalid_TSS(struct pt_regs *, long); -void do_segment_not_present(struct pt_regs *, long); -void do_stack_segment(struct pt_regs *, long); -void do_alignment_check(struct pt_regs *, long); -void do_invalid_op(struct pt_regs *, long); -void do_general_protection(struct pt_regs *, long); -void do_nmi(struct pt_regs *, long); +static inline int get_si_code(unsigned long condition) +{ + if (condition & DR_STEP) + return TRAP_TRACE; + else if (condition & (DR_TRAP0|DR_TRAP1|DR_TRAP2|DR_TRAP3)) + return TRAP_HWBKPT; + else + return TRAP_BRKPT; +} extern int panic_on_unrecovered_nmi; extern int kstack_depth_to_print; #ifdef CONFIG_X86_32 - -void do_iret_error(struct pt_regs *, long); -void do_int3(struct pt_regs *, long); -void do_debug(struct pt_regs *, long); void math_error(void __user *); -void do_coprocessor_error(struct pt_regs *, long); -void do_simd_coprocessor_error(struct pt_regs *, long); -void do_spurious_interrupt_bug(struct pt_regs *, long); unsigned long patch_espfix_desc(unsigned long, unsigned long); asmlinkage void math_emulate(long); +#endif -void do_page_fault(struct pt_regs *regs, unsigned long error_code); - -#else /* CONFIG_X86_32 */ - -asmlinkage void double_fault(void); - -asmlinkage void do_int3(struct pt_regs *, long); -asmlinkage void do_stack_segment(struct pt_regs *, long); -asmlinkage void do_debug(struct pt_regs *, unsigned long); -asmlinkage void do_coprocessor_error(struct pt_regs *); -asmlinkage void do_simd_coprocessor_error(struct pt_regs *); -asmlinkage void do_spurious_interrupt_bug(struct pt_regs *); - -asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code); - -#endif /* CONFIG_X86_32 */ #endif /* ASM_X86__TRAPS_H */ diff --git a/include/asm-x86/xen/hypervisor.h b/include/asm-x86/xen/hypervisor.h index 0ef3a88b869..445a2475956 100644 --- a/include/asm-x86/xen/hypervisor.h +++ b/include/asm-x86/xen/hypervisor.h @@ -54,7 +54,6 @@ /* arch/i386/kernel/setup.c */ extern struct shared_info *HYPERVISOR_shared_info; extern struct start_info *xen_start_info; -#define is_initial_xendomain() (xen_start_info->flags & SIF_INITDOMAIN) /* arch/i386/mach-xen/evtchn.c */ /* Force a proper event-channel callback from Xen. */ @@ -67,6 +66,17 @@ u64 jiffies_to_st(unsigned long jiffies); #define MULTI_UVMFLAGS_INDEX 3 #define MULTI_UVMDOMID_INDEX 4 -#define is_running_on_xen() (xen_start_info ? 1 : 0) +enum xen_domain_type { + XEN_NATIVE, + XEN_PV_DOMAIN, + XEN_HVM_DOMAIN, +}; + +extern enum xen_domain_type xen_domain_type; + +#define xen_domain() (xen_domain_type != XEN_NATIVE) +#define xen_pv_domain() (xen_domain_type == XEN_PV_DOMAIN) +#define xen_initial_domain() (xen_pv_domain() && xen_start_info->flags & SIF_INITDOMAIN) +#define xen_hvm_domain() (xen_domain_type == XEN_HVM_DOMAIN) #endif /* ASM_X86__XEN__HYPERVISOR_H */ diff --git a/include/asm-xtensa/a.out.h b/include/asm-xtensa/a.out.h deleted file mode 100644 index fdf13702924..00000000000 --- a/include/asm-xtensa/a.out.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * include/asm-xtensa/a.out.h - * - * Dummy a.out file. Xtensa does not support the a.out format, but the kernel - * seems to depend on it. - * - * 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. - * - * Copyright (C) 2001 - 2005 Tensilica Inc. - */ - -#ifndef _XTENSA_A_OUT_H -#define _XTENSA_A_OUT_H - -struct exec -{ - unsigned long a_info; - unsigned a_text; - unsigned a_data; - unsigned a_bss; - unsigned a_syms; - unsigned a_entry; - unsigned a_trsize; - unsigned a_drsize; -}; - -#endif /* _XTENSA_A_OUT_H */ diff --git a/include/linux/devpts_fs.h b/include/linux/devpts_fs.h index 154769cad3f..5ce0e5fd712 100644 --- a/include/linux/devpts_fs.h +++ b/include/linux/devpts_fs.h @@ -17,20 +17,31 @@ #ifdef CONFIG_UNIX98_PTYS -int devpts_new_index(void); -void devpts_kill_index(int idx); -int devpts_pty_new(struct tty_struct *tty); /* mknod in devpts */ -struct tty_struct *devpts_get_tty(int number); /* get tty structure */ -void devpts_pty_kill(int number); /* unlink */ +int devpts_new_index(struct inode *ptmx_inode); +void devpts_kill_index(struct inode *ptmx_inode, int idx); +/* mknod in devpts */ +int devpts_pty_new(struct inode *ptmx_inode, struct tty_struct *tty); +/* get tty structure */ +struct tty_struct *devpts_get_tty(struct inode *pts_inode, int number); +/* unlink */ +void devpts_pty_kill(struct tty_struct *tty); #else /* Dummy stubs in the no-pty case */ -static inline int devpts_new_index(void) { return -EINVAL; } -static inline void devpts_kill_index(int idx) { } -static inline int devpts_pty_new(struct tty_struct *tty) { return -EINVAL; } -static inline struct tty_struct *devpts_get_tty(int number) { return NULL; } -static inline void devpts_pty_kill(int number) { } +static inline int devpts_new_index(struct inode *ptmx_inode) { return -EINVAL; } +static inline void devpts_kill_index(struct inode *ptmx_inode, int idx) { } +static inline int devpts_pty_new(struct inode *ptmx_inode, + struct tty_struct *tty) +{ + return -EINVAL; +} +static inline struct tty_struct *devpts_get_tty(struct inode *pts_inode, + int number) +{ + return NULL; +} +static inline void devpts_pty_kill(struct tty_struct *tty) { } #endif diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 2a063b64133..e5084eb5943 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -2,29 +2,9 @@ #define __DMI_H__ #include <linux/list.h> +#include <linux/mod_devicetable.h> -enum dmi_field { - DMI_NONE, - DMI_BIOS_VENDOR, - DMI_BIOS_VERSION, - DMI_BIOS_DATE, - DMI_SYS_VENDOR, - DMI_PRODUCT_NAME, - DMI_PRODUCT_VERSION, - DMI_PRODUCT_SERIAL, - DMI_PRODUCT_UUID, - DMI_BOARD_VENDOR, - DMI_BOARD_NAME, - DMI_BOARD_VERSION, - DMI_BOARD_SERIAL, - DMI_BOARD_ASSET_TAG, - DMI_CHASSIS_VENDOR, - DMI_CHASSIS_TYPE, - DMI_CHASSIS_VERSION, - DMI_CHASSIS_SERIAL, - DMI_CHASSIS_ASSET_TAG, - DMI_STRING_MAX, -}; +/* enum dmi_field is in mod_devicetable.h */ enum dmi_device_type { DMI_DEV_TYPE_ANY = 0, @@ -48,23 +28,6 @@ struct dmi_header { u16 handle; }; -/* - * DMI callbacks for problem boards - */ -struct dmi_strmatch { - u8 slot; - char *substr; -}; - -struct dmi_system_id { - int (*callback)(const struct dmi_system_id *); - const char *ident; - struct dmi_strmatch matches[4]; - void *driver_data; -}; - -#define DMI_MATCH(a, b) { a, b } - struct dmi_device { struct list_head list; int type; diff --git a/include/linux/elf.h b/include/linux/elf.h index edc3dac3f02..0b61ca41a04 100644 --- a/include/linux/elf.h +++ b/include/linux/elf.h @@ -360,6 +360,7 @@ typedef struct elf64_shdr { #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ /* Note header in a PT_NOTE section */ diff --git a/include/linux/hpet.h b/include/linux/hpet.h index 2dc29ce6c8e..79f63a27bce 100644 --- a/include/linux/hpet.h +++ b/include/linux/hpet.h @@ -37,6 +37,7 @@ struct hpet { #define hpet_compare _u1._hpet_compare #define HPET_MAX_TIMERS (32) +#define HPET_MAX_IRQ (32) /* * HPET general capabilities register @@ -64,7 +65,7 @@ struct hpet { */ #define Tn_INT_ROUTE_CAP_MASK (0xffffffff00000000ULL) -#define Tn_INI_ROUTE_CAP_SHIFT (32UL) +#define Tn_INT_ROUTE_CAP_SHIFT (32UL) #define Tn_FSB_INT_DELCAP_MASK (0x8000UL) #define Tn_FSB_INT_DELCAP_SHIFT (15) #define Tn_FSB_EN_CNF_MASK (0x4000UL) @@ -91,23 +92,14 @@ struct hpet { * exported interfaces */ -struct hpet_task { - void (*ht_func) (void *); - void *ht_data; - void *ht_opaque; -}; - struct hpet_data { unsigned long hd_phys_address; void __iomem *hd_address; unsigned short hd_nirqs; - unsigned short hd_flags; unsigned int hd_state; /* timer allocated */ unsigned int hd_irq[HPET_MAX_TIMERS]; }; -#define HPET_DATA_PLATFORM 0x0001 /* platform call to hpet_alloc */ - static inline void hpet_reserve_timer(struct hpet_data *hd, int timer) { hd->hd_state |= (1 << timer); @@ -125,7 +117,7 @@ struct hpet_info { unsigned short hi_timer; }; -#define HPET_INFO_PERIODIC 0x0001 /* timer is periodic */ +#define HPET_INFO_PERIODIC 0x0010 /* periodic-capable comparator */ #define HPET_IE_ON _IO('h', 0x01) /* interrupt on */ #define HPET_IE_OFF _IO('h', 0x02) /* interrupt off */ diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 66c3499478b..d2e91ea998f 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -967,6 +967,9 @@ struct journal_s #define JBD2_FLUSHED 0x008 /* The journal superblock has been flushed */ #define JBD2_LOADED 0x010 /* The journal superblock has been loaded */ #define JBD2_BARRIER 0x020 /* Use IDE barriers */ +#define JBD2_ABORT_ON_SYNCDATA_ERR 0x040 /* Abort the journal on file + * data write error in ordered + * mode */ /* * Function declarations for the journaling transaction and buffer @@ -1060,7 +1063,7 @@ extern void jbd2_journal_clear_features (journal_t *, unsigned long, unsigned long, unsigned long); extern int jbd2_journal_create (journal_t *); extern int jbd2_journal_load (journal_t *journal); -extern void jbd2_journal_destroy (journal_t *); +extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_wipe (journal_t *, int); extern int jbd2_journal_skip_recovery (journal_t *); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 2651f805ba6..75d81f157d2 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -182,7 +182,7 @@ extern int vsscanf(const char *, const char *, va_list) extern int get_option(char **str, int *pint); extern char *get_options(const char *str, int nints, int *ints); -extern unsigned long long memparse(char *ptr, char **retptr); +extern unsigned long long memparse(const char *ptr, char **retptr); extern int core_kernel_text(unsigned long addr); extern int __kernel_text_address(unsigned long addr); diff --git a/include/linux/key.h b/include/linux/key.h index c45c962d1cc..1b70e35a71e 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -299,6 +299,7 @@ extern void key_init(void); #define key_validate(k) 0 #define key_serial(k) 0 #define key_get(k) ({ NULL; }) +#define key_revoke(k) do { } while(0) #define key_put(k) do { } while(0) #define key_ref_put(k) do { } while(0) #define make_key_ref(k, p) ({ NULL; }) diff --git a/include/linux/magic.h b/include/linux/magic.h index 1fa0c2ce4de..f7f3fdddbef 100644 --- a/include/linux/magic.h +++ b/include/linux/magic.h @@ -6,6 +6,10 @@ #define AFS_SUPER_MAGIC 0x5346414F #define AUTOFS_SUPER_MAGIC 0x0187 #define CODA_SUPER_MAGIC 0x73757245 +#define DEBUGFS_MAGIC 0x64626720 +#define SYSFS_MAGIC 0x62656572 +#define SECURITYFS_MAGIC 0x73636673 +#define TMPFS_MAGIC 0x01021994 #define EFS_SUPER_MAGIC 0x414A53 #define EXT2_SUPER_MAGIC 0xEF53 #define EXT3_SUPER_MAGIC 0xEF53 diff --git a/include/linux/mm.h b/include/linux/mm.h index 72a15dc26bb..c61ba10768e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -7,6 +7,7 @@ #include <linux/gfp.h> #include <linux/list.h> +#include <linux/mmdebug.h> #include <linux/mmzone.h> #include <linux/rbtree.h> #include <linux/prio_tree.h> @@ -219,12 +220,6 @@ struct inode; */ #include <linux/page-flags.h> -#ifdef CONFIG_DEBUG_VM -#define VM_BUG_ON(cond) BUG_ON(cond) -#else -#define VM_BUG_ON(condition) do { } while(0) -#endif - /* * Methods to modify the page usage count. * @@ -919,7 +914,7 @@ static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long a } #endif /* CONFIG_MMU && !__ARCH_HAS_4LEVEL_HACK */ -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS /* * We tuck a spinlock to guard each pagetable page into its struct page, * at page->private, with BUILD_BUG_ON to make sure that this will not @@ -932,14 +927,14 @@ static inline pmd_t *pmd_alloc(struct mm_struct *mm, pud_t *pud, unsigned long a } while (0) #define pte_lock_deinit(page) ((page)->mapping = NULL) #define pte_lockptr(mm, pmd) ({(void)(mm); __pte_lockptr(pmd_page(*(pmd)));}) -#else +#else /* !USE_SPLIT_PTLOCKS */ /* * We use mm->page_table_lock to guard all pagetable pages of the mm. */ #define pte_lock_init(page) do {} while (0) #define pte_lock_deinit(page) do {} while (0) #define pte_lockptr(mm, pmd) ({(void)(pmd); &(mm)->page_table_lock;}) -#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#endif /* USE_SPLIT_PTLOCKS */ static inline void pgtable_page_ctor(struct page *page) { diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index bf334138c7c..9d49fa36bbe 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -21,11 +21,13 @@ struct address_space; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#define USE_SPLIT_PTLOCKS (NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS) + +#if USE_SPLIT_PTLOCKS typedef atomic_long_t mm_counter_t; -#else /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#else /* !USE_SPLIT_PTLOCKS */ typedef unsigned long mm_counter_t; -#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#endif /* !USE_SPLIT_PTLOCKS */ /* * Each physical page in the system has a struct page associated with @@ -65,7 +67,7 @@ struct page { * see PAGE_MAPPING_ANON below. */ }; -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS spinlock_t ptl; #endif struct kmem_cache *slab; /* SLUB: Pointer to slab */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9c288c90987..bde891f6459 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -65,7 +65,7 @@ struct mmc_host_ops { * -ENOSYS when not supported (equal to NULL callback) * or a negative errno value when something bad happened * - * Return values for the get_ro callback should be: + * Return values for the get_cd callback should be: * 0 for a absent card * 1 for a present card * -ENOSYS when not supported (equal to NULL callback) diff --git a/include/linux/mmdebug.h b/include/linux/mmdebug.h new file mode 100644 index 00000000000..8a550987719 --- /dev/null +++ b/include/linux/mmdebug.h @@ -0,0 +1,18 @@ +#ifndef LINUX_MM_DEBUG_H +#define LINUX_MM_DEBUG_H 1 + +#include <linux/autoconf.h> + +#ifdef CONFIG_DEBUG_VM +#define VM_BUG_ON(cond) BUG_ON(cond) +#else +#define VM_BUG_ON(cond) do { } while (0) +#endif + +#ifdef CONFIG_DEBUG_VIRTUAL +#define VIRTUAL_BUG_ON(cond) BUG_ON(cond) +#else +#define VIRTUAL_BUG_ON(cond) do { } while (0) +#endif + +#endif diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index c4db5827963..3481a7d5bc0 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -388,5 +388,52 @@ struct i2c_device_id { __attribute__((aligned(sizeof(kernel_ulong_t)))); }; +/* dmi */ +enum dmi_field { + DMI_NONE, + DMI_BIOS_VENDOR, + DMI_BIOS_VERSION, + DMI_BIOS_DATE, + DMI_SYS_VENDOR, + DMI_PRODUCT_NAME, + DMI_PRODUCT_VERSION, + DMI_PRODUCT_SERIAL, + DMI_PRODUCT_UUID, + DMI_BOARD_VENDOR, + DMI_BOARD_NAME, + DMI_BOARD_VERSION, + DMI_BOARD_SERIAL, + DMI_BOARD_ASSET_TAG, + DMI_CHASSIS_VENDOR, + DMI_CHASSIS_TYPE, + DMI_CHASSIS_VERSION, + DMI_CHASSIS_SERIAL, + DMI_CHASSIS_ASSET_TAG, + DMI_STRING_MAX, +}; + +struct dmi_strmatch { + unsigned char slot; + char substr[79]; +}; + +#ifndef __KERNEL__ +struct dmi_system_id { + kernel_ulong_t callback; + kernel_ulong_t ident; + struct dmi_strmatch matches[4]; + kernel_ulong_t driver_data + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; +#else +struct dmi_system_id { + int (*callback)(const struct dmi_system_id *); + const char *ident; + struct dmi_strmatch matches[4]; + void *driver_data; +}; +#endif + +#define DMI_MATCH(a, b) { a, b } #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/oprofile.h b/include/linux/oprofile.h index 041bb31100f..bcb8f725427 100644 --- a/include/linux/oprofile.h +++ b/include/linux/oprofile.h @@ -36,6 +36,8 @@ #define XEN_ENTER_SWITCH_CODE 10 #define SPU_PROFILING_CODE 11 #define SPU_CTX_SWITCH_CODE 12 +#define IBS_FETCH_CODE 13 +#define IBS_OP_CODE 14 struct super_block; struct dentry; diff --git a/include/linux/parser.h b/include/linux/parser.h index 7dcd0507575..ea2281e726f 100644 --- a/include/linux/parser.h +++ b/include/linux/parser.h @@ -25,7 +25,7 @@ typedef struct { char *to; } substring_t; -int match_token(char *, match_table_t table, substring_t args[]); +int match_token(char *, const match_table_t table, substring_t args[]); int match_int(substring_t *, int *result); int match_octal(substring_t *, int *result); int match_hex(substring_t *, int *result); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index f63b5455801..1176f1f177e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1533,7 +1533,9 @@ #define PCI_DEVICE_ID_MARVELL_GT64260 0x6430 #define PCI_DEVICE_ID_MARVELL_MV64360 0x6460 #define PCI_DEVICE_ID_MARVELL_MV64460 0x6480 -#define PCI_DEVICE_ID_MARVELL_CAFE_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_NAND 0x4100 +#define PCI_DEVICE_ID_MARVELL_88ALP01_SD 0x4101 +#define PCI_DEVICE_ID_MARVELL_88ALP01_CCIC 0x4102 #define PCI_VENDOR_ID_V3 0x11b0 #define PCI_DEVICE_ID_V3_V960 0x0001 diff --git a/include/linux/rtc/m48t59.h b/include/linux/rtc/m48t59.h index e8c7c21ceb1..6fc961459b4 100644 --- a/include/linux/rtc/m48t59.h +++ b/include/linux/rtc/m48t59.h @@ -18,40 +18,47 @@ /* * M48T59 Register Offset */ -#define M48T59_YEAR 0x1fff -#define M48T59_MONTH 0x1ffe -#define M48T59_MDAY 0x1ffd /* Day of Month */ -#define M48T59_WDAY 0x1ffc /* Day of Week */ +#define M48T59_YEAR 0xf +#define M48T59_MONTH 0xe +#define M48T59_MDAY 0xd /* Day of Month */ +#define M48T59_WDAY 0xc /* Day of Week */ #define M48T59_WDAY_CB 0x20 /* Century Bit */ #define M48T59_WDAY_CEB 0x10 /* Century Enable Bit */ -#define M48T59_HOUR 0x1ffb -#define M48T59_MIN 0x1ffa -#define M48T59_SEC 0x1ff9 -#define M48T59_CNTL 0x1ff8 +#define M48T59_HOUR 0xb +#define M48T59_MIN 0xa +#define M48T59_SEC 0x9 +#define M48T59_CNTL 0x8 #define M48T59_CNTL_READ 0x40 #define M48T59_CNTL_WRITE 0x80 -#define M48T59_WATCHDOG 0x1ff7 -#define M48T59_INTR 0x1ff6 +#define M48T59_WATCHDOG 0x7 +#define M48T59_INTR 0x6 #define M48T59_INTR_AFE 0x80 /* Alarm Interrupt Enable */ #define M48T59_INTR_ABE 0x20 -#define M48T59_ALARM_DATE 0x1ff5 -#define M48T59_ALARM_HOUR 0x1ff4 -#define M48T59_ALARM_MIN 0x1ff3 -#define M48T59_ALARM_SEC 0x1ff2 -#define M48T59_UNUSED 0x1ff1 -#define M48T59_FLAGS 0x1ff0 +#define M48T59_ALARM_DATE 0x5 +#define M48T59_ALARM_HOUR 0x4 +#define M48T59_ALARM_MIN 0x3 +#define M48T59_ALARM_SEC 0x2 +#define M48T59_UNUSED 0x1 +#define M48T59_FLAGS 0x0 #define M48T59_FLAGS_WDT 0x80 /* watchdog timer expired */ #define M48T59_FLAGS_AF 0x40 /* alarm */ #define M48T59_FLAGS_BF 0x10 /* low battery */ -#define M48T59_NVRAM_SIZE 0x1ff0 +#define M48T59RTC_TYPE_M48T59 0 /* to keep compatibility */ +#define M48T59RTC_TYPE_M48T02 1 +#define M48T59RTC_TYPE_M48T08 2 struct m48t59_plat_data { - /* The method to access M48T59 registers, - * NOTE: The 'ofs' should be 0x00~0x1fff - */ + /* The method to access M48T59 registers */ void (*write_byte)(struct device *dev, u32 ofs, u8 val); unsigned char (*read_byte)(struct device *dev, u32 ofs); + + int type; /* RTC model */ + + /* ioaddr mapped externally */ + void __iomem *ioaddr; + /* offset to RTC registers, automatically set according to the type */ + unsigned int offset; }; #endif /* _LINUX_RTC_M48T59_H_ */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 5d0819ee442..c226c7b8294 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -352,7 +352,7 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr, extern void arch_unmap_area(struct mm_struct *, unsigned long); extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); -#if NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS +#if USE_SPLIT_PTLOCKS /* * The mm counters are not protected by its page_table_lock, * so must be incremented atomically. @@ -363,7 +363,7 @@ extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); #define inc_mm_counter(mm, member) atomic_long_inc(&(mm)->_##member) #define dec_mm_counter(mm, member) atomic_long_dec(&(mm)->_##member) -#else /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#else /* !USE_SPLIT_PTLOCKS */ /* * The mm counters are protected by its page_table_lock, * so can be incremented directly. @@ -374,7 +374,7 @@ extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long); #define inc_mm_counter(mm, member) (mm)->_##member++ #define dec_mm_counter(mm, member) (mm)->_##member-- -#endif /* NR_CPUS < CONFIG_SPLIT_PTLOCK_CPUS */ +#endif /* !USE_SPLIT_PTLOCKS */ #define get_mm_rss(mm) \ (get_mm_counter(mm, file_rss) + get_mm_counter(mm, anon_rss)) diff --git a/include/linux/serial.h b/include/linux/serial.h index deb714314fb..1ea8d9265bf 100644 --- a/include/linux/serial.h +++ b/include/linux/serial.h @@ -173,6 +173,22 @@ struct serial_icounter_struct { int reserved[9]; }; +/* + * Serial interface for controlling RS485 settings on chips with suitable + * support. Set with TIOCSRS485 and get with TIOCGRS485 if supported by your + * platform. The set function returns the new state, with any unsupported bits + * reverted appropriately. + */ + +struct serial_rs485 { + __u32 flags; /* RS485 feature flags */ +#define SER_RS485_ENABLED (1 << 0) +#define SER_RS485_RTS_ON_SEND (1 << 1) +#define SER_RS485_RTS_AFTER_SEND (1 << 2) + __u32 delay_rts_before_send; /* Milliseconds */ + __u32 padding[6]; /* Memory is cheap, new structs + are a royal PITA .. */ +}; #ifdef __KERNEL__ #include <linux/compiler.h> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 3b2f6c04855..e27f216361f 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -241,7 +241,7 @@ typedef unsigned int __bitwise__ upf_t; struct uart_port { spinlock_t lock; /* port lock */ - unsigned int iobase; /* in/out[bwl] */ + unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ diff --git a/include/linux/termios.h b/include/linux/termios.h index 478662889f4..2acd0c1f8a2 100644 --- a/include/linux/termios.h +++ b/include/linux/termios.h @@ -4,4 +4,19 @@ #include <linux/types.h> #include <asm/termios.h> +#define NFF 5 + +struct termiox +{ + __u16 x_hflag; + __u16 x_cflag; + __u16 x_rflag[NFF]; + __u16 x_sflag; +}; + +#define RTSXOFF 0x0001 /* RTS flow control on input */ +#define CTSXON 0x0002 /* CTS flow control on output */ +#define DTRXOFF 0x0004 /* DTR flow control on input */ +#define DSRXON 0x0008 /* DCD flow control on output */ + #endif diff --git a/include/linux/tty.h b/include/linux/tty.h index 0cbec74ec08..3b8121d4e36 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -23,7 +23,7 @@ */ #define NR_UNIX98_PTY_DEFAULT 4096 /* Default maximum for Unix98 ptys */ #define NR_UNIX98_PTY_MAX (1 << MINORBITS) /* Absolute limit */ -#define NR_LDISCS 18 +#define NR_LDISCS 19 /* line disciplines */ #define N_TTY 0 @@ -45,6 +45,7 @@ #define N_HCI 15 /* Bluetooth HCI UART */ #define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */ #define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */ +#define N_PPS 18 /* Pulse per Second */ /* * This character is the same as _POSIX_VDISABLE: it cannot be used as @@ -181,6 +182,7 @@ struct signal_struct; struct tty_port { struct tty_struct *tty; /* Back pointer */ + spinlock_t lock; /* Lock protecting tty field */ int blocked_open; /* Waiting to open */ int count; /* Usage count */ wait_queue_head_t open_wait; /* Open waiters */ @@ -208,6 +210,7 @@ struct tty_operations; struct tty_struct { int magic; + struct kref kref; struct tty_driver *driver; const struct tty_operations *ops; int index; @@ -217,6 +220,7 @@ struct tty_struct { spinlock_t ctrl_lock; /* Termios values are protected by the termios mutex */ struct ktermios *termios, *termios_locked; + struct termiox *termiox; /* May be NULL for unsupported */ char name[64]; struct pid *pgrp; /* Protected by ctrl lock */ struct pid *session; @@ -310,6 +314,25 @@ extern int kmsg_redirect; extern void console_init(void); extern int vcs_init(void); +extern struct class *tty_class; + +/** + * tty_kref_get - get a tty reference + * @tty: tty device + * + * Return a new reference to a tty object. The caller must hold + * sufficient locks/counts to ensure that their existing reference cannot + * go away + */ + +extern inline struct tty_struct *tty_kref_get(struct tty_struct *tty) +{ + if (tty) + kref_get(&tty->kref); + return tty; +} +extern void tty_kref_put(struct tty_struct *tty); + extern int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, const char *routine); extern char *tty_name(struct tty_struct *tty, char *buf); @@ -333,13 +356,15 @@ extern void tty_throttle(struct tty_struct *tty); extern void tty_unthrottle(struct tty_struct *tty); extern int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize *ws); - +extern void tty_shutdown(struct tty_struct *tty); +extern void tty_free_termios(struct tty_struct *tty); extern int is_current_pgrp_orphaned(void); extern struct pid *tty_get_pgrp(struct tty_struct *tty); extern int is_ignored(int sig); extern int tty_signal(int sig, struct tty_struct *tty); extern void tty_hangup(struct tty_struct *tty); extern void tty_vhangup(struct tty_struct *tty); +extern void tty_vhangup_self(void); extern void tty_unhangup(struct file *filp); extern int tty_hung_up_p(struct file *filp); extern void do_SAK(struct tty_struct *tty); @@ -347,6 +372,9 @@ extern void __do_SAK(struct tty_struct *tty); extern void disassociate_ctty(int priv); extern void no_tty(void); extern void tty_flip_buffer_push(struct tty_struct *tty); +extern void tty_buffer_free_all(struct tty_struct *tty); +extern void tty_buffer_flush(struct tty_struct *tty); +extern void tty_buffer_init(struct tty_struct *tty); extern speed_t tty_get_baud_rate(struct tty_struct *tty); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); @@ -372,6 +400,15 @@ extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg); extern dev_t tty_devnum(struct tty_struct *tty); extern void proc_clear_tty(struct task_struct *p); extern struct tty_struct *get_current_tty(void); +extern void tty_default_fops(struct file_operations *fops); +extern struct tty_struct *alloc_tty_struct(void); +extern void free_tty_struct(struct tty_struct *tty); +extern void initialize_tty_struct(struct tty_struct *tty, + struct tty_driver *driver, int idx); +extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, + int first_ok); +extern void tty_release_dev(struct file *filp); +extern int tty_init_termios(struct tty_struct *tty); extern struct mutex tty_mutex; @@ -382,6 +419,8 @@ extern int tty_write_lock(struct tty_struct *tty, int ndelay); extern void tty_port_init(struct tty_port *port); extern int tty_port_alloc_xmit_buf(struct tty_port *port); extern void tty_port_free_xmit_buf(struct tty_port *port); +extern struct tty_struct *tty_port_tty_get(struct tty_port *port); +extern void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); @@ -427,7 +466,7 @@ static inline void tty_audit_push_task(struct task_struct *tsk, #endif /* tty_ioctl.c */ -extern int n_tty_ioctl(struct tty_struct *tty, struct file *file, +extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); /* serial.c */ diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 16d27944c32..78416b90158 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -7,6 +7,28 @@ * defined; unless noted otherwise, they are optional, and can be * filled in with a null pointer. * + * struct tty_struct * (*lookup)(struct tty_driver *self, int idx) + * + * Return the tty device corresponding to idx, NULL if there is not + * one currently in use and an ERR_PTR value on error. Called under + * tty_mutex (for now!) + * + * Optional method. Default behaviour is to use the ttys array + * + * int (*install)(struct tty_driver *self, struct tty_struct *tty) + * + * Install a new tty into the tty driver internal tables. Used in + * conjunction with lookup and remove methods. + * + * Optional method. Default behaviour is to use the ttys array + * + * void (*remove)(struct tty_driver *self, struct tty_struct *tty) + * + * Remove a closed tty from the tty driver internal tables. Used in + * conjunction with lookup and remove methods. + * + * Optional method. Default behaviour is to use the ttys array + * * int (*open)(struct tty_struct * tty, struct file * filp); * * This routine is called when a particular tty device is opened. @@ -21,6 +43,11 @@ * * Required method. * + * void (*shutdown)(struct tty_struct * tty); + * + * This routine is called when a particular tty device is closed for + * the last time freeing up the resources. + * * int (*write)(struct tty_struct * tty, * const unsigned char *buf, int count); * @@ -180,6 +207,14 @@ * not force errors here if they are not resizable objects (eg a serial * line). See tty_do_resize() if you need to wrap the standard method * in your own logic - the usual case. + * + * void (*set_termiox)(struct tty_struct *tty, struct termiox *new); + * + * Called when the device receives a termiox based ioctl. Passes down + * the requested data from user space. This method will not be invoked + * unless the tty also has a valid tty->termiox pointer. + * + * Optional: Called under the termios lock */ #include <linux/fs.h> @@ -190,8 +225,13 @@ struct tty_struct; struct tty_driver; struct tty_operations { + struct tty_struct * (*lookup)(struct tty_driver *driver, + struct inode *inode, int idx); + int (*install)(struct tty_driver *driver, struct tty_struct *tty); + void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); + void (*shutdown)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count); int (*put_char)(struct tty_struct *tty, unsigned char ch); @@ -220,6 +260,7 @@ struct tty_operations { unsigned int set, unsigned int clear); int (*resize)(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize *ws); + int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew); #ifdef CONFIG_CONSOLE_POLL int (*poll_init)(struct tty_driver *driver, int line, char *options); int (*poll_get_char)(struct tty_driver *driver, int line); @@ -229,6 +270,7 @@ struct tty_operations { struct tty_driver { int magic; /* magic number for this structure */ + struct kref kref; /* Reference management */ struct cdev cdev; struct module *owner; const char *driver_name; @@ -242,7 +284,6 @@ struct tty_driver { short subtype; /* subtype of tty driver */ struct ktermios init_termios; /* Initial termios */ int flags; /* tty driver flags */ - int refcount; /* for loadable tty drivers */ struct proc_dir_entry *proc_entry; /* /proc fs entry */ struct tty_driver *other; /* only used for the PTY driver */ @@ -264,12 +305,19 @@ struct tty_driver { extern struct list_head tty_drivers; -struct tty_driver *alloc_tty_driver(int lines); -void put_tty_driver(struct tty_driver *driver); -void tty_set_operations(struct tty_driver *driver, +extern struct tty_driver *alloc_tty_driver(int lines); +extern void put_tty_driver(struct tty_driver *driver); +extern void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op); extern struct tty_driver *tty_find_polling_driver(char *name, int *line); +extern void tty_driver_kref_put(struct tty_driver *driver); +extern inline struct tty_driver *tty_driver_kref_get(struct tty_driver *d) +{ + kref_get(&d->kref); + return d; +} + /* tty driver magic number */ #define TTY_DRIVER_MAGIC 0x5402 diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h new file mode 100644 index 00000000000..5b88e36c910 --- /dev/null +++ b/include/linux/usb/ehci_def.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2001-2002 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __LINUX_USB_EHCI_DEF_H +#define __LINUX_USB_EHCI_DEF_H + +/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ + +/* Section 2.2 Host Controller Capability Registers */ +struct ehci_caps { + /* these fields are specified as 8 and 16 bit registers, + * but some hosts can't perform 8 or 16 bit PCI accesses. + */ + u32 hc_capbase; +#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ +#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ + u32 hcs_params; /* HCSPARAMS - offset 0x4 */ +#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ +#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ +#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */ +#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */ +#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */ +#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */ +#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ + + u32 hcc_params; /* HCCPARAMS - offset 0x8 */ +#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */ +#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */ +#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */ +#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ +#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ +#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ + u8 portroute [8]; /* nibbles for routing - offset 0xC */ +} __attribute__ ((packed)); + + +/* Section 2.3 Host Controller Operational Registers */ +struct ehci_regs { + + /* USBCMD: offset 0x00 */ + u32 command; +/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ +#define CMD_PARK (1<<11) /* enable "park" on async qh */ +#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ +#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */ +#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ +#define CMD_ASE (1<<5) /* async schedule enable */ +#define CMD_PSE (1<<4) /* periodic schedule enable */ +/* 3:2 is periodic frame list size */ +#define CMD_RESET (1<<1) /* reset HC not bus */ +#define CMD_RUN (1<<0) /* start/stop HC */ + + /* USBSTS: offset 0x04 */ + u32 status; +#define STS_ASS (1<<15) /* Async Schedule Status */ +#define STS_PSS (1<<14) /* Periodic Schedule Status */ +#define STS_RECL (1<<13) /* Reclamation */ +#define STS_HALT (1<<12) /* Not running (any reason) */ +/* some bits reserved */ + /* these STS_* flags are also intr_enable bits (USBINTR) */ +#define STS_IAA (1<<5) /* Interrupted on async advance */ +#define STS_FATAL (1<<4) /* such as some PCI access errors */ +#define STS_FLR (1<<3) /* frame list rolled over */ +#define STS_PCD (1<<2) /* port change detect */ +#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ +#define STS_INT (1<<0) /* "normal" completion (short, ...) */ + + /* USBINTR: offset 0x08 */ + u32 intr_enable; + + /* FRINDEX: offset 0x0C */ + u32 frame_index; /* current microframe number */ + /* CTRLDSSEGMENT: offset 0x10 */ + u32 segment; /* address bits 63:32 if needed */ + /* PERIODICLISTBASE: offset 0x14 */ + u32 frame_list; /* points to periodic list */ + /* ASYNCLISTADDR: offset 0x18 */ + u32 async_next; /* address of next async queue head */ + + u32 reserved [9]; + + /* CONFIGFLAG: offset 0x40 */ + u32 configured_flag; +#define FLAG_CF (1<<0) /* true: we'll support "high speed" */ + + /* PORTSC: offset 0x44 */ + u32 port_status [0]; /* up to N_PORTS */ +/* 31:23 reserved */ +#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */ +#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */ +#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */ +/* 19:16 for port testing */ +#define PORT_LED_OFF (0<<14) +#define PORT_LED_AMBER (1<<14) +#define PORT_LED_GREEN (2<<14) +#define PORT_LED_MASK (3<<14) +#define PORT_OWNER (1<<13) /* true: companion hc owns this port */ +#define PORT_POWER (1<<12) /* true: has power (see PPC) */ +#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ +/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +/* 9 reserved */ +#define PORT_RESET (1<<8) /* reset port */ +#define PORT_SUSPEND (1<<7) /* suspend port */ +#define PORT_RESUME (1<<6) /* resume it */ +#define PORT_OCC (1<<5) /* over current change */ +#define PORT_OC (1<<4) /* over current active */ +#define PORT_PEC (1<<3) /* port enable change */ +#define PORT_PE (1<<2) /* port enable */ +#define PORT_CSC (1<<1) /* connect status change */ +#define PORT_CONNECT (1<<0) /* device connected */ +#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) +} __attribute__ ((packed)); + +#define USBMODE 0x68 /* USB Device mode */ +#define USBMODE_SDIS (1<<3) /* Stream disable */ +#define USBMODE_BE (1<<2) /* BE/LE endianness select */ +#define USBMODE_CM_HC (3<<0) /* host controller mode */ +#define USBMODE_CM_IDLE (0<<0) /* idle state */ + +/* Appendix C, Debug port ... intended for use with special "debug devices" + * that can help if there's no serial console. (nonstandard enumeration.) + */ +struct ehci_dbg_port { + u32 control; +#define DBGP_OWNER (1<<30) +#define DBGP_ENABLED (1<<28) +#define DBGP_DONE (1<<16) +#define DBGP_INUSE (1<<10) +#define DBGP_ERRCODE(x) (((x)>>7)&0x07) +# define DBGP_ERR_BAD 1 +# define DBGP_ERR_SIGNAL 2 +#define DBGP_ERROR (1<<6) +#define DBGP_GO (1<<5) +#define DBGP_OUT (1<<4) +#define DBGP_LEN(x) (((x)>>0)&0x0f) + u32 pids; +#define DBGP_PID_GET(x) (((x)>>16)&0xff) +#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok)) + u32 data03; + u32 data47; + u32 address; +#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) +} __attribute__ ((packed)); + +#endif /* __LINUX_USB_EHCI_DEF_H */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 1cbd0a7db4e..2f1113467f7 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -96,7 +96,7 @@ void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); extern int unbind_con_driver(const struct consw *csw, int first, int last, int deflt); -int vty_init(void); +int vty_init(const struct file_operations *console_fops); /* * vc_screen.c shares this temporary buffer with the console write code so that diff --git a/include/net/cipso_ipv4.h b/include/net/cipso_ipv4.h index a6bb94530cf..9909774eb99 100644 --- a/include/net/cipso_ipv4.h +++ b/include/net/cipso_ipv4.h @@ -40,11 +40,12 @@ #include <linux/net.h> #include <linux/skbuff.h> #include <net/netlabel.h> +#include <asm/atomic.h> /* known doi values */ #define CIPSO_V4_DOI_UNKNOWN 0x00000000 -/* tag types */ +/* standard tag types */ #define CIPSO_V4_TAG_INVALID 0 #define CIPSO_V4_TAG_RBITMAP 1 #define CIPSO_V4_TAG_ENUM 2 @@ -52,10 +53,14 @@ #define CIPSO_V4_TAG_PBITMAP 6 #define CIPSO_V4_TAG_FREEFORM 7 +/* non-standard tag types (tags > 127) */ +#define CIPSO_V4_TAG_LOCAL 128 + /* doi mapping types */ #define CIPSO_V4_MAP_UNKNOWN 0 -#define CIPSO_V4_MAP_STD 1 +#define CIPSO_V4_MAP_TRANS 1 #define CIPSO_V4_MAP_PASS 2 +#define CIPSO_V4_MAP_LOCAL 3 /* limits */ #define CIPSO_V4_MAX_REM_LVLS 255 @@ -79,10 +84,9 @@ struct cipso_v4_doi { } map; u8 tags[CIPSO_V4_TAG_MAXCNT]; - u32 valid; + atomic_t refcount; struct list_head list; struct rcu_head rcu; - struct list_head dom_list; }; /* Standard CIPSO mapping table */ @@ -128,25 +132,26 @@ extern int cipso_v4_rbm_strictvalid; #ifdef CONFIG_NETLABEL int cipso_v4_doi_add(struct cipso_v4_doi *doi_def); -int cipso_v4_doi_remove(u32 doi, - struct netlbl_audit *audit_info, - void (*callback) (struct rcu_head * head)); +void cipso_v4_doi_free(struct cipso_v4_doi *doi_def); +int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info); struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi); +void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def); int cipso_v4_doi_walk(u32 *skip_cnt, int (*callback) (struct cipso_v4_doi *doi_def, void *arg), void *cb_arg); -int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain); -int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, - const char *domain); #else static inline int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) { return -ENOSYS; } +static inline void cipso_v4_doi_free(struct cipso_v4_doi *doi_def) +{ + return; +} + static inline int cipso_v4_doi_remove(u32 doi, - struct netlbl_audit *audit_info, - void (*callback) (struct rcu_head * head)) + struct netlbl_audit *audit_info) { return 0; } @@ -206,10 +211,15 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway); int cipso_v4_sock_setattr(struct sock *sk, const struct cipso_v4_doi *doi_def, const struct netlbl_lsm_secattr *secattr); +void cipso_v4_sock_delattr(struct sock *sk); int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr); +int cipso_v4_skbuff_setattr(struct sk_buff *skb, + const struct cipso_v4_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); +int cipso_v4_skbuff_delattr(struct sk_buff *skb); int cipso_v4_skbuff_getattr(const struct sk_buff *skb, struct netlbl_lsm_secattr *secattr); -int cipso_v4_validate(unsigned char **option); +int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option); #else static inline void cipso_v4_error(struct sk_buff *skb, int error, @@ -225,19 +235,36 @@ static inline int cipso_v4_sock_setattr(struct sock *sk, return -ENOSYS; } +static inline void cipso_v4_sock_delattr(struct sock *sk) +{ +} + static inline int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) { return -ENOSYS; } +static inline int cipso_v4_skbuff_setattr(struct sk_buff *skb, + const struct cipso_v4_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + return -ENOSYS; +} + +static inline int cipso_v4_skbuff_delattr(struct sk_buff *skb) +{ + return -ENOSYS; +} + static inline int cipso_v4_skbuff_getattr(const struct sk_buff *skb, struct netlbl_lsm_secattr *secattr) { return -ENOSYS; } -static inline int cipso_v4_validate(unsigned char **option) +static inline int cipso_v4_validate(const struct sk_buff *skb, + unsigned char **option) { return -ENOSYS; } diff --git a/include/net/netlabel.h b/include/net/netlabel.h index e4d2d6baa98..17c442a4514 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -9,7 +9,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,8 +72,10 @@ struct cipso_v4_doi; /* NetLabel NETLINK protocol version * 1: initial version * 2: added static labels for unlabeled connections + * 3: network selectors added to the NetLabel/LSM domain mapping and the + * CIPSO_V4_MAP_LOCAL CIPSO mapping was added */ -#define NETLBL_PROTO_VERSION 2 +#define NETLBL_PROTO_VERSION 3 /* NetLabel NETLINK types/families */ #define NETLBL_NLTYPE_NONE 0 @@ -87,6 +89,8 @@ struct cipso_v4_doi; #define NETLBL_NLTYPE_CIPSOV6_NAME "NLBL_CIPSOv6" #define NETLBL_NLTYPE_UNLABELED 5 #define NETLBL_NLTYPE_UNLABELED_NAME "NLBL_UNLBL" +#define NETLBL_NLTYPE_ADDRSELECT 6 +#define NETLBL_NLTYPE_ADDRSELECT_NAME "NLBL_ADRSEL" /* * NetLabel - Kernel API for accessing the network packet label mappings. @@ -200,7 +204,7 @@ struct netlbl_lsm_secattr { u32 type; char *domain; struct netlbl_lsm_cache *cache; - union { + struct { struct { struct netlbl_lsm_secattr_catmap *cat; u32 lvl; @@ -352,12 +356,9 @@ static inline void netlbl_secattr_free(struct netlbl_lsm_secattr *secattr) int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info); int netlbl_cfg_unlbl_add_map(const char *domain, struct netlbl_audit *audit_info); -int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, - struct netlbl_audit *audit_info); int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, const char *domain, struct netlbl_audit *audit_info); -int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info); /* * LSM security attribute operations @@ -380,12 +381,19 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, int netlbl_enabled(void); int netlbl_sock_setattr(struct sock *sk, const struct netlbl_lsm_secattr *secattr); +void netlbl_sock_delattr(struct sock *sk); int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr); +int netlbl_conn_setattr(struct sock *sk, + struct sockaddr *addr, + const struct netlbl_lsm_secattr *secattr); +int netlbl_skbuff_setattr(struct sk_buff *skb, + u16 family, + const struct netlbl_lsm_secattr *secattr); int netlbl_skbuff_getattr(const struct sk_buff *skb, u16 family, struct netlbl_lsm_secattr *secattr); -void netlbl_skbuff_err(struct sk_buff *skb, int error); +void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway); /* * LSM label mapping cache operations @@ -404,22 +412,12 @@ static inline int netlbl_cfg_unlbl_add_map(const char *domain, { return -ENOSYS; } -static inline int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, - struct netlbl_audit *audit_info) -{ - return -ENOSYS; -} static inline int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, const char *domain, struct netlbl_audit *audit_info) { return -ENOSYS; } -static inline int netlbl_cfg_cipsov4_del(u32 doi, - struct netlbl_audit *audit_info) -{ - return -ENOSYS; -} static inline int netlbl_secattr_catmap_walk( struct netlbl_lsm_secattr_catmap *catmap, u32 offset) @@ -456,18 +454,35 @@ static inline int netlbl_sock_setattr(struct sock *sk, { return -ENOSYS; } +static inline void netlbl_sock_delattr(struct sock *sk) +{ +} static inline int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) { return -ENOSYS; } +static inline int netlbl_conn_setattr(struct sock *sk, + struct sockaddr *addr, + const struct netlbl_lsm_secattr *secattr) +{ + return -ENOSYS; +} +static inline int netlbl_skbuff_setattr(struct sk_buff *skb, + u16 family, + const struct netlbl_lsm_secattr *secattr) +{ + return -ENOSYS; +} static inline int netlbl_skbuff_getattr(const struct sk_buff *skb, u16 family, struct netlbl_lsm_secattr *secattr) { return -ENOSYS; } -static inline void netlbl_skbuff_err(struct sk_buff *skb, int error) +static inline void netlbl_skbuff_err(struct sk_buff *skb, + int error, + int gateway) { return; } diff --git a/include/sound/core.h b/include/sound/core.h index e5eec5f7350..35424a971b7 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -43,9 +43,6 @@ #ifdef CONFIG_PCI struct pci_dev; #endif -#ifdef CONFIG_SBUS -struct sbus_dev; -#endif /* device allocation stuff */ diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h index d787a6b4a10..7ccce94a525 100644 --- a/include/sound/memalloc.h +++ b/include/sound/memalloc.h @@ -37,7 +37,6 @@ struct snd_dma_device { #ifndef snd_dma_pci_data #define snd_dma_pci_data(pci) (&(pci)->dev) #define snd_dma_isa_data() NULL -#define snd_dma_sbus_data(sbus) ((struct device *)(sbus)) #define snd_dma_continuous_data(x) ((struct device *)(unsigned long)(x)) #endif @@ -49,7 +48,6 @@ struct snd_dma_device { #define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ #define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */ #define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */ -#define SNDRV_DMA_TYPE_SBUS 4 /* SBUS continuous */ /* * info for buffer allocation diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index c1b26fcc0b5..ca699a3017f 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -240,6 +240,7 @@ int snd_soc_dapm_sys_add(struct device *dev); /* dapm audio pin control and status */ int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin); int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin); +int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin); int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin); int snd_soc_dapm_sync(struct snd_soc_codec *codec); diff --git a/include/xen/balloon.h b/include/xen/balloon.h deleted file mode 100644 index fe43b0f3c86..00000000000 --- a/include/xen/balloon.h +++ /dev/null @@ -1,61 +0,0 @@ -/****************************************************************************** - * balloon.h - * - * Xen balloon driver - enables returning/claiming memory to/from Xen. - * - * Copyright (c) 2003, B Dragovic - * Copyright (c) 2003-2004, M Williamson, K Fraser - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation; or, when distributed - * separately from the Linux kernel or incorporated into other - * software packages, subject to the following license: - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this source file (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, copy, modify, - * merge, publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef __XEN_BALLOON_H__ -#define __XEN_BALLOON_H__ - -#include <linux/spinlock.h> - -#if 0 -/* - * Inform the balloon driver that it should allow some slop for device-driver - * memory activities. - */ -void balloon_update_driver_allowance(long delta); - -/* Allocate/free a set of empty pages in low memory (i.e., no RAM mapped). */ -struct page **alloc_empty_pages_and_pagevec(int nr_pages); -void free_empty_pages_and_pagevec(struct page **pagevec, int nr_pages); - -void balloon_release_driver_page(struct page *page); - -/* - * Prevent the balloon driver from changing the memory reservation during - * a driver critical region. - */ -extern spinlock_t balloon_lock; -#define balloon_lock(__flags) spin_lock_irqsave(&balloon_lock, __flags) -#define balloon_unlock(__flags) spin_unlock_irqrestore(&balloon_lock, __flags) -#endif - -#endif /* __XEN_BALLOON_H__ */ diff --git a/include/xen/events.h b/include/xen/events.h index 4680ff3fbc9..0d5f1adc036 100644 --- a/include/xen/events.h +++ b/include/xen/events.h @@ -46,6 +46,8 @@ extern void xen_irq_resume(void); /* Clear an irq's pending state, in preparation for polling on it */ void xen_clear_irq_pending(int irq); +void xen_set_irq_pending(int irq); +bool xen_test_irq_pending(int irq); /* Poll waiting for an irq to become pending. In the usual case, the irq will be disabled so it won't deliver an interrupt. */ diff --git a/init/Kconfig b/init/Kconfig index c11da38837e..8a8e2d00c40 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -779,16 +779,6 @@ config MARKERS source "arch/Kconfig" -config PROC_PAGE_MONITOR - default y - depends on PROC_FS && MMU - bool "Enable /proc page monitoring" if EMBEDDED - help - Various /proc files exist to monitor process memory utilization: - /proc/pid/smaps, /proc/pid/clear_refs, /proc/pid/pagemap, - /proc/kpagecount, and /proc/kpageflags. Disabling these - interfaces will reduce the size of the kernel by approximately 4kb. - endmenu # General setup config HAVE_GENERIC_DMA_COHERENT diff --git a/kernel/acct.c b/kernel/acct.c index dd68b905941..f6006a60df5 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -548,7 +548,7 @@ static void do_acct_process(struct bsd_acct_struct *acct, #endif spin_lock_irq(¤t->sighand->siglock); - tty = current->signal->tty; + tty = current->signal->tty; /* Safe as we hold the siglock */ ac.ac_tty = tty ? old_encode_dev(tty_devnum(tty)) : 0; ac.ac_utime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_utime))); ac.ac_stime = encode_comp_t(jiffies_to_AHZ(cputime_to_jiffies(pacct->ac_stime))); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 59cedfb040e..cf5bc2f5f9c 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -246,8 +246,8 @@ static int audit_match_perm(struct audit_context *ctx, int mask) unsigned n; if (unlikely(!ctx)) return 0; - n = ctx->major; + switch (audit_classify_syscall(ctx->arch, n)) { case 0: /* native */ if ((mask & AUDIT_PERM_WRITE) && @@ -1204,13 +1204,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code); - mutex_lock(&tty_mutex); - read_lock(&tasklist_lock); + spin_lock_irq(&tsk->sighand->siglock); if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) tty = tsk->signal->tty->name; else tty = "(none)"; - read_unlock(&tasklist_lock); + spin_unlock_irq(&tsk->sighand->siglock); + audit_log_format(ab, " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" " ppid=%d pid=%d auid=%u uid=%u gid=%u" @@ -1230,7 +1230,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts context->egid, context->sgid, context->fsgid, tty, tsk->sessionid); - mutex_unlock(&tty_mutex); audit_log_task_info(ab, tsk); if (context->filterkey) { diff --git a/kernel/fork.c b/kernel/fork.c index 7ce2ebe8479..30de644a40c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -802,6 +802,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) sig->leader = 0; /* session leadership doesn't inherit */ sig->tty_old_pgrp = NULL; + sig->tty = NULL; sig->utime = sig->stime = sig->cutime = sig->cstime = cputime_zero; sig->gtime = cputime_zero; @@ -838,6 +839,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) void __cleanup_signal(struct signal_struct *sig) { exit_thread_group_keys(sig); + tty_kref_put(sig->tty); kmem_cache_free(signal_cachep, sig); } @@ -1227,7 +1229,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, p->nsproxy->pid_ns->child_reaper = p; p->signal->leader_pid = pid; - p->signal->tty = current->signal->tty; + tty_kref_put(p->signal->tty); + p->signal->tty = tty_kref_get(current->signal->tty); set_task_pgrp(p, task_pgrp_nr(current)); set_task_session(p, task_session_nr(current)); attach_pid(p, PIDTYPE_PGID, task_pgrp(current)); diff --git a/kernel/printk.c b/kernel/printk.c index b51b1567bb5..a430fd04008 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1291,22 +1291,6 @@ static int __init disable_boot_consoles(void) } late_initcall(disable_boot_consoles); -/** - * tty_write_message - write a message to a certain tty, not just the console. - * @tty: the destination tty_struct - * @msg: the message to write - * - * This is used for messages that need to be redirected to a specific tty. - * We don't put it into the syslog queue right now maybe in the future if - * really needed. - */ -void tty_write_message(struct tty_struct *tty, char *msg) -{ - if (tty && tty->ops->write) - tty->ops->write(tty, msg, strlen(msg)); - return; -} - #if defined CONFIG_PRINTK /* diff --git a/kernel/sched_debug.c b/kernel/sched_debug.c index bbe6b31c3c5..ad958c1ec70 100644 --- a/kernel/sched_debug.c +++ b/kernel/sched_debug.c @@ -333,12 +333,10 @@ void proc_sched_show_task(struct task_struct *p, struct seq_file *m) unsigned long flags; int num_threads = 1; - rcu_read_lock(); if (lock_task_sighand(p, &flags)) { num_threads = atomic_read(&p->signal->count); unlock_task_sighand(p, &flags); } - rcu_read_unlock(); SEQ_printf(m, "%s (%d, #threads: %d)\n", p->comm, p->pid, num_threads); SEQ_printf(m, diff --git a/kernel/sys.c b/kernel/sys.c index 038a7bc0901..234d9454294 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1060,9 +1060,7 @@ asmlinkage long sys_setsid(void) group_leader->signal->leader = 1; __set_special_pids(sid); - spin_lock(&group_leader->sighand->siglock); - group_leader->signal->tty = NULL; - spin_unlock(&group_leader->sighand->siglock); + proc_clear_tty(group_leader); err = session; out: diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 50ec0886fa3..c468c3c6dfc 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -80,7 +80,6 @@ extern int pid_max_min, pid_max_max; extern int sysctl_drop_caches; extern int percpu_pagelist_fraction; extern int compat_log; -extern int maps_protect; extern int latencytop_enabled; extern int sysctl_nr_open_min, sysctl_nr_open_max; #ifdef CONFIG_RCU_TORTURE_TEST @@ -118,10 +117,8 @@ extern char modprobe_path[]; extern int sg_big_buff; #endif -#ifdef __sparc__ -extern char reboot_command []; -extern int stop_a_enabled; -extern int scons_pwroff; +#ifdef CONFIG_SPARC +#include <asm/system.h> #endif #ifdef __hppa__ @@ -415,7 +412,7 @@ static struct ctl_table kern_table[] = { .mode = 0644, .proc_handler = &proc_dointvec, }, -#ifdef __sparc__ +#ifdef CONFIG_SPARC { .ctl_name = KERN_SPARC_REBOOT, .procname = "reboot-cmd", @@ -810,16 +807,6 @@ static struct ctl_table kern_table[] = { .proc_handler = &proc_dointvec, }, #endif -#ifdef CONFIG_PROC_FS - { - .ctl_name = CTL_UNNUMBERED, - .procname = "maps_protect", - .data = &maps_protect, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = &proc_dointvec, - }, -#endif { .ctl_name = CTL_UNNUMBERED, .procname = "poweroff_cmd", diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ce697e0b319..aa81d284844 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -495,6 +495,15 @@ config DEBUG_VM If unsure, say N. +config DEBUG_VIRTUAL + bool "Debug VM translations" + depends on DEBUG_KERNEL && X86 + help + Enable some costly sanity checks in virtual to page code. This can + catch mistakes with virt_to_page() and friends. + + If unsure, say N. + config DEBUG_WRITECOUNT bool "Debug filesystem writers count" depends on DEBUG_KERNEL diff --git a/lib/cmdline.c b/lib/cmdline.c index 5ba8a942a47..f5f3ad8b62f 100644 --- a/lib/cmdline.c +++ b/lib/cmdline.c @@ -126,7 +126,7 @@ char *get_options(const char *str, int nints, int *ints) * megabyte, or one gigabyte, respectively. */ -unsigned long long memparse(char *ptr, char **retptr) +unsigned long long memparse(const char *ptr, char **retptr) { char *endptr; /* local pointer to end of parsed string */ diff --git a/lib/parser.c b/lib/parser.c index 4f0cbc03e0e..b00d02059a5 100644 --- a/lib/parser.c +++ b/lib/parser.c @@ -100,7 +100,7 @@ static int match_one(char *s, const char *p, substring_t args[]) * format identifiers which will be taken into account when matching the * tokens, and whose locations will be returned in the @args array. */ -int match_token(char *s, match_table_t table, substring_t args[]) +int match_token(char *s, const match_table_t table, substring_t args[]) { const struct match_token *p; diff --git a/mm/shmem.c b/mm/shmem.c index 04fb4f1ab88..bf66d0191ba 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -50,14 +50,12 @@ #include <linux/migrate.h> #include <linux/highmem.h> #include <linux/seq_file.h> +#include <linux/magic.h> #include <asm/uaccess.h> #include <asm/div64.h> #include <asm/pgtable.h> -/* This magic number is used in glibc for posix shared memory */ -#define TMPFS_MAGIC 0x01021994 - #define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long)) #define ENTRIES_PER_PAGEPAGE (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE) #define BLOCKS_PER_PAGE (PAGE_CACHE_SIZE/512) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 85b9a0d2c87..bba06c41fc5 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -180,6 +180,13 @@ struct page *vmalloc_to_page(const void *vmalloc_addr) pmd_t *pmd; pte_t *ptep, pte; + /* + * XXX we might need to change this if we add VIRTUAL_BUG_ON for + * architectures that do not vmalloc module space + */ + VIRTUAL_BUG_ON(!is_vmalloc_addr(vmalloc_addr) && + !is_module_address(addr)); + if (!pgd_none(*pgd)) { pud = pud_offset(pgd, addr); if (!pud_none(*pud)) { diff --git a/net/9p/client.c b/net/9p/client.c index 10e320307ec..e053e06028a 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -52,7 +52,7 @@ enum { Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_msize, "msize=%u"}, {Opt_legacy, "noextend"}, {Opt_trans, "trans=%s"}, diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index d652baf5ff9..6dabbdb6665 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -86,7 +86,7 @@ enum { Opt_port, Opt_rfdno, Opt_wfdno, Opt_err, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_port, "port=%u"}, {Opt_rfdno, "rfdno=%u"}, {Opt_wfdno, "wfdno=%u"}, diff --git a/net/core/skb_dma_map.c b/net/core/skb_dma_map.c index 1f49afcd8e8..86234923a3b 100644 --- a/net/core/skb_dma_map.c +++ b/net/core/skb_dma_map.c @@ -35,7 +35,7 @@ int skb_dma_map(struct device *dev, struct sk_buff *skb, return 0; unwind: - while (i-- >= 0) { + while (--i >= 0) { skb_frag_t *fp = &sp->frags[i]; dma_unmap_page(dev, sp->dma_maps[i + 1], diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 2c0e4572cc9..490e035c6d9 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -13,7 +13,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -47,17 +47,7 @@ #include <asm/bug.h> #include <asm/unaligned.h> -struct cipso_v4_domhsh_entry { - char *domain; - u32 valid; - struct list_head list; - struct rcu_head rcu; -}; - /* List of available DOI definitions */ -/* XXX - Updates should be minimal so having a single lock for the - * cipso_v4_doi_list and the cipso_v4_doi_list->dom_list should be - * okay. */ /* XXX - This currently assumes a minimal number of different DOIs in use, * if in practice there are a lot of different DOIs this list should * probably be turned into a hash table or something similar so we @@ -119,6 +109,19 @@ int cipso_v4_rbm_strictvalid = 1; * be omitted. */ #define CIPSO_V4_TAG_RNG_CAT_MAX 8 +/* Base length of the local tag (non-standard tag). + * Tag definition (may change between kernel versions) + * + * 0 8 16 24 32 + * +----------+----------+----------+----------+ + * | 10000000 | 00000110 | 32-bit secid value | + * +----------+----------+----------+----------+ + * | in (host byte order)| + * +----------+----------+ + * + */ +#define CIPSO_V4_TAG_LOC_BLEN 6 + /* * Helper Functions */ @@ -194,25 +197,6 @@ static void cipso_v4_bitmap_setbit(unsigned char *bitmap, } /** - * cipso_v4_doi_domhsh_free - Frees a domain list entry - * @entry: the entry's RCU field - * - * Description: - * This function is designed to be used as a callback to the call_rcu() - * function so that the memory allocated to a domain list entry can be released - * safely. - * - */ -static void cipso_v4_doi_domhsh_free(struct rcu_head *entry) -{ - struct cipso_v4_domhsh_entry *ptr; - - ptr = container_of(entry, struct cipso_v4_domhsh_entry, rcu); - kfree(ptr->domain); - kfree(ptr); -} - -/** * cipso_v4_cache_entry_free - Frees a cache entry * @entry: the entry to free * @@ -457,7 +441,7 @@ static struct cipso_v4_doi *cipso_v4_doi_search(u32 doi) struct cipso_v4_doi *iter; list_for_each_entry_rcu(iter, &cipso_v4_doi_list, list) - if (iter->doi == doi && iter->valid) + if (iter->doi == doi && atomic_read(&iter->refcount)) return iter; return NULL; } @@ -496,14 +480,17 @@ int cipso_v4_doi_add(struct cipso_v4_doi *doi_def) if (doi_def->type != CIPSO_V4_MAP_PASS) return -EINVAL; break; + case CIPSO_V4_TAG_LOCAL: + if (doi_def->type != CIPSO_V4_MAP_LOCAL) + return -EINVAL; + break; default: return -EINVAL; } } - doi_def->valid = 1; + atomic_set(&doi_def->refcount, 1); INIT_RCU_HEAD(&doi_def->rcu); - INIT_LIST_HEAD(&doi_def->dom_list); spin_lock(&cipso_v4_doi_list_lock); if (cipso_v4_doi_search(doi_def->doi) != NULL) @@ -519,59 +506,129 @@ doi_add_failure: } /** + * cipso_v4_doi_free - Frees a DOI definition + * @entry: the entry's RCU field + * + * Description: + * This function frees all of the memory associated with a DOI definition. + * + */ +void cipso_v4_doi_free(struct cipso_v4_doi *doi_def) +{ + if (doi_def == NULL) + return; + + switch (doi_def->type) { + case CIPSO_V4_MAP_TRANS: + kfree(doi_def->map.std->lvl.cipso); + kfree(doi_def->map.std->lvl.local); + kfree(doi_def->map.std->cat.cipso); + kfree(doi_def->map.std->cat.local); + break; + } + kfree(doi_def); +} + +/** + * cipso_v4_doi_free_rcu - Frees a DOI definition via the RCU pointer + * @entry: the entry's RCU field + * + * Description: + * This function is designed to be used as a callback to the call_rcu() + * function so that the memory allocated to the DOI definition can be released + * safely. + * + */ +static void cipso_v4_doi_free_rcu(struct rcu_head *entry) +{ + struct cipso_v4_doi *doi_def; + + doi_def = container_of(entry, struct cipso_v4_doi, rcu); + cipso_v4_doi_free(doi_def); +} + +/** * cipso_v4_doi_remove - Remove an existing DOI from the CIPSO protocol engine * @doi: the DOI value * @audit_secid: the LSM secid to use in the audit message - * @callback: the DOI cleanup/free callback * * Description: - * Removes a DOI definition from the CIPSO engine, @callback is called to - * free any memory. The NetLabel routines will be called to release their own - * LSM domain mappings as well as our own domain list. Returns zero on - * success and negative values on failure. + * Removes a DOI definition from the CIPSO engine. The NetLabel routines will + * be called to release their own LSM domain mappings as well as our own + * domain list. Returns zero on success and negative values on failure. * */ -int cipso_v4_doi_remove(u32 doi, - struct netlbl_audit *audit_info, - void (*callback) (struct rcu_head * head)) +int cipso_v4_doi_remove(u32 doi, struct netlbl_audit *audit_info) { struct cipso_v4_doi *doi_def; - struct cipso_v4_domhsh_entry *dom_iter; spin_lock(&cipso_v4_doi_list_lock); doi_def = cipso_v4_doi_search(doi); - if (doi_def != NULL) { - doi_def->valid = 0; - list_del_rcu(&doi_def->list); + if (doi_def == NULL) { spin_unlock(&cipso_v4_doi_list_lock); - rcu_read_lock(); - list_for_each_entry_rcu(dom_iter, &doi_def->dom_list, list) - if (dom_iter->valid) - netlbl_cfg_map_del(dom_iter->domain, - audit_info); - rcu_read_unlock(); - cipso_v4_cache_invalidate(); - call_rcu(&doi_def->rcu, callback); - return 0; + return -ENOENT; + } + if (!atomic_dec_and_test(&doi_def->refcount)) { + spin_unlock(&cipso_v4_doi_list_lock); + return -EBUSY; } + list_del_rcu(&doi_def->list); spin_unlock(&cipso_v4_doi_list_lock); - return -ENOENT; + cipso_v4_cache_invalidate(); + call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); + + return 0; } /** - * cipso_v4_doi_getdef - Returns a pointer to a valid DOI definition + * cipso_v4_doi_getdef - Returns a reference to a valid DOI definition * @doi: the DOI value * * Description: * Searches for a valid DOI definition and if one is found it is returned to * the caller. Otherwise NULL is returned. The caller must ensure that - * rcu_read_lock() is held while accessing the returned definition. + * rcu_read_lock() is held while accessing the returned definition and the DOI + * definition reference count is decremented when the caller is done. * */ struct cipso_v4_doi *cipso_v4_doi_getdef(u32 doi) { - return cipso_v4_doi_search(doi); + struct cipso_v4_doi *doi_def; + + rcu_read_lock(); + doi_def = cipso_v4_doi_search(doi); + if (doi_def == NULL) + goto doi_getdef_return; + if (!atomic_inc_not_zero(&doi_def->refcount)) + doi_def = NULL; + +doi_getdef_return: + rcu_read_unlock(); + return doi_def; +} + +/** + * cipso_v4_doi_putdef - Releases a reference for the given DOI definition + * @doi_def: the DOI definition + * + * Description: + * Releases a DOI definition reference obtained from cipso_v4_doi_getdef(). + * + */ +void cipso_v4_doi_putdef(struct cipso_v4_doi *doi_def) +{ + if (doi_def == NULL) + return; + + if (!atomic_dec_and_test(&doi_def->refcount)) + return; + spin_lock(&cipso_v4_doi_list_lock); + list_del_rcu(&doi_def->list); + spin_unlock(&cipso_v4_doi_list_lock); + + cipso_v4_cache_invalidate(); + call_rcu(&doi_def->rcu, cipso_v4_doi_free_rcu); } /** @@ -597,7 +654,7 @@ int cipso_v4_doi_walk(u32 *skip_cnt, rcu_read_lock(); list_for_each_entry_rcu(iter_doi, &cipso_v4_doi_list, list) - if (iter_doi->valid) { + if (atomic_read(&iter_doi->refcount) > 0) { if (doi_cnt++ < *skip_cnt) continue; ret_val = callback(iter_doi, cb_arg); @@ -613,85 +670,6 @@ doi_walk_return: return ret_val; } -/** - * cipso_v4_doi_domhsh_add - Adds a domain entry to a DOI definition - * @doi_def: the DOI definition - * @domain: the domain to add - * - * Description: - * Adds the @domain to the DOI specified by @doi_def, this function - * should only be called by external functions (i.e. NetLabel). This function - * does allocate memory. Returns zero on success, negative values on failure. - * - */ -int cipso_v4_doi_domhsh_add(struct cipso_v4_doi *doi_def, const char *domain) -{ - struct cipso_v4_domhsh_entry *iter; - struct cipso_v4_domhsh_entry *new_dom; - - new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL); - if (new_dom == NULL) - return -ENOMEM; - if (domain) { - new_dom->domain = kstrdup(domain, GFP_KERNEL); - if (new_dom->domain == NULL) { - kfree(new_dom); - return -ENOMEM; - } - } - new_dom->valid = 1; - INIT_RCU_HEAD(&new_dom->rcu); - - spin_lock(&cipso_v4_doi_list_lock); - list_for_each_entry(iter, &doi_def->dom_list, list) - if (iter->valid && - ((domain != NULL && iter->domain != NULL && - strcmp(iter->domain, domain) == 0) || - (domain == NULL && iter->domain == NULL))) { - spin_unlock(&cipso_v4_doi_list_lock); - kfree(new_dom->domain); - kfree(new_dom); - return -EEXIST; - } - list_add_tail_rcu(&new_dom->list, &doi_def->dom_list); - spin_unlock(&cipso_v4_doi_list_lock); - - return 0; -} - -/** - * cipso_v4_doi_domhsh_remove - Removes a domain entry from a DOI definition - * @doi_def: the DOI definition - * @domain: the domain to remove - * - * Description: - * Removes the @domain from the DOI specified by @doi_def, this function - * should only be called by external functions (i.e. NetLabel). Returns zero - * on success and negative values on error. - * - */ -int cipso_v4_doi_domhsh_remove(struct cipso_v4_doi *doi_def, - const char *domain) -{ - struct cipso_v4_domhsh_entry *iter; - - spin_lock(&cipso_v4_doi_list_lock); - list_for_each_entry(iter, &doi_def->dom_list, list) - if (iter->valid && - ((domain != NULL && iter->domain != NULL && - strcmp(iter->domain, domain) == 0) || - (domain == NULL && iter->domain == NULL))) { - iter->valid = 0; - list_del_rcu(&iter->list); - spin_unlock(&cipso_v4_doi_list_lock); - call_rcu(&iter->rcu, cipso_v4_doi_domhsh_free); - return 0; - } - spin_unlock(&cipso_v4_doi_list_lock); - - return -ENOENT; -} - /* * Label Mapping Functions */ @@ -712,7 +690,7 @@ static int cipso_v4_map_lvl_valid(const struct cipso_v4_doi *doi_def, u8 level) switch (doi_def->type) { case CIPSO_V4_MAP_PASS: return 0; - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: if (doi_def->map.std->lvl.cipso[level] < CIPSO_V4_INV_LVL) return 0; break; @@ -741,7 +719,7 @@ static int cipso_v4_map_lvl_hton(const struct cipso_v4_doi *doi_def, case CIPSO_V4_MAP_PASS: *net_lvl = host_lvl; return 0; - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: if (host_lvl < doi_def->map.std->lvl.local_size && doi_def->map.std->lvl.local[host_lvl] < CIPSO_V4_INV_LVL) { *net_lvl = doi_def->map.std->lvl.local[host_lvl]; @@ -775,7 +753,7 @@ static int cipso_v4_map_lvl_ntoh(const struct cipso_v4_doi *doi_def, case CIPSO_V4_MAP_PASS: *host_lvl = net_lvl; return 0; - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: map_tbl = doi_def->map.std; if (net_lvl < map_tbl->lvl.cipso_size && map_tbl->lvl.cipso[net_lvl] < CIPSO_V4_INV_LVL) { @@ -812,7 +790,7 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def, switch (doi_def->type) { case CIPSO_V4_MAP_PASS: return 0; - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: cipso_cat_size = doi_def->map.std->cat.cipso_size; cipso_array = doi_def->map.std->cat.cipso; for (;;) { @@ -860,7 +838,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, u32 host_cat_size = 0; u32 *host_cat_array = NULL; - if (doi_def->type == CIPSO_V4_MAP_STD) { + if (doi_def->type == CIPSO_V4_MAP_TRANS) { host_cat_size = doi_def->map.std->cat.local_size; host_cat_array = doi_def->map.std->cat.local; } @@ -875,7 +853,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, case CIPSO_V4_MAP_PASS: net_spot = host_spot; break; - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: if (host_spot >= host_cat_size) return -EPERM; net_spot = host_cat_array[host_spot]; @@ -921,7 +899,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, u32 net_cat_size = 0; u32 *net_cat_array = NULL; - if (doi_def->type == CIPSO_V4_MAP_STD) { + if (doi_def->type == CIPSO_V4_MAP_TRANS) { net_cat_size = doi_def->map.std->cat.cipso_size; net_cat_array = doi_def->map.std->cat.cipso; } @@ -941,7 +919,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, case CIPSO_V4_MAP_PASS: host_spot = net_spot; break; - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: if (net_spot >= net_cat_size) return -EPERM; host_spot = net_cat_array[net_spot]; @@ -1277,7 +1255,7 @@ static int cipso_v4_gentag_rbm(const struct cipso_v4_doi *doi_def, } else tag_len = 4; - buffer[0] = 0x01; + buffer[0] = CIPSO_V4_TAG_RBITMAP; buffer[1] = tag_len; buffer[3] = level; @@ -1373,7 +1351,7 @@ static int cipso_v4_gentag_enum(const struct cipso_v4_doi *doi_def, } else tag_len = 4; - buffer[0] = 0x02; + buffer[0] = CIPSO_V4_TAG_ENUM; buffer[1] = tag_len; buffer[3] = level; @@ -1469,7 +1447,7 @@ static int cipso_v4_gentag_rng(const struct cipso_v4_doi *doi_def, } else tag_len = 4; - buffer[0] = 0x05; + buffer[0] = CIPSO_V4_TAG_RANGE; buffer[1] = tag_len; buffer[3] = level; @@ -1523,6 +1501,54 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, } /** + * cipso_v4_gentag_loc - Generate a CIPSO local tag (non-standard) + * @doi_def: the DOI definition + * @secattr: the security attributes + * @buffer: the option buffer + * @buffer_len: length of buffer in bytes + * + * Description: + * Generate a CIPSO option using the local tag. Returns the size of the tag + * on success, negative values on failure. + * + */ +static int cipso_v4_gentag_loc(const struct cipso_v4_doi *doi_def, + const struct netlbl_lsm_secattr *secattr, + unsigned char *buffer, + u32 buffer_len) +{ + if (!(secattr->flags & NETLBL_SECATTR_SECID)) + return -EPERM; + + buffer[0] = CIPSO_V4_TAG_LOCAL; + buffer[1] = CIPSO_V4_TAG_LOC_BLEN; + *(u32 *)&buffer[2] = secattr->attr.secid; + + return CIPSO_V4_TAG_LOC_BLEN; +} + +/** + * cipso_v4_parsetag_loc - Parse a CIPSO local tag + * @doi_def: the DOI definition + * @tag: the CIPSO tag + * @secattr: the security attributes + * + * Description: + * Parse a CIPSO local tag and return the security attributes in @secattr. + * Return zero on success, negatives values on failure. + * + */ +static int cipso_v4_parsetag_loc(const struct cipso_v4_doi *doi_def, + const unsigned char *tag, + struct netlbl_lsm_secattr *secattr) +{ + secattr->attr.secid = *(u32 *)&tag[2]; + secattr->flags |= NETLBL_SECATTR_SECID; + + return 0; +} + +/** * cipso_v4_validate - Validate a CIPSO option * @option: the start of the option, on error it is set to point to the error * @@ -1541,7 +1567,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, * that is unrecognized." * */ -int cipso_v4_validate(unsigned char **option) +int cipso_v4_validate(const struct sk_buff *skb, unsigned char **option) { unsigned char *opt = *option; unsigned char *tag; @@ -1566,7 +1592,7 @@ int cipso_v4_validate(unsigned char **option) goto validate_return_locked; } - opt_iter = 6; + opt_iter = CIPSO_V4_HDR_LEN; tag = opt + opt_iter; while (opt_iter < opt_len) { for (tag_iter = 0; doi_def->tags[tag_iter] != tag[0];) @@ -1584,7 +1610,7 @@ int cipso_v4_validate(unsigned char **option) switch (tag[0]) { case CIPSO_V4_TAG_RBITMAP: - if (tag_len < 4) { + if (tag_len < CIPSO_V4_TAG_RBM_BLEN) { err_offset = opt_iter + 1; goto validate_return_locked; } @@ -1602,7 +1628,7 @@ int cipso_v4_validate(unsigned char **option) err_offset = opt_iter + 3; goto validate_return_locked; } - if (tag_len > 4 && + if (tag_len > CIPSO_V4_TAG_RBM_BLEN && cipso_v4_map_cat_rbm_valid(doi_def, &tag[4], tag_len - 4) < 0) { @@ -1612,7 +1638,7 @@ int cipso_v4_validate(unsigned char **option) } break; case CIPSO_V4_TAG_ENUM: - if (tag_len < 4) { + if (tag_len < CIPSO_V4_TAG_ENUM_BLEN) { err_offset = opt_iter + 1; goto validate_return_locked; } @@ -1622,7 +1648,7 @@ int cipso_v4_validate(unsigned char **option) err_offset = opt_iter + 3; goto validate_return_locked; } - if (tag_len > 4 && + if (tag_len > CIPSO_V4_TAG_ENUM_BLEN && cipso_v4_map_cat_enum_valid(doi_def, &tag[4], tag_len - 4) < 0) { @@ -1631,7 +1657,7 @@ int cipso_v4_validate(unsigned char **option) } break; case CIPSO_V4_TAG_RANGE: - if (tag_len < 4) { + if (tag_len < CIPSO_V4_TAG_RNG_BLEN) { err_offset = opt_iter + 1; goto validate_return_locked; } @@ -1641,7 +1667,7 @@ int cipso_v4_validate(unsigned char **option) err_offset = opt_iter + 3; goto validate_return_locked; } - if (tag_len > 4 && + if (tag_len > CIPSO_V4_TAG_RNG_BLEN && cipso_v4_map_cat_rng_valid(doi_def, &tag[4], tag_len - 4) < 0) { @@ -1649,6 +1675,19 @@ int cipso_v4_validate(unsigned char **option) goto validate_return_locked; } break; + case CIPSO_V4_TAG_LOCAL: + /* This is a non-standard tag that we only allow for + * local connections, so if the incoming interface is + * not the loopback device drop the packet. */ + if (!(skb->dev->flags & IFF_LOOPBACK)) { + err_offset = opt_iter; + goto validate_return_locked; + } + if (tag_len != CIPSO_V4_TAG_LOC_BLEN) { + err_offset = opt_iter + 1; + goto validate_return_locked; + } + break; default: err_offset = opt_iter; goto validate_return_locked; @@ -1704,48 +1743,27 @@ void cipso_v4_error(struct sk_buff *skb, int error, u32 gateway) } /** - * cipso_v4_sock_setattr - Add a CIPSO option to a socket - * @sk: the socket + * cipso_v4_genopt - Generate a CIPSO option + * @buf: the option buffer + * @buf_len: the size of opt_buf * @doi_def: the CIPSO DOI to use - * @secattr: the specific security attributes of the socket + * @secattr: the security attributes * * Description: - * Set the CIPSO option on the given socket using the DOI definition and - * security attributes passed to the function. This function requires - * exclusive access to @sk, which means it either needs to be in the - * process of being created or locked. Returns zero on success and negative - * values on failure. + * Generate a CIPSO option using the DOI definition and security attributes + * passed to the function. Returns the length of the option on success and + * negative values on failure. * */ -int cipso_v4_sock_setattr(struct sock *sk, - const struct cipso_v4_doi *doi_def, - const struct netlbl_lsm_secattr *secattr) +static int cipso_v4_genopt(unsigned char *buf, u32 buf_len, + const struct cipso_v4_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) { - int ret_val = -EPERM; + int ret_val; u32 iter; - unsigned char *buf; - u32 buf_len = 0; - u32 opt_len; - struct ip_options *opt = NULL; - struct inet_sock *sk_inet; - struct inet_connection_sock *sk_conn; - /* In the case of sock_create_lite(), the sock->sk field is not - * defined yet but it is not a problem as the only users of these - * "lite" PF_INET sockets are functions which do an accept() call - * afterwards so we will label the socket as part of the accept(). */ - if (sk == NULL) - return 0; - - /* We allocate the maximum CIPSO option size here so we are probably - * being a little wasteful, but it makes our life _much_ easier later - * on and after all we are only talking about 40 bytes. */ - buf_len = CIPSO_V4_OPT_LEN_MAX; - buf = kmalloc(buf_len, GFP_ATOMIC); - if (buf == NULL) { - ret_val = -ENOMEM; - goto socket_setattr_failure; - } + if (buf_len <= CIPSO_V4_HDR_LEN) + return -ENOSPC; /* XXX - This code assumes only one tag per CIPSO option which isn't * really a good assumption to make but since we only support the MAC @@ -1772,9 +1790,14 @@ int cipso_v4_sock_setattr(struct sock *sk, &buf[CIPSO_V4_HDR_LEN], buf_len - CIPSO_V4_HDR_LEN); break; + case CIPSO_V4_TAG_LOCAL: + ret_val = cipso_v4_gentag_loc(doi_def, + secattr, + &buf[CIPSO_V4_HDR_LEN], + buf_len - CIPSO_V4_HDR_LEN); + break; default: - ret_val = -EPERM; - goto socket_setattr_failure; + return -EPERM; } iter++; @@ -1782,9 +1805,58 @@ int cipso_v4_sock_setattr(struct sock *sk, iter < CIPSO_V4_TAG_MAXCNT && doi_def->tags[iter] != CIPSO_V4_TAG_INVALID); if (ret_val < 0) - goto socket_setattr_failure; + return ret_val; cipso_v4_gentag_hdr(doi_def, buf, ret_val); - buf_len = CIPSO_V4_HDR_LEN + ret_val; + return CIPSO_V4_HDR_LEN + ret_val; +} + +/** + * cipso_v4_sock_setattr - Add a CIPSO option to a socket + * @sk: the socket + * @doi_def: the CIPSO DOI to use + * @secattr: the specific security attributes of the socket + * + * Description: + * Set the CIPSO option on the given socket using the DOI definition and + * security attributes passed to the function. This function requires + * exclusive access to @sk, which means it either needs to be in the + * process of being created or locked. Returns zero on success and negative + * values on failure. + * + */ +int cipso_v4_sock_setattr(struct sock *sk, + const struct cipso_v4_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -EPERM; + unsigned char *buf = NULL; + u32 buf_len; + u32 opt_len; + struct ip_options *opt = NULL; + struct inet_sock *sk_inet; + struct inet_connection_sock *sk_conn; + + /* In the case of sock_create_lite(), the sock->sk field is not + * defined yet but it is not a problem as the only users of these + * "lite" PF_INET sockets are functions which do an accept() call + * afterwards so we will label the socket as part of the accept(). */ + if (sk == NULL) + return 0; + + /* We allocate the maximum CIPSO option size here so we are probably + * being a little wasteful, but it makes our life _much_ easier later + * on and after all we are only talking about 40 bytes. */ + buf_len = CIPSO_V4_OPT_LEN_MAX; + buf = kmalloc(buf_len, GFP_ATOMIC); + if (buf == NULL) { + ret_val = -ENOMEM; + goto socket_setattr_failure; + } + + ret_val = cipso_v4_genopt(buf, buf_len, doi_def, secattr); + if (ret_val < 0) + goto socket_setattr_failure; + buf_len = ret_val; /* We can't use ip_options_get() directly because it makes a call to * ip_options_get_alloc() which allocates memory with GFP_KERNEL and @@ -1822,6 +1894,80 @@ socket_setattr_failure: } /** + * cipso_v4_sock_delattr - Delete the CIPSO option from a socket + * @sk: the socket + * + * Description: + * Removes the CIPSO option from a socket, if present. + * + */ +void cipso_v4_sock_delattr(struct sock *sk) +{ + u8 hdr_delta; + struct ip_options *opt; + struct inet_sock *sk_inet; + + sk_inet = inet_sk(sk); + opt = sk_inet->opt; + if (opt == NULL || opt->cipso == 0) + return; + + if (opt->srr || opt->rr || opt->ts || opt->router_alert) { + u8 cipso_len; + u8 cipso_off; + unsigned char *cipso_ptr; + int iter; + int optlen_new; + + cipso_off = opt->cipso - sizeof(struct iphdr); + cipso_ptr = &opt->__data[cipso_off]; + cipso_len = cipso_ptr[1]; + + if (opt->srr > opt->cipso) + opt->srr -= cipso_len; + if (opt->rr > opt->cipso) + opt->rr -= cipso_len; + if (opt->ts > opt->cipso) + opt->ts -= cipso_len; + if (opt->router_alert > opt->cipso) + opt->router_alert -= cipso_len; + opt->cipso = 0; + + memmove(cipso_ptr, cipso_ptr + cipso_len, + opt->optlen - cipso_off - cipso_len); + + /* determining the new total option length is tricky because of + * the padding necessary, the only thing i can think to do at + * this point is walk the options one-by-one, skipping the + * padding at the end to determine the actual option size and + * from there we can determine the new total option length */ + iter = 0; + optlen_new = 0; + while (iter < opt->optlen) + if (opt->__data[iter] != IPOPT_NOP) { + iter += opt->__data[iter + 1]; + optlen_new = iter; + } else + iter++; + hdr_delta = opt->optlen; + opt->optlen = (optlen_new + 3) & ~3; + hdr_delta -= opt->optlen; + } else { + /* only the cipso option was present on the socket so we can + * remove the entire option struct */ + sk_inet->opt = NULL; + hdr_delta = opt->optlen; + kfree(opt); + } + + if (sk_inet->is_icsk && hdr_delta > 0) { + struct inet_connection_sock *sk_conn = inet_csk(sk); + sk_conn->icsk_ext_hdr_len -= hdr_delta; + sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); + } +} + +/** * cipso_v4_getattr - Helper function for the cipso_v4_*_getattr functions * @cipso: the CIPSO v4 option * @secattr: the security attributes @@ -1859,6 +2005,9 @@ static int cipso_v4_getattr(const unsigned char *cipso, case CIPSO_V4_TAG_RANGE: ret_val = cipso_v4_parsetag_rng(doi_def, &cipso[6], secattr); break; + case CIPSO_V4_TAG_LOCAL: + ret_val = cipso_v4_parsetag_loc(doi_def, &cipso[6], secattr); + break; } if (ret_val == 0) secattr->type = NETLBL_NLTYPE_CIPSOV4; @@ -1893,6 +2042,123 @@ int cipso_v4_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) } /** + * cipso_v4_skbuff_setattr - Set the CIPSO option on a packet + * @skb: the packet + * @secattr: the security attributes + * + * Description: + * Set the CIPSO option on the given packet based on the security attributes. + * Returns a pointer to the IP header on success and NULL on failure. + * + */ +int cipso_v4_skbuff_setattr(struct sk_buff *skb, + const struct cipso_v4_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + struct iphdr *iph; + struct ip_options *opt = &IPCB(skb)->opt; + unsigned char buf[CIPSO_V4_OPT_LEN_MAX]; + u32 buf_len = CIPSO_V4_OPT_LEN_MAX; + u32 opt_len; + int len_delta; + + buf_len = cipso_v4_genopt(buf, buf_len, doi_def, secattr); + if (buf_len < 0) + return buf_len; + opt_len = (buf_len + 3) & ~3; + + /* we overwrite any existing options to ensure that we have enough + * room for the CIPSO option, the reason is that we _need_ to guarantee + * that the security label is applied to the packet - we do the same + * thing when using the socket options and it hasn't caused a problem, + * if we need to we can always revisit this choice later */ + + len_delta = opt_len - opt->optlen; + /* if we don't ensure enough headroom we could panic on the skb_push() + * call below so make sure we have enough, we are also "mangling" the + * packet so we should probably do a copy-on-write call anyway */ + ret_val = skb_cow(skb, skb_headroom(skb) + len_delta); + if (ret_val < 0) + return ret_val; + + if (len_delta > 0) { + /* we assume that the header + opt->optlen have already been + * "pushed" in ip_options_build() or similar */ + iph = ip_hdr(skb); + skb_push(skb, len_delta); + memmove((char *)iph - len_delta, iph, iph->ihl << 2); + skb_reset_network_header(skb); + iph = ip_hdr(skb); + } else if (len_delta < 0) { + iph = ip_hdr(skb); + memset(iph + 1, IPOPT_NOP, opt->optlen); + } else + iph = ip_hdr(skb); + + if (opt->optlen > 0) + memset(opt, 0, sizeof(*opt)); + opt->optlen = opt_len; + opt->cipso = sizeof(struct iphdr); + opt->is_changed = 1; + + /* we have to do the following because we are being called from a + * netfilter hook which means the packet already has had the header + * fields populated and the checksum calculated - yes this means we + * are doing more work than needed but we do it to keep the core + * stack clean and tidy */ + memcpy(iph + 1, buf, buf_len); + if (opt_len > buf_len) + memset((char *)(iph + 1) + buf_len, 0, opt_len - buf_len); + if (len_delta != 0) { + iph->ihl = 5 + (opt_len >> 2); + iph->tot_len = htons(skb->len); + } + ip_send_check(iph); + + return 0; +} + +/** + * cipso_v4_skbuff_delattr - Delete any CIPSO options from a packet + * @skb: the packet + * + * Description: + * Removes any and all CIPSO options from the given packet. Returns zero on + * success, negative values on failure. + * + */ +int cipso_v4_skbuff_delattr(struct sk_buff *skb) +{ + int ret_val; + struct iphdr *iph; + struct ip_options *opt = &IPCB(skb)->opt; + unsigned char *cipso_ptr; + + if (opt->cipso == 0) + return 0; + + /* since we are changing the packet we should make a copy */ + ret_val = skb_cow(skb, skb_headroom(skb)); + if (ret_val < 0) + return ret_val; + + /* the easiest thing to do is just replace the cipso option with noop + * options since we don't change the size of the packet, although we + * still need to recalculate the checksum */ + + iph = ip_hdr(skb); + cipso_ptr = (unsigned char *)iph + opt->cipso; + memset(cipso_ptr, IPOPT_NOOP, cipso_ptr[1]); + opt->cipso = 0; + opt->is_changed = 1; + + ip_send_check(iph); + + return 0; +} + +/** * cipso_v4_skbuff_getattr - Get the security attributes from the CIPSO option * @skb: the packet * @secattr: the security attributes diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 05ebce2881e..85c487b8572 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1345,7 +1345,7 @@ out: static void ipgre_netlink_parms(struct nlattr *data[], struct ip_tunnel_parm *parms) { - memset(parms, 0, sizeof(parms)); + memset(parms, 0, sizeof(*parms)); parms->iph.protocol = IPPROTO_GRE; diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index be3f18a7a40..2c88da6e786 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -438,7 +438,7 @@ int ip_options_compile(struct net *net, goto error; } opt->cipso = optptr - iph; - if (cipso_v4_validate(&optptr)) { + if (cipso_v4_validate(skb, &optptr)) { pp_ptr = optptr; goto error; } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 77e7b014872..cf6b121e1bb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1379,6 +1379,7 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) return RX_QUEUED; } +#ifdef CONFIG_MAC80211_MESH static ieee80211_rx_result ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) { @@ -1453,7 +1454,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) else return RX_DROP_MONITOR; } - +#endif static ieee80211_rx_result debug_noinline ieee80211_rx_h_data(struct ieee80211_rx_data *rx) @@ -1780,8 +1781,10 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_sub_if_data *sdata, /* must be after MMIC verify so header is counted in MPDU mic */ CALL_RXH(ieee80211_rx_h_remove_qos_control) CALL_RXH(ieee80211_rx_h_amsdu) +#ifdef CONFIG_MAC80211_MESH if (ieee80211_vif_is_mesh(&sdata->vif)) CALL_RXH(ieee80211_rx_h_mesh_fwding); +#endif CALL_RXH(ieee80211_rx_h_data) CALL_RXH(ieee80211_rx_h_ctrl) CALL_RXH(ieee80211_rx_h_action) diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig index de6004de80b..05048e40326 100644 --- a/net/netfilter/ipvs/Kconfig +++ b/net/netfilter/ipvs/Kconfig @@ -2,8 +2,8 @@ # IP Virtual Server configuration # menuconfig IP_VS - tristate "IP virtual server support (EXPERIMENTAL)" - depends on NETFILTER + tristate "IP virtual server support" + depends on NET && INET && NETFILTER ---help--- IP Virtual Server support will let you build a high-performance virtual server based on cluster of two or more real servers. This diff --git a/net/netfilter/nf_tproxy_core.c b/net/netfilter/nf_tproxy_core.c index fe34f4bf74c..cdc97f3105a 100644 --- a/net/netfilter/nf_tproxy_core.c +++ b/net/netfilter/nf_tproxy_core.c @@ -10,7 +10,6 @@ * */ -#include <linux/version.h> #include <linux/module.h> #include <linux/net.h> diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile index 8af18c0a47d..ea750e9df65 100644 --- a/net/netlabel/Makefile +++ b/net/netlabel/Makefile @@ -5,7 +5,8 @@ # # base objects -obj-y := netlabel_user.o netlabel_kapi.o netlabel_domainhash.o +obj-y := netlabel_user.o netlabel_kapi.o +obj-y += netlabel_domainhash.o netlabel_addrlist.o # management objects obj-y += netlabel_mgmt.o diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c new file mode 100644 index 00000000000..b0925a30335 --- /dev/null +++ b/net/netlabel/netlabel_addrlist.c @@ -0,0 +1,388 @@ +/* + * NetLabel Network Address Lists + * + * This file contains network address list functions used to manage ordered + * lists of network addresses for use by the NetLabel subsystem. The NetLabel + * system manages static and dynamic label mappings for network protocols such + * as CIPSO and RIPSO. + * + * Author: Paul Moore <paul.moore@hp.com> + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <linux/rcupdate.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/in.h> +#include <linux/in6.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <net/ip.h> +#include <net/ipv6.h> +#include <linux/audit.h> + +#include "netlabel_addrlist.h" + +/* + * Address List Functions + */ + +/** + * netlbl_af4list_search - Search for a matching IPv4 address entry + * @addr: IPv4 address + * @head: the list head + * + * Description: + * Searches the IPv4 address list given by @head. If a matching address entry + * is found it is returned, otherwise NULL is returned. The caller is + * responsible for calling the rcu_read_[un]lock() functions. + * + */ +struct netlbl_af4list *netlbl_af4list_search(__be32 addr, + struct list_head *head) +{ + struct netlbl_af4list *iter; + + list_for_each_entry_rcu(iter, head, list) + if (iter->valid && (addr & iter->mask) == iter->addr) + return iter; + + return NULL; +} + +/** + * netlbl_af4list_search_exact - Search for an exact IPv4 address entry + * @addr: IPv4 address + * @mask: IPv4 address mask + * @head: the list head + * + * Description: + * Searches the IPv4 address list given by @head. If an exact match if found + * it is returned, otherwise NULL is returned. The caller is responsible for + * calling the rcu_read_[un]lock() functions. + * + */ +struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr, + __be32 mask, + struct list_head *head) +{ + struct netlbl_af4list *iter; + + list_for_each_entry_rcu(iter, head, list) + if (iter->valid && iter->addr == addr && iter->mask == mask) + return iter; + + return NULL; +} + + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +/** + * netlbl_af6list_search - Search for a matching IPv6 address entry + * @addr: IPv6 address + * @head: the list head + * + * Description: + * Searches the IPv6 address list given by @head. If a matching address entry + * is found it is returned, otherwise NULL is returned. The caller is + * responsible for calling the rcu_read_[un]lock() functions. + * + */ +struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr, + struct list_head *head) +{ + struct netlbl_af6list *iter; + + list_for_each_entry_rcu(iter, head, list) + if (iter->valid && + ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0) + return iter; + + return NULL; +} + +/** + * netlbl_af6list_search_exact - Search for an exact IPv6 address entry + * @addr: IPv6 address + * @mask: IPv6 address mask + * @head: the list head + * + * Description: + * Searches the IPv6 address list given by @head. If an exact match if found + * it is returned, otherwise NULL is returned. The caller is responsible for + * calling the rcu_read_[un]lock() functions. + * + */ +struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr, + const struct in6_addr *mask, + struct list_head *head) +{ + struct netlbl_af6list *iter; + + list_for_each_entry_rcu(iter, head, list) + if (iter->valid && + ipv6_addr_equal(&iter->addr, addr) && + ipv6_addr_equal(&iter->mask, mask)) + return iter; + + return NULL; +} +#endif /* IPv6 */ + +/** + * netlbl_af4list_add - Add a new IPv4 address entry to a list + * @entry: address entry + * @head: the list head + * + * Description: + * Add a new address entry to the list pointed to by @head. On success zero is + * returned, otherwise a negative value is returned. The caller is responsible + * for calling the necessary locking functions. + * + */ +int netlbl_af4list_add(struct netlbl_af4list *entry, struct list_head *head) +{ + struct netlbl_af4list *iter; + + iter = netlbl_af4list_search(entry->addr, head); + if (iter != NULL && + iter->addr == entry->addr && iter->mask == entry->mask) + return -EEXIST; + + /* in order to speed up address searches through the list (the common + * case) we need to keep the list in order based on the size of the + * address mask such that the entry with the widest mask (smallest + * numerical value) appears first in the list */ + list_for_each_entry_rcu(iter, head, list) + if (iter->valid && + ntohl(entry->mask) > ntohl(iter->mask)) { + __list_add_rcu(&entry->list, + iter->list.prev, + &iter->list); + return 0; + } + list_add_tail_rcu(&entry->list, head); + return 0; +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +/** + * netlbl_af6list_add - Add a new IPv6 address entry to a list + * @entry: address entry + * @head: the list head + * + * Description: + * Add a new address entry to the list pointed to by @head. On success zero is + * returned, otherwise a negative value is returned. The caller is responsible + * for calling the necessary locking functions. + * + */ +int netlbl_af6list_add(struct netlbl_af6list *entry, struct list_head *head) +{ + struct netlbl_af6list *iter; + + iter = netlbl_af6list_search(&entry->addr, head); + if (iter != NULL && + ipv6_addr_equal(&iter->addr, &entry->addr) && + ipv6_addr_equal(&iter->mask, &entry->mask)) + return -EEXIST; + + /* in order to speed up address searches through the list (the common + * case) we need to keep the list in order based on the size of the + * address mask such that the entry with the widest mask (smallest + * numerical value) appears first in the list */ + list_for_each_entry_rcu(iter, head, list) + if (iter->valid && + ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) { + __list_add_rcu(&entry->list, + iter->list.prev, + &iter->list); + return 0; + } + list_add_tail_rcu(&entry->list, head); + return 0; +} +#endif /* IPv6 */ + +/** + * netlbl_af4list_remove_entry - Remove an IPv4 address entry + * @entry: address entry + * + * Description: + * Remove the specified IP address entry. The caller is responsible for + * calling the necessary locking functions. + * + */ +void netlbl_af4list_remove_entry(struct netlbl_af4list *entry) +{ + entry->valid = 0; + list_del_rcu(&entry->list); +} + +/** + * netlbl_af4list_remove - Remove an IPv4 address entry + * @addr: IP address + * @mask: IP address mask + * @head: the list head + * + * Description: + * Remove an IP address entry from the list pointed to by @head. Returns the + * entry on success, NULL on failure. The caller is responsible for calling + * the necessary locking functions. + * + */ +struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask, + struct list_head *head) +{ + struct netlbl_af4list *entry; + + entry = netlbl_af4list_search(addr, head); + if (entry != NULL && entry->addr == addr && entry->mask == mask) { + netlbl_af4list_remove_entry(entry); + return entry; + } + + return NULL; +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +/** + * netlbl_af6list_remove_entry - Remove an IPv6 address entry + * @entry: address entry + * + * Description: + * Remove the specified IP address entry. The caller is responsible for + * calling the necessary locking functions. + * + */ +void netlbl_af6list_remove_entry(struct netlbl_af6list *entry) +{ + entry->valid = 0; + list_del_rcu(&entry->list); +} + +/** + * netlbl_af6list_remove - Remove an IPv6 address entry + * @addr: IP address + * @mask: IP address mask + * @head: the list head + * + * Description: + * Remove an IP address entry from the list pointed to by @head. Returns the + * entry on success, NULL on failure. The caller is responsible for calling + * the necessary locking functions. + * + */ +struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr, + const struct in6_addr *mask, + struct list_head *head) +{ + struct netlbl_af6list *entry; + + entry = netlbl_af6list_search(addr, head); + if (entry != NULL && + ipv6_addr_equal(&entry->addr, addr) && + ipv6_addr_equal(&entry->mask, mask)) { + netlbl_af6list_remove_entry(entry); + return entry; + } + + return NULL; +} +#endif /* IPv6 */ + +/* + * Audit Helper Functions + */ + +/** + * netlbl_af4list_audit_addr - Audit an IPv4 address + * @audit_buf: audit buffer + * @src: true if source address, false if destination + * @dev: network interface + * @addr: IP address + * @mask: IP address mask + * + * Description: + * Write the IPv4 address and address mask, if necessary, to @audit_buf. + * + */ +void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf, + int src, const char *dev, + __be32 addr, __be32 mask) +{ + u32 mask_val = ntohl(mask); + char *dir = (src ? "src" : "dst"); + + if (dev != NULL) + audit_log_format(audit_buf, " netif=%s", dev); + audit_log_format(audit_buf, " %s=" NIPQUAD_FMT, dir, NIPQUAD(addr)); + if (mask_val != 0xffffffff) { + u32 mask_len = 0; + while (mask_val > 0) { + mask_val <<= 1; + mask_len++; + } + audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); + } +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +/** + * netlbl_af6list_audit_addr - Audit an IPv6 address + * @audit_buf: audit buffer + * @src: true if source address, false if destination + * @dev: network interface + * @addr: IP address + * @mask: IP address mask + * + * Description: + * Write the IPv6 address and address mask, if necessary, to @audit_buf. + * + */ +void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf, + int src, + const char *dev, + const struct in6_addr *addr, + const struct in6_addr *mask) +{ + char *dir = (src ? "src" : "dst"); + + if (dev != NULL) + audit_log_format(audit_buf, " netif=%s", dev); + audit_log_format(audit_buf, " %s=" NIP6_FMT, dir, NIP6(*addr)); + if (ntohl(mask->s6_addr32[3]) != 0xffffffff) { + u32 mask_len = 0; + u32 mask_val; + int iter = -1; + while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff) + mask_len += 32; + mask_val = ntohl(mask->s6_addr32[iter]); + while (mask_val > 0) { + mask_val <<= 1; + mask_len++; + } + audit_log_format(audit_buf, " %s_prefixlen=%d", dir, mask_len); + } +} +#endif /* IPv6 */ diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h new file mode 100644 index 00000000000..0242bead405 --- /dev/null +++ b/net/netlabel/netlabel_addrlist.h @@ -0,0 +1,189 @@ +/* + * NetLabel Network Address Lists + * + * This file contains network address list functions used to manage ordered + * lists of network addresses for use by the NetLabel subsystem. The NetLabel + * system manages static and dynamic label mappings for network protocols such + * as CIPSO and RIPSO. + * + * Author: Paul Moore <paul.moore@hp.com> + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _NETLABEL_ADDRLIST_H +#define _NETLABEL_ADDRLIST_H + +#include <linux/types.h> +#include <linux/rcupdate.h> +#include <linux/list.h> +#include <linux/in6.h> +#include <linux/audit.h> + +/** + * struct netlbl_af4list - NetLabel IPv4 address list + * @addr: IPv4 address + * @mask: IPv4 address mask + * @valid: valid flag + * @list: list structure, used internally + */ +struct netlbl_af4list { + __be32 addr; + __be32 mask; + + u32 valid; + struct list_head list; +}; + +/** + * struct netlbl_af6list - NetLabel IPv6 address list + * @addr: IPv6 address + * @mask: IPv6 address mask + * @valid: valid flag + * @list: list structure, used internally + */ +struct netlbl_af6list { + struct in6_addr addr; + struct in6_addr mask; + + u32 valid; + struct list_head list; +}; + +#define __af4list_entry(ptr) container_of(ptr, struct netlbl_af4list, list) + +static inline struct netlbl_af4list *__af4list_valid(struct list_head *s, + struct list_head *h) +{ + struct list_head *i = s; + struct netlbl_af4list *n = __af4list_entry(s); + while (i != h && !n->valid) { + i = i->next; + n = __af4list_entry(i); + } + return n; +} + +static inline struct netlbl_af4list *__af4list_valid_rcu(struct list_head *s, + struct list_head *h) +{ + struct list_head *i = s; + struct netlbl_af4list *n = __af4list_entry(s); + while (i != h && !n->valid) { + i = rcu_dereference(i->next); + n = __af4list_entry(i); + } + return n; +} + +#define netlbl_af4list_foreach(iter, head) \ + for (iter = __af4list_valid((head)->next, head); \ + prefetch(iter->list.next), &iter->list != (head); \ + iter = __af4list_valid(iter->list.next, head)) + +#define netlbl_af4list_foreach_rcu(iter, head) \ + for (iter = __af4list_valid_rcu((head)->next, head); \ + prefetch(iter->list.next), &iter->list != (head); \ + iter = __af4list_valid_rcu(iter->list.next, head)) + +#define netlbl_af4list_foreach_safe(iter, tmp, head) \ + for (iter = __af4list_valid((head)->next, head), \ + tmp = __af4list_valid(iter->list.next, head); \ + &iter->list != (head); \ + iter = tmp, tmp = __af4list_valid(iter->list.next, head)) + +int netlbl_af4list_add(struct netlbl_af4list *entry, + struct list_head *head); +struct netlbl_af4list *netlbl_af4list_remove(__be32 addr, __be32 mask, + struct list_head *head); +void netlbl_af4list_remove_entry(struct netlbl_af4list *entry); +struct netlbl_af4list *netlbl_af4list_search(__be32 addr, + struct list_head *head); +struct netlbl_af4list *netlbl_af4list_search_exact(__be32 addr, + __be32 mask, + struct list_head *head); +void netlbl_af4list_audit_addr(struct audit_buffer *audit_buf, + int src, const char *dev, + __be32 addr, __be32 mask); + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + +#define __af6list_entry(ptr) container_of(ptr, struct netlbl_af6list, list) + +static inline struct netlbl_af6list *__af6list_valid(struct list_head *s, + struct list_head *h) +{ + struct list_head *i = s; + struct netlbl_af6list *n = __af6list_entry(s); + while (i != h && !n->valid) { + i = i->next; + n = __af6list_entry(i); + } + return n; +} + +static inline struct netlbl_af6list *__af6list_valid_rcu(struct list_head *s, + struct list_head *h) +{ + struct list_head *i = s; + struct netlbl_af6list *n = __af6list_entry(s); + while (i != h && !n->valid) { + i = rcu_dereference(i->next); + n = __af6list_entry(i); + } + return n; +} + +#define netlbl_af6list_foreach(iter, head) \ + for (iter = __af6list_valid((head)->next, head); \ + prefetch(iter->list.next), &iter->list != (head); \ + iter = __af6list_valid(iter->list.next, head)) + +#define netlbl_af6list_foreach_rcu(iter, head) \ + for (iter = __af6list_valid_rcu((head)->next, head); \ + prefetch(iter->list.next), &iter->list != (head); \ + iter = __af6list_valid_rcu(iter->list.next, head)) + +#define netlbl_af6list_foreach_safe(iter, tmp, head) \ + for (iter = __af6list_valid((head)->next, head), \ + tmp = __af6list_valid(iter->list.next, head); \ + &iter->list != (head); \ + iter = tmp, tmp = __af6list_valid(iter->list.next, head)) + +int netlbl_af6list_add(struct netlbl_af6list *entry, + struct list_head *head); +struct netlbl_af6list *netlbl_af6list_remove(const struct in6_addr *addr, + const struct in6_addr *mask, + struct list_head *head); +void netlbl_af6list_remove_entry(struct netlbl_af6list *entry); +struct netlbl_af6list *netlbl_af6list_search(const struct in6_addr *addr, + struct list_head *head); +struct netlbl_af6list *netlbl_af6list_search_exact(const struct in6_addr *addr, + const struct in6_addr *mask, + struct list_head *head); +void netlbl_af6list_audit_addr(struct audit_buffer *audit_buf, + int src, + const char *dev, + const struct in6_addr *addr, + const struct in6_addr *mask); +#endif /* IPV6 */ + +#endif diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c index 0aec318bf0e..fff32b70efa 100644 --- a/net/netlabel/netlabel_cipso_v4.c +++ b/net/netlabel/netlabel_cipso_v4.c @@ -43,6 +43,7 @@ #include "netlabel_user.h" #include "netlabel_cipso_v4.h" #include "netlabel_mgmt.h" +#include "netlabel_domainhash.h" /* Argument struct for cipso_v4_doi_walk() */ struct netlbl_cipsov4_doiwalk_arg { @@ -51,6 +52,12 @@ struct netlbl_cipsov4_doiwalk_arg { u32 seq; }; +/* Argument struct for netlbl_domhsh_walk() */ +struct netlbl_domhsh_walk_arg { + struct netlbl_audit *audit_info; + u32 doi; +}; + /* NetLabel Generic NETLINK CIPSOv4 family */ static struct genl_family netlbl_cipsov4_gnl_family = { .id = GENL_ID_GENERATE, @@ -81,32 +88,6 @@ static const struct nla_policy netlbl_cipsov4_genl_policy[NLBL_CIPSOV4_A_MAX + 1 */ /** - * netlbl_cipsov4_doi_free - Frees a CIPSO V4 DOI definition - * @entry: the entry's RCU field - * - * Description: - * This function is designed to be used as a callback to the call_rcu() - * function so that the memory allocated to the DOI definition can be released - * safely. - * - */ -void netlbl_cipsov4_doi_free(struct rcu_head *entry) -{ - struct cipso_v4_doi *ptr; - - ptr = container_of(entry, struct cipso_v4_doi, rcu); - switch (ptr->type) { - case CIPSO_V4_MAP_STD: - kfree(ptr->map.std->lvl.cipso); - kfree(ptr->map.std->lvl.local); - kfree(ptr->map.std->cat.cipso); - kfree(ptr->map.std->cat.local); - break; - } - kfree(ptr); -} - -/** * netlbl_cipsov4_add_common - Parse the common sections of a ADD message * @info: the Generic NETLINK info block * @doi_def: the CIPSO V4 DOI definition @@ -151,9 +132,9 @@ static int netlbl_cipsov4_add_common(struct genl_info *info, * @info: the Generic NETLINK info block * * Description: - * Create a new CIPSO_V4_MAP_STD DOI definition based on the given ADD message - * and add it to the CIPSO V4 engine. Return zero on success and non-zero on - * error. + * Create a new CIPSO_V4_MAP_TRANS DOI definition based on the given ADD + * message and add it to the CIPSO V4 engine. Return zero on success and + * non-zero on error. * */ static int netlbl_cipsov4_add_std(struct genl_info *info) @@ -183,7 +164,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info) ret_val = -ENOMEM; goto add_std_failure; } - doi_def->type = CIPSO_V4_MAP_STD; + doi_def->type = CIPSO_V4_MAP_TRANS; ret_val = netlbl_cipsov4_add_common(info, doi_def); if (ret_val != 0) @@ -342,7 +323,7 @@ static int netlbl_cipsov4_add_std(struct genl_info *info) add_std_failure: if (doi_def) - netlbl_cipsov4_doi_free(&doi_def->rcu); + cipso_v4_doi_free(doi_def); return ret_val; } @@ -379,7 +360,44 @@ static int netlbl_cipsov4_add_pass(struct genl_info *info) return 0; add_pass_failure: - netlbl_cipsov4_doi_free(&doi_def->rcu); + cipso_v4_doi_free(doi_def); + return ret_val; +} + +/** + * netlbl_cipsov4_add_local - Adds a CIPSO V4 DOI definition + * @info: the Generic NETLINK info block + * + * Description: + * Create a new CIPSO_V4_MAP_LOCAL DOI definition based on the given ADD + * message and add it to the CIPSO V4 engine. Return zero on success and + * non-zero on error. + * + */ +static int netlbl_cipsov4_add_local(struct genl_info *info) +{ + int ret_val; + struct cipso_v4_doi *doi_def = NULL; + + if (!info->attrs[NLBL_CIPSOV4_A_TAGLST]) + return -EINVAL; + + doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); + if (doi_def == NULL) + return -ENOMEM; + doi_def->type = CIPSO_V4_MAP_LOCAL; + + ret_val = netlbl_cipsov4_add_common(info, doi_def); + if (ret_val != 0) + goto add_local_failure; + + ret_val = cipso_v4_doi_add(doi_def); + if (ret_val != 0) + goto add_local_failure; + return 0; + +add_local_failure: + cipso_v4_doi_free(doi_def); return ret_val; } @@ -412,14 +430,18 @@ static int netlbl_cipsov4_add(struct sk_buff *skb, struct genl_info *info) type = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_MTYPE]); switch (type) { - case CIPSO_V4_MAP_STD: - type_str = "std"; + case CIPSO_V4_MAP_TRANS: + type_str = "trans"; ret_val = netlbl_cipsov4_add_std(info); break; case CIPSO_V4_MAP_PASS: type_str = "pass"; ret_val = netlbl_cipsov4_add_pass(info); break; + case CIPSO_V4_MAP_LOCAL: + type_str = "local"; + ret_val = netlbl_cipsov4_add_local(info); + break; } if (ret_val == 0) atomic_inc(&netlabel_mgmt_protocount); @@ -491,7 +513,7 @@ list_start: doi_def = cipso_v4_doi_getdef(doi); if (doi_def == NULL) { ret_val = -EINVAL; - goto list_failure; + goto list_failure_lock; } ret_val = nla_put_u32(ans_skb, NLBL_CIPSOV4_A_MTYPE, doi_def->type); @@ -516,7 +538,7 @@ list_start: nla_nest_end(ans_skb, nla_a); switch (doi_def->type) { - case CIPSO_V4_MAP_STD: + case CIPSO_V4_MAP_TRANS: nla_a = nla_nest_start(ans_skb, NLBL_CIPSOV4_A_MLSLVLLST); if (nla_a == NULL) { ret_val = -ENOMEM; @@ -655,7 +677,7 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb, struct netlink_callback *cb) { struct netlbl_cipsov4_doiwalk_arg cb_arg; - int doi_skip = cb->args[0]; + u32 doi_skip = cb->args[0]; cb_arg.nl_cb = cb; cb_arg.skb = skb; @@ -668,6 +690,29 @@ static int netlbl_cipsov4_listall(struct sk_buff *skb, } /** + * netlbl_cipsov4_remove_cb - netlbl_cipsov4_remove() callback for REMOVE + * @entry: LSM domain mapping entry + * @arg: the netlbl_domhsh_walk_arg structure + * + * Description: + * This function is intended for use by netlbl_cipsov4_remove() as the callback + * for the netlbl_domhsh_walk() function; it removes LSM domain map entries + * which are associated with the CIPSO DOI specified in @arg. Returns zero on + * success, negative values on failure. + * + */ +static int netlbl_cipsov4_remove_cb(struct netlbl_dom_map *entry, void *arg) +{ + struct netlbl_domhsh_walk_arg *cb_arg = arg; + + if (entry->type == NETLBL_NLTYPE_CIPSOV4 && + entry->type_def.cipsov4->doi == cb_arg->doi) + return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info); + + return 0; +} + +/** * netlbl_cipsov4_remove - Handle a REMOVE message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block @@ -681,8 +726,11 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) { int ret_val = -EINVAL; u32 doi = 0; + struct netlbl_domhsh_walk_arg cb_arg; struct audit_buffer *audit_buf; struct netlbl_audit audit_info; + u32 skip_bkt = 0; + u32 skip_chain = 0; if (!info->attrs[NLBL_CIPSOV4_A_DOI]) return -EINVAL; @@ -690,11 +738,15 @@ static int netlbl_cipsov4_remove(struct sk_buff *skb, struct genl_info *info) doi = nla_get_u32(info->attrs[NLBL_CIPSOV4_A_DOI]); netlbl_netlink_auditinfo(skb, &audit_info); - ret_val = cipso_v4_doi_remove(doi, - &audit_info, - netlbl_cipsov4_doi_free); - if (ret_val == 0) - atomic_dec(&netlabel_mgmt_protocount); + cb_arg.doi = doi; + cb_arg.audit_info = &audit_info; + ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain, + netlbl_cipsov4_remove_cb, &cb_arg); + if (ret_val == 0 || ret_val == -ENOENT) { + ret_val = cipso_v4_doi_remove(doi, &audit_info); + if (ret_val == 0) + atomic_dec(&netlabel_mgmt_protocount); + } audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_DEL, &audit_info); diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h index 220cb9d06b4..c8a4079261f 100644 --- a/net/netlabel/netlabel_cipso_v4.h +++ b/net/netlabel/netlabel_cipso_v4.h @@ -45,12 +45,13 @@ * NLBL_CIPSOV4_A_MTYPE * NLBL_CIPSOV4_A_TAGLST * - * If using CIPSO_V4_MAP_STD the following attributes are required: + * If using CIPSO_V4_MAP_TRANS the following attributes are required: * * NLBL_CIPSOV4_A_MLSLVLLST * NLBL_CIPSOV4_A_MLSCATLST * - * If using CIPSO_V4_MAP_PASS no additional attributes are required. + * If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes + * are required. * * o REMOVE: * Sent by an application to remove a specific DOI mapping table from the @@ -76,12 +77,13 @@ * NLBL_CIPSOV4_A_MTYPE * NLBL_CIPSOV4_A_TAGLST * - * If using CIPSO_V4_MAP_STD the following attributes are required: + * If using CIPSO_V4_MAP_TRANS the following attributes are required: * * NLBL_CIPSOV4_A_MLSLVLLST * NLBL_CIPSOV4_A_MLSCATLST * - * If using CIPSO_V4_MAP_PASS no additional attributes are required. + * If using CIPSO_V4_MAP_PASS or CIPSO_V4_MAP_LOCAL no additional attributes + * are required. * * o LISTALL: * This message is sent by an application to list the valid DOIs on the diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 643c032a3a5..5fadf10e5dd 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -11,7 +11,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,6 +40,7 @@ #include <asm/bug.h> #include "netlabel_mgmt.h" +#include "netlabel_addrlist.h" #include "netlabel_domainhash.h" #include "netlabel_user.h" @@ -72,8 +73,28 @@ static struct netlbl_dom_map *netlbl_domhsh_def = NULL; static void netlbl_domhsh_free_entry(struct rcu_head *entry) { struct netlbl_dom_map *ptr; + struct netlbl_af4list *iter4; + struct netlbl_af4list *tmp4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *iter6; + struct netlbl_af6list *tmp6; +#endif /* IPv6 */ ptr = container_of(entry, struct netlbl_dom_map, rcu); + if (ptr->type == NETLBL_NLTYPE_ADDRSELECT) { + netlbl_af4list_foreach_safe(iter4, tmp4, + &ptr->type_def.addrsel->list4) { + netlbl_af4list_remove_entry(iter4); + kfree(netlbl_domhsh_addr4_entry(iter4)); + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_safe(iter6, tmp6, + &ptr->type_def.addrsel->list6) { + netlbl_af6list_remove_entry(iter6); + kfree(netlbl_domhsh_addr6_entry(iter6)); + } +#endif /* IPv6 */ + } kfree(ptr->domain); kfree(ptr); } @@ -115,13 +136,13 @@ static u32 netlbl_domhsh_hash(const char *key) static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain) { u32 bkt; + struct list_head *bkt_list; struct netlbl_dom_map *iter; if (domain != NULL) { bkt = netlbl_domhsh_hash(domain); - list_for_each_entry_rcu(iter, - &rcu_dereference(netlbl_domhsh)->tbl[bkt], - list) + bkt_list = &rcu_dereference(netlbl_domhsh)->tbl[bkt]; + list_for_each_entry_rcu(iter, bkt_list, list) if (iter->valid && strcmp(iter->domain, domain) == 0) return iter; } @@ -156,6 +177,69 @@ static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain) return entry; } +/** + * netlbl_domhsh_audit_add - Generate an audit entry for an add event + * @entry: the entry being added + * @addr4: the IPv4 address information + * @addr6: the IPv6 address information + * @result: the result code + * @audit_info: NetLabel audit information + * + * Description: + * Generate an audit record for adding a new NetLabel/LSM mapping entry with + * the given information. Caller is responsibile for holding the necessary + * locks. + * + */ +static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry, + struct netlbl_af4list *addr4, + struct netlbl_af6list *addr6, + int result, + struct netlbl_audit *audit_info) +{ + struct audit_buffer *audit_buf; + struct cipso_v4_doi *cipsov4 = NULL; + u32 type; + + audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info); + if (audit_buf != NULL) { + audit_log_format(audit_buf, " nlbl_domain=%s", + entry->domain ? entry->domain : "(default)"); + if (addr4 != NULL) { + struct netlbl_domaddr4_map *map4; + map4 = netlbl_domhsh_addr4_entry(addr4); + type = map4->type; + cipsov4 = map4->type_def.cipsov4; + netlbl_af4list_audit_addr(audit_buf, 0, NULL, + addr4->addr, addr4->mask); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + } else if (addr6 != NULL) { + struct netlbl_domaddr6_map *map6; + map6 = netlbl_domhsh_addr6_entry(addr6); + type = map6->type; + netlbl_af6list_audit_addr(audit_buf, 0, NULL, + &addr6->addr, &addr6->mask); +#endif /* IPv6 */ + } else { + type = entry->type; + cipsov4 = entry->type_def.cipsov4; + } + switch (type) { + case NETLBL_NLTYPE_UNLABELED: + audit_log_format(audit_buf, " nlbl_protocol=unlbl"); + break; + case NETLBL_NLTYPE_CIPSOV4: + BUG_ON(cipsov4 == NULL); + audit_log_format(audit_buf, + " nlbl_protocol=cipsov4 cipso_doi=%u", + cipsov4->doi); + break; + } + audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0); + audit_log_end(audit_buf); + } +} + /* * Domain Hash Table Functions */ @@ -213,74 +297,106 @@ int __init netlbl_domhsh_init(u32 size) int netlbl_domhsh_add(struct netlbl_dom_map *entry, struct netlbl_audit *audit_info) { - int ret_val; - u32 bkt; - struct audit_buffer *audit_buf; - - switch (entry->type) { - case NETLBL_NLTYPE_UNLABELED: - ret_val = 0; - break; - case NETLBL_NLTYPE_CIPSOV4: - ret_val = cipso_v4_doi_domhsh_add(entry->type_def.cipsov4, - entry->domain); - break; - default: - return -EINVAL; - } - if (ret_val != 0) - return ret_val; - - entry->valid = 1; - INIT_RCU_HEAD(&entry->rcu); + int ret_val = 0; + struct netlbl_dom_map *entry_old; + struct netlbl_af4list *iter4; + struct netlbl_af4list *tmp4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *iter6; + struct netlbl_af6list *tmp6; +#endif /* IPv6 */ rcu_read_lock(); + spin_lock(&netlbl_domhsh_lock); - if (entry->domain != NULL) { - bkt = netlbl_domhsh_hash(entry->domain); - if (netlbl_domhsh_search(entry->domain) == NULL) + if (entry->domain != NULL) + entry_old = netlbl_domhsh_search(entry->domain); + else + entry_old = netlbl_domhsh_search_def(entry->domain); + if (entry_old == NULL) { + entry->valid = 1; + INIT_RCU_HEAD(&entry->rcu); + + if (entry->domain != NULL) { + u32 bkt = netlbl_domhsh_hash(entry->domain); list_add_tail_rcu(&entry->list, &rcu_dereference(netlbl_domhsh)->tbl[bkt]); - else - ret_val = -EEXIST; - } else { - INIT_LIST_HEAD(&entry->list); - if (rcu_dereference(netlbl_domhsh_def) == NULL) + } else { + INIT_LIST_HEAD(&entry->list); rcu_assign_pointer(netlbl_domhsh_def, entry); - else - ret_val = -EEXIST; - } - spin_unlock(&netlbl_domhsh_lock); - audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info); - if (audit_buf != NULL) { - audit_log_format(audit_buf, - " nlbl_domain=%s", - entry->domain ? entry->domain : "(default)"); - switch (entry->type) { - case NETLBL_NLTYPE_UNLABELED: - audit_log_format(audit_buf, " nlbl_protocol=unlbl"); - break; - case NETLBL_NLTYPE_CIPSOV4: - audit_log_format(audit_buf, - " nlbl_protocol=cipsov4 cipso_doi=%u", - entry->type_def.cipsov4->doi); - break; } - audit_log_format(audit_buf, " res=%u", ret_val == 0 ? 1 : 0); - audit_log_end(audit_buf); - } - rcu_read_unlock(); - if (ret_val != 0) { - switch (entry->type) { - case NETLBL_NLTYPE_CIPSOV4: - if (cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4, - entry->domain) != 0) - BUG(); - break; + if (entry->type == NETLBL_NLTYPE_ADDRSELECT) { + netlbl_af4list_foreach_rcu(iter4, + &entry->type_def.addrsel->list4) + netlbl_domhsh_audit_add(entry, iter4, NULL, + ret_val, audit_info); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(iter6, + &entry->type_def.addrsel->list6) + netlbl_domhsh_audit_add(entry, NULL, iter6, + ret_val, audit_info); +#endif /* IPv6 */ + } else + netlbl_domhsh_audit_add(entry, NULL, NULL, + ret_val, audit_info); + } else if (entry_old->type == NETLBL_NLTYPE_ADDRSELECT && + entry->type == NETLBL_NLTYPE_ADDRSELECT) { + struct list_head *old_list4; + struct list_head *old_list6; + + old_list4 = &entry_old->type_def.addrsel->list4; + old_list6 = &entry_old->type_def.addrsel->list6; + + /* we only allow the addition of address selectors if all of + * the selectors do not exist in the existing domain map */ + netlbl_af4list_foreach_rcu(iter4, + &entry->type_def.addrsel->list4) + if (netlbl_af4list_search_exact(iter4->addr, + iter4->mask, + old_list4)) { + ret_val = -EEXIST; + goto add_return; + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(iter6, + &entry->type_def.addrsel->list6) + if (netlbl_af6list_search_exact(&iter6->addr, + &iter6->mask, + old_list6)) { + ret_val = -EEXIST; + goto add_return; + } +#endif /* IPv6 */ + + netlbl_af4list_foreach_safe(iter4, tmp4, + &entry->type_def.addrsel->list4) { + netlbl_af4list_remove_entry(iter4); + iter4->valid = 1; + ret_val = netlbl_af4list_add(iter4, old_list4); + netlbl_domhsh_audit_add(entry_old, iter4, NULL, + ret_val, audit_info); + if (ret_val != 0) + goto add_return; } - } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_safe(iter6, tmp6, + &entry->type_def.addrsel->list6) { + netlbl_af6list_remove_entry(iter6); + iter6->valid = 1; + ret_val = netlbl_af6list_add(iter6, old_list6); + netlbl_domhsh_audit_add(entry_old, NULL, iter6, + ret_val, audit_info); + if (ret_val != 0) + goto add_return; + } +#endif /* IPv6 */ + } else + ret_val = -EINVAL; +add_return: + spin_unlock(&netlbl_domhsh_lock); + rcu_read_unlock(); return ret_val; } @@ -302,35 +418,26 @@ int netlbl_domhsh_add_default(struct netlbl_dom_map *entry, } /** - * netlbl_domhsh_remove - Removes an entry from the domain hash table - * @domain: the domain to remove + * netlbl_domhsh_remove_entry - Removes a given entry from the domain table + * @entry: the entry to remove * @audit_info: NetLabel audit information * * Description: * Removes an entry from the domain hash table and handles any updates to the - * lower level protocol handler (i.e. CIPSO). Returns zero on success, - * negative on failure. + * lower level protocol handler (i.e. CIPSO). Caller is responsible for + * ensuring that the RCU read lock is held. Returns zero on success, negative + * on failure. * */ -int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) +int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, + struct netlbl_audit *audit_info) { - int ret_val = -ENOENT; - struct netlbl_dom_map *entry; + int ret_val = 0; struct audit_buffer *audit_buf; - rcu_read_lock(); - if (domain) - entry = netlbl_domhsh_search(domain); - else - entry = netlbl_domhsh_search_def(domain); if (entry == NULL) - goto remove_return; - switch (entry->type) { - case NETLBL_NLTYPE_CIPSOV4: - cipso_v4_doi_domhsh_remove(entry->type_def.cipsov4, - entry->domain); - break; - } + return -ENOENT; + spin_lock(&netlbl_domhsh_lock); if (entry->valid) { entry->valid = 0; @@ -338,8 +445,8 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) list_del_rcu(&entry->list); else rcu_assign_pointer(netlbl_domhsh_def, NULL); - ret_val = 0; - } + } else + ret_val = -ENOENT; spin_unlock(&netlbl_domhsh_lock); audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_DEL, audit_info); @@ -351,10 +458,54 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) audit_log_end(audit_buf); } -remove_return: - rcu_read_unlock(); - if (ret_val == 0) + if (ret_val == 0) { + struct netlbl_af4list *iter4; + struct netlbl_domaddr4_map *map4; + + switch (entry->type) { + case NETLBL_NLTYPE_ADDRSELECT: + netlbl_af4list_foreach_rcu(iter4, + &entry->type_def.addrsel->list4) { + map4 = netlbl_domhsh_addr4_entry(iter4); + cipso_v4_doi_putdef(map4->type_def.cipsov4); + } + /* no need to check the IPv6 list since we currently + * support only unlabeled protocols for IPv6 */ + break; + case NETLBL_NLTYPE_CIPSOV4: + cipso_v4_doi_putdef(entry->type_def.cipsov4); + break; + } call_rcu(&entry->rcu, netlbl_domhsh_free_entry); + } + + return ret_val; +} + +/** + * netlbl_domhsh_remove - Removes an entry from the domain hash table + * @domain: the domain to remove + * @audit_info: NetLabel audit information + * + * Description: + * Removes an entry from the domain hash table and handles any updates to the + * lower level protocol handler (i.e. CIPSO). Returns zero on success, + * negative on failure. + * + */ +int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) +{ + int ret_val; + struct netlbl_dom_map *entry; + + rcu_read_lock(); + if (domain) + entry = netlbl_domhsh_search(domain); + else + entry = netlbl_domhsh_search_def(domain); + ret_val = netlbl_domhsh_remove_entry(entry, audit_info); + rcu_read_unlock(); + return ret_val; } @@ -389,6 +540,70 @@ struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain) } /** + * netlbl_domhsh_getentry_af4 - Get an entry from the domain hash table + * @domain: the domain name to search for + * @addr: the IP address to search for + * + * Description: + * Look through the domain hash table searching for an entry to match @domain + * and @addr, return a pointer to a copy of the entry or NULL. The caller is + * responsible for ensuring that rcu_read_[un]lock() is called. + * + */ +struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain, + __be32 addr) +{ + struct netlbl_dom_map *dom_iter; + struct netlbl_af4list *addr_iter; + + dom_iter = netlbl_domhsh_search_def(domain); + if (dom_iter == NULL) + return NULL; + if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT) + return NULL; + + addr_iter = netlbl_af4list_search(addr, + &dom_iter->type_def.addrsel->list4); + if (addr_iter == NULL) + return NULL; + + return netlbl_domhsh_addr4_entry(addr_iter); +} + +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +/** + * netlbl_domhsh_getentry_af6 - Get an entry from the domain hash table + * @domain: the domain name to search for + * @addr: the IP address to search for + * + * Description: + * Look through the domain hash table searching for an entry to match @domain + * and @addr, return a pointer to a copy of the entry or NULL. The caller is + * responsible for ensuring that rcu_read_[un]lock() is called. + * + */ +struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain, + const struct in6_addr *addr) +{ + struct netlbl_dom_map *dom_iter; + struct netlbl_af6list *addr_iter; + + dom_iter = netlbl_domhsh_search_def(domain); + if (dom_iter == NULL) + return NULL; + if (dom_iter->type != NETLBL_NLTYPE_ADDRSELECT) + return NULL; + + addr_iter = netlbl_af6list_search(addr, + &dom_iter->type_def.addrsel->list6); + if (addr_iter == NULL) + return NULL; + + return netlbl_domhsh_addr6_entry(addr_iter); +} +#endif /* IPv6 */ + +/** * netlbl_domhsh_walk - Iterate through the domain mapping hash table * @skip_bkt: the number of buckets to skip at the start * @skip_chain: the number of entries to skip in the first iterated bucket @@ -410,6 +625,7 @@ int netlbl_domhsh_walk(u32 *skip_bkt, { int ret_val = -ENOENT; u32 iter_bkt; + struct list_head *iter_list; struct netlbl_dom_map *iter_entry; u32 chain_cnt = 0; @@ -417,9 +633,8 @@ int netlbl_domhsh_walk(u32 *skip_bkt, for (iter_bkt = *skip_bkt; iter_bkt < rcu_dereference(netlbl_domhsh)->size; iter_bkt++, chain_cnt = 0) { - list_for_each_entry_rcu(iter_entry, - &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt], - list) + iter_list = &rcu_dereference(netlbl_domhsh)->tbl[iter_bkt]; + list_for_each_entry_rcu(iter_entry, iter_list, list) if (iter_entry->valid) { if (chain_cnt++ < *skip_chain) continue; diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index 8220990ceb9..bfcb6763a1a 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -11,7 +11,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,16 +36,43 @@ #include <linux/rcupdate.h> #include <linux/list.h> +#include "netlabel_addrlist.h" + /* Domain hash table size */ /* XXX - currently this number is an uneducated guess */ #define NETLBL_DOMHSH_BITSIZE 7 -/* Domain mapping definition struct */ +/* Domain mapping definition structures */ +#define netlbl_domhsh_addr4_entry(iter) \ + container_of(iter, struct netlbl_domaddr4_map, list) +struct netlbl_domaddr4_map { + u32 type; + union { + struct cipso_v4_doi *cipsov4; + } type_def; + + struct netlbl_af4list list; +}; +#define netlbl_domhsh_addr6_entry(iter) \ + container_of(iter, struct netlbl_domaddr6_map, list) +struct netlbl_domaddr6_map { + u32 type; + + /* NOTE: no 'type_def' union needed at present since we don't currently + * support any IPv6 labeling protocols */ + + struct netlbl_af6list list; +}; +struct netlbl_domaddr_map { + struct list_head list4; + struct list_head list6; +}; struct netlbl_dom_map { char *domain; u32 type; union { struct cipso_v4_doi *cipsov4; + struct netlbl_domaddr_map *addrsel; } type_def; u32 valid; @@ -61,12 +88,21 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry, struct netlbl_audit *audit_info); int netlbl_domhsh_add_default(struct netlbl_dom_map *entry, struct netlbl_audit *audit_info); +int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, + struct netlbl_audit *audit_info); int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info); int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info); struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain); +struct netlbl_domaddr4_map *netlbl_domhsh_getentry_af4(const char *domain, + __be32 addr); int netlbl_domhsh_walk(u32 *skip_bkt, u32 *skip_chain, int (*callback) (struct netlbl_dom_map *entry, void *arg), void *cb_arg); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +struct netlbl_domaddr6_map *netlbl_domhsh_getentry_af6(const char *domain, + const struct in6_addr *addr); +#endif /* IPv6 */ + #endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 39793a1a93a..b32eceb3ab0 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -10,7 +10,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,7 +82,7 @@ int netlbl_cfg_unlbl_add_map(const char *domain, entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) - goto cfg_unlbl_add_map_failure; + return -ENOMEM; if (domain != NULL) { entry->domain = kstrdup(domain, GFP_ATOMIC); if (entry->domain == NULL) @@ -104,49 +104,6 @@ cfg_unlbl_add_map_failure: } /** - * netlbl_cfg_cipsov4_add - Add a new CIPSOv4 DOI definition - * @doi_def: the DOI definition - * @audit_info: NetLabel audit information - * - * Description: - * Add a new CIPSOv4 DOI definition to the NetLabel subsystem. Returns zero on - * success, negative values on failure. - * - */ -int netlbl_cfg_cipsov4_add(struct cipso_v4_doi *doi_def, - struct netlbl_audit *audit_info) -{ - int ret_val; - const char *type_str; - struct audit_buffer *audit_buf; - - ret_val = cipso_v4_doi_add(doi_def); - - audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, - audit_info); - if (audit_buf != NULL) { - switch (doi_def->type) { - case CIPSO_V4_MAP_STD: - type_str = "std"; - break; - case CIPSO_V4_MAP_PASS: - type_str = "pass"; - break; - default: - type_str = "(unknown)"; - } - audit_log_format(audit_buf, - " cipso_doi=%u cipso_type=%s res=%u", - doi_def->doi, - type_str, - ret_val == 0 ? 1 : 0); - audit_log_end(audit_buf); - } - - return ret_val; -} - -/** * netlbl_cfg_cipsov4_add_map - Add a new CIPSOv4 DOI definition and mapping * @doi_def: the DOI definition * @domain: the domain mapping to add @@ -164,58 +121,71 @@ int netlbl_cfg_cipsov4_add_map(struct cipso_v4_doi *doi_def, struct netlbl_audit *audit_info) { int ret_val = -ENOMEM; + u32 doi; + u32 doi_type; struct netlbl_dom_map *entry; + const char *type_str; + struct audit_buffer *audit_buf; + + doi = doi_def->doi; + doi_type = doi_def->type; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) - goto cfg_cipsov4_add_map_failure; + return -ENOMEM; if (domain != NULL) { entry->domain = kstrdup(domain, GFP_ATOMIC); if (entry->domain == NULL) goto cfg_cipsov4_add_map_failure; } - entry->type = NETLBL_NLTYPE_CIPSOV4; - entry->type_def.cipsov4 = doi_def; - - /* Grab a RCU read lock here so nothing happens to the doi_def variable - * between adding it to the CIPSOv4 protocol engine and adding a - * domain mapping for it. */ - rcu_read_lock(); - ret_val = netlbl_cfg_cipsov4_add(doi_def, audit_info); + ret_val = cipso_v4_doi_add(doi_def); if (ret_val != 0) - goto cfg_cipsov4_add_map_failure_unlock; + goto cfg_cipsov4_add_map_failure_remove_doi; + entry->type = NETLBL_NLTYPE_CIPSOV4; + entry->type_def.cipsov4 = cipso_v4_doi_getdef(doi); + if (entry->type_def.cipsov4 == NULL) { + ret_val = -ENOENT; + goto cfg_cipsov4_add_map_failure_remove_doi; + } ret_val = netlbl_domhsh_add(entry, audit_info); if (ret_val != 0) - goto cfg_cipsov4_add_map_failure_remove_doi; - rcu_read_unlock(); + goto cfg_cipsov4_add_map_failure_release_doi; - return 0; +cfg_cipsov4_add_map_return: + audit_buf = netlbl_audit_start_common(AUDIT_MAC_CIPSOV4_ADD, + audit_info); + if (audit_buf != NULL) { + switch (doi_type) { + case CIPSO_V4_MAP_TRANS: + type_str = "trans"; + break; + case CIPSO_V4_MAP_PASS: + type_str = "pass"; + break; + case CIPSO_V4_MAP_LOCAL: + type_str = "local"; + break; + default: + type_str = "(unknown)"; + } + audit_log_format(audit_buf, + " cipso_doi=%u cipso_type=%s res=%u", + doi, type_str, ret_val == 0 ? 1 : 0); + audit_log_end(audit_buf); + } + return ret_val; + +cfg_cipsov4_add_map_failure_release_doi: + cipso_v4_doi_putdef(doi_def); cfg_cipsov4_add_map_failure_remove_doi: - cipso_v4_doi_remove(doi_def->doi, audit_info, netlbl_cipsov4_doi_free); -cfg_cipsov4_add_map_failure_unlock: - rcu_read_unlock(); + cipso_v4_doi_remove(doi, audit_info); cfg_cipsov4_add_map_failure: if (entry != NULL) kfree(entry->domain); kfree(entry); - return ret_val; -} - -/** - * netlbl_cfg_cipsov4_del - Removean existing CIPSOv4 DOI definition - * @doi: the CIPSO DOI value - * @audit_info: NetLabel audit information - * - * Description: - * Removes an existing CIPSOv4 DOI definition from the NetLabel subsystem. - * Returns zero on success, negative values on failure. - * - */ -int netlbl_cfg_cipsov4_del(u32 doi, struct netlbl_audit *audit_info) -{ - return cipso_v4_doi_remove(doi, audit_info, netlbl_cipsov4_doi_free); + goto cfg_cipsov4_add_map_return; } /* @@ -452,7 +422,9 @@ int netlbl_enabled(void) * Attach the correct label to the given socket using the security attributes * specified in @secattr. This function requires exclusive access to @sk, * which means it either needs to be in the process of being created or locked. - * Returns zero on success, negative values on failure. + * Returns zero on success, -EDESTADDRREQ if the domain is configured to use + * network address selectors (can't blindly label the socket), and negative + * values on all other failures. * */ int netlbl_sock_setattr(struct sock *sk, @@ -466,6 +438,9 @@ int netlbl_sock_setattr(struct sock *sk, if (dom_entry == NULL) goto socket_setattr_return; switch (dom_entry->type) { + case NETLBL_NLTYPE_ADDRSELECT: + ret_val = -EDESTADDRREQ; + break; case NETLBL_NLTYPE_CIPSOV4: ret_val = cipso_v4_sock_setattr(sk, dom_entry->type_def.cipsov4, @@ -484,6 +459,20 @@ socket_setattr_return: } /** + * netlbl_sock_delattr - Delete all the NetLabel labels on a socket + * @sk: the socket + * + * Description: + * Remove all the NetLabel labeling from @sk. The caller is responsible for + * ensuring that @sk is locked. + * + */ +void netlbl_sock_delattr(struct sock *sk) +{ + cipso_v4_sock_delattr(sk); +} + +/** * netlbl_sock_getattr - Determine the security attributes of a sock * @sk: the sock * @secattr: the security attributes @@ -501,6 +490,128 @@ int netlbl_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) } /** + * netlbl_conn_setattr - Label a connected socket using the correct protocol + * @sk: the socket to label + * @addr: the destination address + * @secattr: the security attributes + * + * Description: + * Attach the correct label to the given connected socket using the security + * attributes specified in @secattr. The caller is responsible for ensuring + * that @sk is locked. Returns zero on success, negative values on failure. + * + */ +int netlbl_conn_setattr(struct sock *sk, + struct sockaddr *addr, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + struct sockaddr_in *addr4; + struct netlbl_domaddr4_map *af4_entry; + + rcu_read_lock(); + switch (addr->sa_family) { + case AF_INET: + addr4 = (struct sockaddr_in *)addr; + af4_entry = netlbl_domhsh_getentry_af4(secattr->domain, + addr4->sin_addr.s_addr); + if (af4_entry == NULL) { + ret_val = -ENOENT; + goto conn_setattr_return; + } + switch (af4_entry->type) { + case NETLBL_NLTYPE_CIPSOV4: + ret_val = cipso_v4_sock_setattr(sk, + af4_entry->type_def.cipsov4, + secattr); + break; + case NETLBL_NLTYPE_UNLABELED: + /* just delete the protocols we support for right now + * but we could remove other protocols if needed */ + cipso_v4_sock_delattr(sk); + ret_val = 0; + break; + default: + ret_val = -ENOENT; + } + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + /* since we don't support any IPv6 labeling protocols right + * now we can optimize everything away until we do */ + ret_val = 0; + break; +#endif /* IPv6 */ + default: + ret_val = 0; + } + +conn_setattr_return: + rcu_read_unlock(); + return ret_val; +} + +/** + * netlbl_skbuff_setattr - Label a packet using the correct protocol + * @skb: the packet + * @family: protocol family + * @secattr: the security attributes + * + * Description: + * Attach the correct label to the given packet using the security attributes + * specified in @secattr. Returns zero on success, negative values on failure. + * + */ +int netlbl_skbuff_setattr(struct sk_buff *skb, + u16 family, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + struct iphdr *hdr4; + struct netlbl_domaddr4_map *af4_entry; + + rcu_read_lock(); + switch (family) { + case AF_INET: + hdr4 = ip_hdr(skb); + af4_entry = netlbl_domhsh_getentry_af4(secattr->domain, + hdr4->daddr); + if (af4_entry == NULL) { + ret_val = -ENOENT; + goto skbuff_setattr_return; + } + switch (af4_entry->type) { + case NETLBL_NLTYPE_CIPSOV4: + ret_val = cipso_v4_skbuff_setattr(skb, + af4_entry->type_def.cipsov4, + secattr); + break; + case NETLBL_NLTYPE_UNLABELED: + /* just delete the protocols we support for right now + * but we could remove other protocols if needed */ + ret_val = cipso_v4_skbuff_delattr(skb); + break; + default: + ret_val = -ENOENT; + } + break; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: + /* since we don't support any IPv6 labeling protocols right + * now we can optimize everything away until we do */ + ret_val = 0; + break; +#endif /* IPv6 */ + default: + ret_val = 0; + } + +skbuff_setattr_return: + rcu_read_unlock(); + return ret_val; +} + +/** * netlbl_skbuff_getattr - Determine the security attributes of a packet * @skb: the packet * @family: protocol family @@ -528,6 +639,7 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, * netlbl_skbuff_err - Handle a LSM error on a sk_buff * @skb: the packet * @error: the error code + * @gateway: true if host is acting as a gateway, false otherwise * * Description: * Deal with a LSM problem when handling the packet in @skb, typically this is @@ -535,10 +647,10 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, * according to the packet's labeling protocol. * */ -void netlbl_skbuff_err(struct sk_buff *skb, int error) +void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway) { if (CIPSO_V4_OPTEXIST(skb)) - cipso_v4_error(skb, error, 0); + cipso_v4_error(skb, error, gateway); } /** diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 44be5d5261f..ee769ecaa13 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -10,7 +10,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,9 +32,13 @@ #include <linux/socket.h> #include <linux/string.h> #include <linux/skbuff.h> +#include <linux/in.h> +#include <linux/in6.h> #include <net/sock.h> #include <net/netlink.h> #include <net/genetlink.h> +#include <net/ip.h> +#include <net/ipv6.h> #include <net/netlabel.h> #include <net/cipso_ipv4.h> #include <asm/atomic.h> @@ -71,86 +75,337 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { }; /* - * NetLabel Command Handlers + * Helper Functions */ /** * netlbl_mgmt_add - Handle an ADD message - * @skb: the NETLINK buffer * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information * * Description: - * Process a user generated ADD message and add the domains from the message - * to the hash table. See netlabel.h for a description of the message format. - * Returns zero on success, negative values on failure. + * Helper function for the ADD and ADDDEF messages to add the domain mappings + * from the message to the hash table. See netlabel.h for a description of the + * message format. Returns zero on success, negative values on failure. * */ -static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) +static int netlbl_mgmt_add_common(struct genl_info *info, + struct netlbl_audit *audit_info) { int ret_val = -EINVAL; struct netlbl_dom_map *entry = NULL; - size_t tmp_size; + struct netlbl_domaddr_map *addrmap = NULL; + struct cipso_v4_doi *cipsov4 = NULL; u32 tmp_val; - struct netlbl_audit audit_info; - - if (!info->attrs[NLBL_MGMT_A_DOMAIN] || - !info->attrs[NLBL_MGMT_A_PROTOCOL]) - goto add_failure; - - netlbl_netlink_auditinfo(skb, &audit_info); entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (entry == NULL) { ret_val = -ENOMEM; goto add_failure; } - tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]); - entry->domain = kmalloc(tmp_size, GFP_KERNEL); - if (entry->domain == NULL) { - ret_val = -ENOMEM; - goto add_failure; - } entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); - nla_strlcpy(entry->domain, info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size); + if (info->attrs[NLBL_MGMT_A_DOMAIN]) { + size_t tmp_size = nla_len(info->attrs[NLBL_MGMT_A_DOMAIN]); + entry->domain = kmalloc(tmp_size, GFP_KERNEL); + if (entry->domain == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + nla_strlcpy(entry->domain, + info->attrs[NLBL_MGMT_A_DOMAIN], tmp_size); + } + + /* NOTE: internally we allow/use a entry->type value of + * NETLBL_NLTYPE_ADDRSELECT but we don't currently allow users + * to pass that as a protocol value because we need to know the + * "real" protocol */ switch (entry->type) { case NETLBL_NLTYPE_UNLABELED: - ret_val = netlbl_domhsh_add(entry, &audit_info); break; case NETLBL_NLTYPE_CIPSOV4: if (!info->attrs[NLBL_MGMT_A_CV4DOI]) goto add_failure; tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); - /* We should be holding a rcu_read_lock() here while we hold - * the result but since the entry will always be deleted when - * the CIPSO DOI is deleted we aren't going to keep the - * lock. */ - rcu_read_lock(); - entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); - if (entry->type_def.cipsov4 == NULL) { - rcu_read_unlock(); + cipsov4 = cipso_v4_doi_getdef(tmp_val); + if (cipsov4 == NULL) goto add_failure; - } - ret_val = netlbl_domhsh_add(entry, &audit_info); - rcu_read_unlock(); + entry->type_def.cipsov4 = cipsov4; break; default: goto add_failure; } + + if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) { + struct in_addr *addr; + struct in_addr *mask; + struct netlbl_domaddr4_map *map; + + addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL); + if (addrmap == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + INIT_LIST_HEAD(&addrmap->list4); + INIT_LIST_HEAD(&addrmap->list6); + + if (nla_len(info->attrs[NLBL_MGMT_A_IPV4ADDR]) != + sizeof(struct in_addr)) { + ret_val = -EINVAL; + goto add_failure; + } + if (nla_len(info->attrs[NLBL_MGMT_A_IPV4MASK]) != + sizeof(struct in_addr)) { + ret_val = -EINVAL; + goto add_failure; + } + addr = nla_data(info->attrs[NLBL_MGMT_A_IPV4ADDR]); + mask = nla_data(info->attrs[NLBL_MGMT_A_IPV4MASK]); + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (map == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + map->list.addr = addr->s_addr & mask->s_addr; + map->list.mask = mask->s_addr; + map->list.valid = 1; + map->type = entry->type; + if (cipsov4) + map->type_def.cipsov4 = cipsov4; + + ret_val = netlbl_af4list_add(&map->list, &addrmap->list4); + if (ret_val != 0) { + kfree(map); + goto add_failure; + } + + entry->type = NETLBL_NLTYPE_ADDRSELECT; + entry->type_def.addrsel = addrmap; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + } else if (info->attrs[NLBL_MGMT_A_IPV6ADDR]) { + struct in6_addr *addr; + struct in6_addr *mask; + struct netlbl_domaddr6_map *map; + + addrmap = kzalloc(sizeof(*addrmap), GFP_KERNEL); + if (addrmap == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + INIT_LIST_HEAD(&addrmap->list4); + INIT_LIST_HEAD(&addrmap->list6); + + if (nla_len(info->attrs[NLBL_MGMT_A_IPV6ADDR]) != + sizeof(struct in6_addr)) { + ret_val = -EINVAL; + goto add_failure; + } + if (nla_len(info->attrs[NLBL_MGMT_A_IPV6MASK]) != + sizeof(struct in6_addr)) { + ret_val = -EINVAL; + goto add_failure; + } + addr = nla_data(info->attrs[NLBL_MGMT_A_IPV6ADDR]); + mask = nla_data(info->attrs[NLBL_MGMT_A_IPV6MASK]); + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (map == NULL) { + ret_val = -ENOMEM; + goto add_failure; + } + ipv6_addr_copy(&map->list.addr, addr); + map->list.addr.s6_addr32[0] &= mask->s6_addr32[0]; + map->list.addr.s6_addr32[1] &= mask->s6_addr32[1]; + map->list.addr.s6_addr32[2] &= mask->s6_addr32[2]; + map->list.addr.s6_addr32[3] &= mask->s6_addr32[3]; + ipv6_addr_copy(&map->list.mask, mask); + map->list.valid = 1; + map->type = entry->type; + + ret_val = netlbl_af6list_add(&map->list, &addrmap->list6); + if (ret_val != 0) { + kfree(map); + goto add_failure; + } + + entry->type = NETLBL_NLTYPE_ADDRSELECT; + entry->type_def.addrsel = addrmap; +#endif /* IPv6 */ + } + + ret_val = netlbl_domhsh_add(entry, audit_info); if (ret_val != 0) goto add_failure; return 0; add_failure: + if (cipsov4) + cipso_v4_doi_putdef(cipsov4); if (entry) kfree(entry->domain); + kfree(addrmap); kfree(entry); return ret_val; } /** + * netlbl_mgmt_listentry - List a NetLabel/LSM domain map entry + * @skb: the NETLINK buffer + * @entry: the map entry + * + * Description: + * This function is a helper function used by the LISTALL and LISTDEF command + * handlers. The caller is responsibile for ensuring that the RCU read lock + * is held. Returns zero on success, negative values on failure. + * + */ +static int netlbl_mgmt_listentry(struct sk_buff *skb, + struct netlbl_dom_map *entry) +{ + int ret_val; + struct nlattr *nla_a; + struct nlattr *nla_b; + struct netlbl_af4list *iter4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *iter6; +#endif + + if (entry->domain != NULL) { + ret_val = nla_put_string(skb, + NLBL_MGMT_A_DOMAIN, entry->domain); + if (ret_val != 0) + return ret_val; + } + + switch (entry->type) { + case NETLBL_NLTYPE_ADDRSELECT: + nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST); + if (nla_a == NULL) + return -ENOMEM; + + netlbl_af4list_foreach_rcu(iter4, + &entry->type_def.addrsel->list4) { + struct netlbl_domaddr4_map *map4; + struct in_addr addr_struct; + + nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR); + if (nla_b == NULL) + return -ENOMEM; + + addr_struct.s_addr = iter4->addr; + ret_val = nla_put(skb, NLBL_MGMT_A_IPV4ADDR, + sizeof(struct in_addr), + &addr_struct); + if (ret_val != 0) + return ret_val; + addr_struct.s_addr = iter4->mask; + ret_val = nla_put(skb, NLBL_MGMT_A_IPV4MASK, + sizeof(struct in_addr), + &addr_struct); + if (ret_val != 0) + return ret_val; + map4 = netlbl_domhsh_addr4_entry(iter4); + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, + map4->type); + if (ret_val != 0) + return ret_val; + switch (map4->type) { + case NETLBL_NLTYPE_CIPSOV4: + ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, + map4->type_def.cipsov4->doi); + if (ret_val != 0) + return ret_val; + break; + } + + nla_nest_end(skb, nla_b); + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(iter6, + &entry->type_def.addrsel->list6) { + struct netlbl_domaddr6_map *map6; + + nla_b = nla_nest_start(skb, NLBL_MGMT_A_ADDRSELECTOR); + if (nla_b == NULL) + return -ENOMEM; + + ret_val = nla_put(skb, NLBL_MGMT_A_IPV6ADDR, + sizeof(struct in6_addr), + &iter6->addr); + if (ret_val != 0) + return ret_val; + ret_val = nla_put(skb, NLBL_MGMT_A_IPV6MASK, + sizeof(struct in6_addr), + &iter6->mask); + if (ret_val != 0) + return ret_val; + map6 = netlbl_domhsh_addr6_entry(iter6); + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, + map6->type); + if (ret_val != 0) + return ret_val; + + nla_nest_end(skb, nla_b); + } +#endif /* IPv6 */ + + nla_nest_end(skb, nla_a); + break; + case NETLBL_NLTYPE_UNLABELED: + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type); + break; + case NETLBL_NLTYPE_CIPSOV4: + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, entry->type); + if (ret_val != 0) + return ret_val; + ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, + entry->type_def.cipsov4->doi); + break; + } + + return ret_val; +} + +/* + * NetLabel Command Handlers + */ + +/** + * netlbl_mgmt_add - Handle an ADD message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Process a user generated ADD message and add the domains from the message + * to the hash table. See netlabel.h for a description of the message format. + * Returns zero on success, negative values on failure. + * + */ +static int netlbl_mgmt_add(struct sk_buff *skb, struct genl_info *info) +{ + struct netlbl_audit audit_info; + + if ((!info->attrs[NLBL_MGMT_A_DOMAIN]) || + (!info->attrs[NLBL_MGMT_A_PROTOCOL]) || + (info->attrs[NLBL_MGMT_A_IPV4ADDR] && + info->attrs[NLBL_MGMT_A_IPV6ADDR]) || + (info->attrs[NLBL_MGMT_A_IPV4MASK] && + info->attrs[NLBL_MGMT_A_IPV6MASK]) || + ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^ + (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) || + ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^ + (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL))) + return -EINVAL; + + netlbl_netlink_auditinfo(skb, &audit_info); + + return netlbl_mgmt_add_common(info, &audit_info); +} + +/** * netlbl_mgmt_remove - Handle a REMOVE message * @skb: the NETLINK buffer * @info: the Generic NETLINK info block @@ -198,23 +453,9 @@ static int netlbl_mgmt_listall_cb(struct netlbl_dom_map *entry, void *arg) if (data == NULL) goto listall_cb_failure; - ret_val = nla_put_string(cb_arg->skb, - NLBL_MGMT_A_DOMAIN, - entry->domain); + ret_val = netlbl_mgmt_listentry(cb_arg->skb, entry); if (ret_val != 0) goto listall_cb_failure; - ret_val = nla_put_u32(cb_arg->skb, NLBL_MGMT_A_PROTOCOL, entry->type); - if (ret_val != 0) - goto listall_cb_failure; - switch (entry->type) { - case NETLBL_NLTYPE_CIPSOV4: - ret_val = nla_put_u32(cb_arg->skb, - NLBL_MGMT_A_CV4DOI, - entry->type_def.cipsov4->doi); - if (ret_val != 0) - goto listall_cb_failure; - break; - } cb_arg->seq++; return genlmsg_end(cb_arg->skb, data); @@ -268,56 +509,22 @@ static int netlbl_mgmt_listall(struct sk_buff *skb, */ static int netlbl_mgmt_adddef(struct sk_buff *skb, struct genl_info *info) { - int ret_val = -EINVAL; - struct netlbl_dom_map *entry = NULL; - u32 tmp_val; struct netlbl_audit audit_info; - if (!info->attrs[NLBL_MGMT_A_PROTOCOL]) - goto adddef_failure; + if ((!info->attrs[NLBL_MGMT_A_PROTOCOL]) || + (info->attrs[NLBL_MGMT_A_IPV4ADDR] && + info->attrs[NLBL_MGMT_A_IPV6ADDR]) || + (info->attrs[NLBL_MGMT_A_IPV4MASK] && + info->attrs[NLBL_MGMT_A_IPV6MASK]) || + ((info->attrs[NLBL_MGMT_A_IPV4ADDR] != NULL) ^ + (info->attrs[NLBL_MGMT_A_IPV4MASK] != NULL)) || + ((info->attrs[NLBL_MGMT_A_IPV6ADDR] != NULL) ^ + (info->attrs[NLBL_MGMT_A_IPV6MASK] != NULL))) + return -EINVAL; netlbl_netlink_auditinfo(skb, &audit_info); - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (entry == NULL) { - ret_val = -ENOMEM; - goto adddef_failure; - } - entry->type = nla_get_u32(info->attrs[NLBL_MGMT_A_PROTOCOL]); - - switch (entry->type) { - case NETLBL_NLTYPE_UNLABELED: - ret_val = netlbl_domhsh_add_default(entry, &audit_info); - break; - case NETLBL_NLTYPE_CIPSOV4: - if (!info->attrs[NLBL_MGMT_A_CV4DOI]) - goto adddef_failure; - - tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CV4DOI]); - /* We should be holding a rcu_read_lock() here while we hold - * the result but since the entry will always be deleted when - * the CIPSO DOI is deleted we aren't going to keep the - * lock. */ - rcu_read_lock(); - entry->type_def.cipsov4 = cipso_v4_doi_getdef(tmp_val); - if (entry->type_def.cipsov4 == NULL) { - rcu_read_unlock(); - goto adddef_failure; - } - ret_val = netlbl_domhsh_add_default(entry, &audit_info); - rcu_read_unlock(); - break; - default: - goto adddef_failure; - } - if (ret_val != 0) - goto adddef_failure; - - return 0; - -adddef_failure: - kfree(entry); - return ret_val; + return netlbl_mgmt_add_common(info, &audit_info); } /** @@ -371,19 +578,10 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) ret_val = -ENOENT; goto listdef_failure_lock; } - ret_val = nla_put_u32(ans_skb, NLBL_MGMT_A_PROTOCOL, entry->type); - if (ret_val != 0) - goto listdef_failure_lock; - switch (entry->type) { - case NETLBL_NLTYPE_CIPSOV4: - ret_val = nla_put_u32(ans_skb, - NLBL_MGMT_A_CV4DOI, - entry->type_def.cipsov4->doi); - if (ret_val != 0) - goto listdef_failure_lock; - break; - } + ret_val = netlbl_mgmt_listentry(ans_skb, entry); rcu_read_unlock(); + if (ret_val != 0) + goto listdef_failure; genlmsg_end(ans_skb, data); return genlmsg_reply(ans_skb, info); diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h index a43bff169d6..05d96431f81 100644 --- a/net/netlabel/netlabel_mgmt.h +++ b/net/netlabel/netlabel_mgmt.h @@ -45,6 +45,16 @@ * NLBL_MGMT_A_DOMAIN * NLBL_MGMT_A_PROTOCOL * + * If IPv4 is specified the following attributes are required: + * + * NLBL_MGMT_A_IPV4ADDR + * NLBL_MGMT_A_IPV4MASK + * + * If IPv6 is specified the following attributes are required: + * + * NLBL_MGMT_A_IPV6ADDR + * NLBL_MGMT_A_IPV6MASK + * * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: * * NLBL_MGMT_A_CV4DOI @@ -68,13 +78,24 @@ * Required attributes: * * NLBL_MGMT_A_DOMAIN + * + * If the IP address selectors are not used the following attribute is + * required: + * * NLBL_MGMT_A_PROTOCOL * - * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: + * If the IP address selectors are used then the following attritbute is + * required: + * + * NLBL_MGMT_A_SELECTORLIST + * + * If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following + * attributes are required: * * NLBL_MGMT_A_CV4DOI * - * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. + * If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other + * attributes are required. * * o ADDDEF: * Sent by an application to set the default domain mapping for the NetLabel @@ -100,15 +121,23 @@ * application there is no payload. On success the kernel should send a * response using the following format. * - * Required attributes: + * If the IP address selectors are not used the following attribute is + * required: * * NLBL_MGMT_A_PROTOCOL * - * If using NETLBL_NLTYPE_CIPSOV4 the following attributes are required: + * If the IP address selectors are used then the following attritbute is + * required: + * + * NLBL_MGMT_A_SELECTORLIST + * + * If the mapping is using the NETLBL_NLTYPE_CIPSOV4 type then the following + * attributes are required: * * NLBL_MGMT_A_CV4DOI * - * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. + * If the mapping is using the NETLBL_NLTYPE_UNLABELED type no other + * attributes are required. * * o PROTOCOLS: * Sent by an application to request a list of configured NetLabel protocols @@ -162,6 +191,26 @@ enum { NLBL_MGMT_A_CV4DOI, /* (NLA_U32) * the CIPSOv4 DOI value */ + NLBL_MGMT_A_IPV6ADDR, + /* (NLA_BINARY, struct in6_addr) + * an IPv6 address */ + NLBL_MGMT_A_IPV6MASK, + /* (NLA_BINARY, struct in6_addr) + * an IPv6 address mask */ + NLBL_MGMT_A_IPV4ADDR, + /* (NLA_BINARY, struct in_addr) + * an IPv4 address */ + NLBL_MGMT_A_IPV4MASK, + /* (NLA_BINARY, struct in_addr) + * and IPv4 address mask */ + NLBL_MGMT_A_ADDRSELECTOR, + /* (NLA_NESTED) + * an IP address selector, must contain an address, mask, and protocol + * attribute plus any protocol specific attributes */ + NLBL_MGMT_A_SELECTORLIST, + /* (NLA_NESTED) + * the selector list, there must be at least one + * NLBL_MGMT_A_ADDRSELECTOR attribute */ __NLBL_MGMT_A_MAX, }; #define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1) diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 921c118ead8..e8a5c32b0f1 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -10,7 +10,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2007 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 - 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,6 +54,7 @@ #include <asm/atomic.h> #include "netlabel_user.h" +#include "netlabel_addrlist.h" #include "netlabel_domainhash.h" #include "netlabel_unlabeled.h" #include "netlabel_mgmt.h" @@ -76,22 +77,20 @@ struct netlbl_unlhsh_tbl { struct list_head *tbl; u32 size; }; +#define netlbl_unlhsh_addr4_entry(iter) \ + container_of(iter, struct netlbl_unlhsh_addr4, list) struct netlbl_unlhsh_addr4 { - __be32 addr; - __be32 mask; u32 secid; - u32 valid; - struct list_head list; + struct netlbl_af4list list; struct rcu_head rcu; }; +#define netlbl_unlhsh_addr6_entry(iter) \ + container_of(iter, struct netlbl_unlhsh_addr6, list) struct netlbl_unlhsh_addr6 { - struct in6_addr addr; - struct in6_addr mask; u32 secid; - u32 valid; - struct list_head list; + struct netlbl_af6list list; struct rcu_head rcu; }; struct netlbl_unlhsh_iface { @@ -147,76 +146,6 @@ static const struct nla_policy netlbl_unlabel_genl_policy[NLBL_UNLABEL_A_MAX + 1 }; /* - * Audit Helper Functions - */ - -/** - * netlbl_unlabel_audit_addr4 - Audit an IPv4 address - * @audit_buf: audit buffer - * @dev: network interface - * @addr: IP address - * @mask: IP address mask - * - * Description: - * Write the IPv4 address and address mask, if necessary, to @audit_buf. - * - */ -static void netlbl_unlabel_audit_addr4(struct audit_buffer *audit_buf, - const char *dev, - __be32 addr, __be32 mask) -{ - u32 mask_val = ntohl(mask); - - if (dev != NULL) - audit_log_format(audit_buf, " netif=%s", dev); - audit_log_format(audit_buf, " src=" NIPQUAD_FMT, NIPQUAD(addr)); - if (mask_val != 0xffffffff) { - u32 mask_len = 0; - while (mask_val > 0) { - mask_val <<= 1; - mask_len++; - } - audit_log_format(audit_buf, " src_prefixlen=%d", mask_len); - } -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -/** - * netlbl_unlabel_audit_addr6 - Audit an IPv6 address - * @audit_buf: audit buffer - * @dev: network interface - * @addr: IP address - * @mask: IP address mask - * - * Description: - * Write the IPv6 address and address mask, if necessary, to @audit_buf. - * - */ -static void netlbl_unlabel_audit_addr6(struct audit_buffer *audit_buf, - const char *dev, - const struct in6_addr *addr, - const struct in6_addr *mask) -{ - if (dev != NULL) - audit_log_format(audit_buf, " netif=%s", dev); - audit_log_format(audit_buf, " src=" NIP6_FMT, NIP6(*addr)); - if (ntohl(mask->s6_addr32[3]) != 0xffffffff) { - u32 mask_len = 0; - u32 mask_val; - int iter = -1; - while (ntohl(mask->s6_addr32[++iter]) == 0xffffffff) - mask_len += 32; - mask_val = ntohl(mask->s6_addr32[iter]); - while (mask_val > 0) { - mask_val <<= 1; - mask_len++; - } - audit_log_format(audit_buf, " src_prefixlen=%d", mask_len); - } -} -#endif /* IPv6 */ - -/* * Unlabeled Connection Hash Table Functions */ @@ -274,26 +203,28 @@ static void netlbl_unlhsh_free_addr6(struct rcu_head *entry) static void netlbl_unlhsh_free_iface(struct rcu_head *entry) { struct netlbl_unlhsh_iface *iface; - struct netlbl_unlhsh_addr4 *iter4; - struct netlbl_unlhsh_addr4 *tmp4; - struct netlbl_unlhsh_addr6 *iter6; - struct netlbl_unlhsh_addr6 *tmp6; + struct netlbl_af4list *iter4; + struct netlbl_af4list *tmp4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *iter6; + struct netlbl_af6list *tmp6; +#endif /* IPv6 */ iface = container_of(entry, struct netlbl_unlhsh_iface, rcu); /* no need for locks here since we are the only one with access to this * structure */ - list_for_each_entry_safe(iter4, tmp4, &iface->addr4_list, list) - if (iter4->valid) { - list_del_rcu(&iter4->list); - kfree(iter4); - } - list_for_each_entry_safe(iter6, tmp6, &iface->addr6_list, list) - if (iter6->valid) { - list_del_rcu(&iter6->list); - kfree(iter6); - } + netlbl_af4list_foreach_safe(iter4, tmp4, &iface->addr4_list) { + netlbl_af4list_remove_entry(iter4); + kfree(netlbl_unlhsh_addr4_entry(iter4)); + } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_safe(iter6, tmp6, &iface->addr6_list) { + netlbl_af6list_remove_entry(iter6); + kfree(netlbl_unlhsh_addr6_entry(iter6)); + } +#endif /* IPv6 */ kfree(iface); } @@ -316,59 +247,6 @@ static u32 netlbl_unlhsh_hash(int ifindex) } /** - * netlbl_unlhsh_search_addr4 - Search for a matching IPv4 address entry - * @addr: IPv4 address - * @iface: the network interface entry - * - * Description: - * Searches the IPv4 address list of the network interface specified by @iface. - * If a matching address entry is found it is returned, otherwise NULL is - * returned. The caller is responsible for calling the rcu_read_[un]lock() - * functions. - * - */ -static struct netlbl_unlhsh_addr4 *netlbl_unlhsh_search_addr4( - __be32 addr, - const struct netlbl_unlhsh_iface *iface) -{ - struct netlbl_unlhsh_addr4 *iter; - - list_for_each_entry_rcu(iter, &iface->addr4_list, list) - if (iter->valid && (addr & iter->mask) == iter->addr) - return iter; - - return NULL; -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -/** - * netlbl_unlhsh_search_addr6 - Search for a matching IPv6 address entry - * @addr: IPv6 address - * @iface: the network interface entry - * - * Description: - * Searches the IPv6 address list of the network interface specified by @iface. - * If a matching address entry is found it is returned, otherwise NULL is - * returned. The caller is responsible for calling the rcu_read_[un]lock() - * functions. - * - */ -static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6( - const struct in6_addr *addr, - const struct netlbl_unlhsh_iface *iface) -{ - struct netlbl_unlhsh_addr6 *iter; - - list_for_each_entry_rcu(iter, &iface->addr6_list, list) - if (iter->valid && - ipv6_masked_addr_cmp(&iter->addr, &iter->mask, addr) == 0) - return iter; - - return NULL; -} -#endif /* IPv6 */ - -/** * netlbl_unlhsh_search_iface - Search for a matching interface entry * @ifindex: the network interface * @@ -381,12 +259,12 @@ static struct netlbl_unlhsh_addr6 *netlbl_unlhsh_search_addr6( static struct netlbl_unlhsh_iface *netlbl_unlhsh_search_iface(int ifindex) { u32 bkt; + struct list_head *bkt_list; struct netlbl_unlhsh_iface *iter; bkt = netlbl_unlhsh_hash(ifindex); - list_for_each_entry_rcu(iter, - &rcu_dereference(netlbl_unlhsh)->tbl[bkt], - list) + bkt_list = &rcu_dereference(netlbl_unlhsh)->tbl[bkt]; + list_for_each_entry_rcu(iter, bkt_list, list) if (iter->valid && iter->ifindex == ifindex) return iter; @@ -439,43 +317,26 @@ static int netlbl_unlhsh_add_addr4(struct netlbl_unlhsh_iface *iface, const struct in_addr *mask, u32 secid) { + int ret_val; struct netlbl_unlhsh_addr4 *entry; - struct netlbl_unlhsh_addr4 *iter; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) return -ENOMEM; - entry->addr = addr->s_addr & mask->s_addr; - entry->mask = mask->s_addr; - entry->secid = secid; - entry->valid = 1; + entry->list.addr = addr->s_addr & mask->s_addr; + entry->list.mask = mask->s_addr; + entry->list.valid = 1; INIT_RCU_HEAD(&entry->rcu); + entry->secid = secid; spin_lock(&netlbl_unlhsh_lock); - iter = netlbl_unlhsh_search_addr4(entry->addr, iface); - if (iter != NULL && - iter->addr == addr->s_addr && iter->mask == mask->s_addr) { - spin_unlock(&netlbl_unlhsh_lock); - kfree(entry); - return -EEXIST; - } - /* in order to speed up address searches through the list (the common - * case) we need to keep the list in order based on the size of the - * address mask such that the entry with the widest mask (smallest - * numerical value) appears first in the list */ - list_for_each_entry_rcu(iter, &iface->addr4_list, list) - if (iter->valid && - ntohl(entry->mask) > ntohl(iter->mask)) { - __list_add_rcu(&entry->list, - iter->list.prev, - &iter->list); - spin_unlock(&netlbl_unlhsh_lock); - return 0; - } - list_add_tail_rcu(&entry->list, &iface->addr4_list); + ret_val = netlbl_af4list_add(&entry->list, &iface->addr4_list); spin_unlock(&netlbl_unlhsh_lock); - return 0; + + if (ret_val != 0) + kfree(entry); + return ret_val; } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -498,47 +359,29 @@ static int netlbl_unlhsh_add_addr6(struct netlbl_unlhsh_iface *iface, const struct in6_addr *mask, u32 secid) { + int ret_val; struct netlbl_unlhsh_addr6 *entry; - struct netlbl_unlhsh_addr6 *iter; entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) return -ENOMEM; - ipv6_addr_copy(&entry->addr, addr); - entry->addr.s6_addr32[0] &= mask->s6_addr32[0]; - entry->addr.s6_addr32[1] &= mask->s6_addr32[1]; - entry->addr.s6_addr32[2] &= mask->s6_addr32[2]; - entry->addr.s6_addr32[3] &= mask->s6_addr32[3]; - ipv6_addr_copy(&entry->mask, mask); - entry->secid = secid; - entry->valid = 1; + ipv6_addr_copy(&entry->list.addr, addr); + entry->list.addr.s6_addr32[0] &= mask->s6_addr32[0]; + entry->list.addr.s6_addr32[1] &= mask->s6_addr32[1]; + entry->list.addr.s6_addr32[2] &= mask->s6_addr32[2]; + entry->list.addr.s6_addr32[3] &= mask->s6_addr32[3]; + ipv6_addr_copy(&entry->list.mask, mask); + entry->list.valid = 1; INIT_RCU_HEAD(&entry->rcu); + entry->secid = secid; spin_lock(&netlbl_unlhsh_lock); - iter = netlbl_unlhsh_search_addr6(&entry->addr, iface); - if (iter != NULL && - (ipv6_addr_equal(&iter->addr, addr) && - ipv6_addr_equal(&iter->mask, mask))) { - spin_unlock(&netlbl_unlhsh_lock); - kfree(entry); - return -EEXIST; - } - /* in order to speed up address searches through the list (the common - * case) we need to keep the list in order based on the size of the - * address mask such that the entry with the widest mask (smallest - * numerical value) appears first in the list */ - list_for_each_entry_rcu(iter, &iface->addr6_list, list) - if (iter->valid && - ipv6_addr_cmp(&entry->mask, &iter->mask) > 0) { - __list_add_rcu(&entry->list, - iter->list.prev, - &iter->list); - spin_unlock(&netlbl_unlhsh_lock); - return 0; - } - list_add_tail_rcu(&entry->list, &iface->addr6_list); + ret_val = netlbl_af6list_add(&entry->list, &iface->addr6_list); spin_unlock(&netlbl_unlhsh_lock); + + if (ret_val != 0) + kfree(entry); return 0; } #endif /* IPv6 */ @@ -658,10 +501,10 @@ static int netlbl_unlhsh_add(struct net *net, mask4 = (struct in_addr *)mask; ret_val = netlbl_unlhsh_add_addr4(iface, addr4, mask4, secid); if (audit_buf != NULL) - netlbl_unlabel_audit_addr4(audit_buf, - dev_name, - addr4->s_addr, - mask4->s_addr); + netlbl_af4list_audit_addr(audit_buf, 1, + dev_name, + addr4->s_addr, + mask4->s_addr); break; } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -672,9 +515,9 @@ static int netlbl_unlhsh_add(struct net *net, mask6 = (struct in6_addr *)mask; ret_val = netlbl_unlhsh_add_addr6(iface, addr6, mask6, secid); if (audit_buf != NULL) - netlbl_unlabel_audit_addr6(audit_buf, - dev_name, - addr6, mask6); + netlbl_af6list_audit_addr(audit_buf, 1, + dev_name, + addr6, mask6); break; } #endif /* IPv6 */ @@ -719,35 +562,34 @@ static int netlbl_unlhsh_remove_addr4(struct net *net, const struct in_addr *mask, struct netlbl_audit *audit_info) { - int ret_val = -ENOENT; + int ret_val = 0; + struct netlbl_af4list *list_entry; struct netlbl_unlhsh_addr4 *entry; - struct audit_buffer *audit_buf = NULL; + struct audit_buffer *audit_buf; struct net_device *dev; - char *secctx = NULL; + char *secctx; u32 secctx_len; spin_lock(&netlbl_unlhsh_lock); - entry = netlbl_unlhsh_search_addr4(addr->s_addr, iface); - if (entry != NULL && - entry->addr == addr->s_addr && entry->mask == mask->s_addr) { - entry->valid = 0; - list_del_rcu(&entry->list); - ret_val = 0; - } + list_entry = netlbl_af4list_remove(addr->s_addr, mask->s_addr, + &iface->addr4_list); spin_unlock(&netlbl_unlhsh_lock); + if (list_entry == NULL) + ret_val = -ENOENT; + entry = netlbl_unlhsh_addr4_entry(list_entry); audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL, audit_info); if (audit_buf != NULL) { dev = dev_get_by_index(net, iface->ifindex); - netlbl_unlabel_audit_addr4(audit_buf, - (dev != NULL ? dev->name : NULL), - entry->addr, entry->mask); + netlbl_af4list_audit_addr(audit_buf, 1, + (dev != NULL ? dev->name : NULL), + addr->s_addr, mask->s_addr); if (dev != NULL) dev_put(dev); - if (security_secid_to_secctx(entry->secid, - &secctx, - &secctx_len) == 0) { + if (entry && security_secid_to_secctx(entry->secid, + &secctx, + &secctx_len) == 0) { audit_log_format(audit_buf, " sec_obj=%s", secctx); security_release_secctx(secctx, secctx_len); } @@ -781,36 +623,33 @@ static int netlbl_unlhsh_remove_addr6(struct net *net, const struct in6_addr *mask, struct netlbl_audit *audit_info) { - int ret_val = -ENOENT; + int ret_val = 0; + struct netlbl_af6list *list_entry; struct netlbl_unlhsh_addr6 *entry; - struct audit_buffer *audit_buf = NULL; + struct audit_buffer *audit_buf; struct net_device *dev; - char *secctx = NULL; + char *secctx; u32 secctx_len; spin_lock(&netlbl_unlhsh_lock); - entry = netlbl_unlhsh_search_addr6(addr, iface); - if (entry != NULL && - (ipv6_addr_equal(&entry->addr, addr) && - ipv6_addr_equal(&entry->mask, mask))) { - entry->valid = 0; - list_del_rcu(&entry->list); - ret_val = 0; - } + list_entry = netlbl_af6list_remove(addr, mask, &iface->addr6_list); spin_unlock(&netlbl_unlhsh_lock); + if (list_entry == NULL) + ret_val = -ENOENT; + entry = netlbl_unlhsh_addr6_entry(list_entry); audit_buf = netlbl_audit_start_common(AUDIT_MAC_UNLBL_STCDEL, audit_info); if (audit_buf != NULL) { dev = dev_get_by_index(net, iface->ifindex); - netlbl_unlabel_audit_addr6(audit_buf, - (dev != NULL ? dev->name : NULL), - addr, mask); + netlbl_af6list_audit_addr(audit_buf, 1, + (dev != NULL ? dev->name : NULL), + addr, mask); if (dev != NULL) dev_put(dev); - if (security_secid_to_secctx(entry->secid, - &secctx, - &secctx_len) == 0) { + if (entry && security_secid_to_secctx(entry->secid, + &secctx, + &secctx_len) == 0) { audit_log_format(audit_buf, " sec_obj=%s", secctx); security_release_secctx(secctx, secctx_len); } @@ -836,16 +675,18 @@ static int netlbl_unlhsh_remove_addr6(struct net *net, */ static void netlbl_unlhsh_condremove_iface(struct netlbl_unlhsh_iface *iface) { - struct netlbl_unlhsh_addr4 *iter4; - struct netlbl_unlhsh_addr6 *iter6; + struct netlbl_af4list *iter4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *iter6; +#endif /* IPv6 */ spin_lock(&netlbl_unlhsh_lock); - list_for_each_entry_rcu(iter4, &iface->addr4_list, list) - if (iter4->valid) - goto unlhsh_condremove_failure; - list_for_each_entry_rcu(iter6, &iface->addr6_list, list) - if (iter6->valid) - goto unlhsh_condremove_failure; + netlbl_af4list_foreach_rcu(iter4, &iface->addr4_list) + goto unlhsh_condremove_failure; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(iter6, &iface->addr6_list) + goto unlhsh_condremove_failure; +#endif /* IPv6 */ iface->valid = 0; if (iface->ifindex > 0) list_del_rcu(&iface->list); @@ -1349,7 +1190,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd, if (addr4) { struct in_addr addr_struct; - addr_struct.s_addr = addr4->addr; + addr_struct.s_addr = addr4->list.addr; ret_val = nla_put(cb_arg->skb, NLBL_UNLABEL_A_IPV4ADDR, sizeof(struct in_addr), @@ -1357,7 +1198,7 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd, if (ret_val != 0) goto list_cb_failure; - addr_struct.s_addr = addr4->mask; + addr_struct.s_addr = addr4->list.mask; ret_val = nla_put(cb_arg->skb, NLBL_UNLABEL_A_IPV4MASK, sizeof(struct in_addr), @@ -1370,14 +1211,14 @@ static int netlbl_unlabel_staticlist_gen(u32 cmd, ret_val = nla_put(cb_arg->skb, NLBL_UNLABEL_A_IPV6ADDR, sizeof(struct in6_addr), - &addr6->addr); + &addr6->list.addr); if (ret_val != 0) goto list_cb_failure; ret_val = nla_put(cb_arg->skb, NLBL_UNLABEL_A_IPV6MASK, sizeof(struct in6_addr), - &addr6->mask); + &addr6->list.mask); if (ret_val != 0) goto list_cb_failure; @@ -1425,8 +1266,11 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb, u32 iter_bkt; u32 iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0; struct netlbl_unlhsh_iface *iface; - struct netlbl_unlhsh_addr4 *addr4; - struct netlbl_unlhsh_addr6 *addr6; + struct list_head *iter_list; + struct netlbl_af4list *addr4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + struct netlbl_af6list *addr6; +#endif cb_arg.nl_cb = cb; cb_arg.skb = skb; @@ -1436,44 +1280,43 @@ static int netlbl_unlabel_staticlist(struct sk_buff *skb, for (iter_bkt = skip_bkt; iter_bkt < rcu_dereference(netlbl_unlhsh)->size; iter_bkt++, iter_chain = 0, iter_addr4 = 0, iter_addr6 = 0) { - list_for_each_entry_rcu(iface, - &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt], - list) { + iter_list = &rcu_dereference(netlbl_unlhsh)->tbl[iter_bkt]; + list_for_each_entry_rcu(iface, iter_list, list) { if (!iface->valid || iter_chain++ < skip_chain) continue; - list_for_each_entry_rcu(addr4, - &iface->addr4_list, - list) { - if (!addr4->valid || iter_addr4++ < skip_addr4) + netlbl_af4list_foreach_rcu(addr4, + &iface->addr4_list) { + if (iter_addr4++ < skip_addr4) continue; if (netlbl_unlabel_staticlist_gen( - NLBL_UNLABEL_C_STATICLIST, - iface, - addr4, - NULL, - &cb_arg) < 0) { + NLBL_UNLABEL_C_STATICLIST, + iface, + netlbl_unlhsh_addr4_entry(addr4), + NULL, + &cb_arg) < 0) { iter_addr4--; iter_chain--; goto unlabel_staticlist_return; } } - list_for_each_entry_rcu(addr6, - &iface->addr6_list, - list) { - if (!addr6->valid || iter_addr6++ < skip_addr6) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(addr6, + &iface->addr6_list) { + if (iter_addr6++ < skip_addr6) continue; if (netlbl_unlabel_staticlist_gen( - NLBL_UNLABEL_C_STATICLIST, - iface, - NULL, - addr6, - &cb_arg) < 0) { + NLBL_UNLABEL_C_STATICLIST, + iface, + NULL, + netlbl_unlhsh_addr6_entry(addr6), + &cb_arg) < 0) { iter_addr6--; iter_chain--; goto unlabel_staticlist_return; } } +#endif /* IPv6 */ } } @@ -1504,9 +1347,12 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb, struct netlbl_unlhsh_iface *iface; u32 skip_addr4 = cb->args[0]; u32 skip_addr6 = cb->args[1]; - u32 iter_addr4 = 0, iter_addr6 = 0; - struct netlbl_unlhsh_addr4 *addr4; - struct netlbl_unlhsh_addr6 *addr6; + u32 iter_addr4 = 0; + struct netlbl_af4list *addr4; +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + u32 iter_addr6 = 0; + struct netlbl_af6list *addr6; +#endif cb_arg.nl_cb = cb; cb_arg.skb = skb; @@ -1517,30 +1363,32 @@ static int netlbl_unlabel_staticlistdef(struct sk_buff *skb, if (iface == NULL || !iface->valid) goto unlabel_staticlistdef_return; - list_for_each_entry_rcu(addr4, &iface->addr4_list, list) { - if (!addr4->valid || iter_addr4++ < skip_addr4) + netlbl_af4list_foreach_rcu(addr4, &iface->addr4_list) { + if (iter_addr4++ < skip_addr4) continue; if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF, - iface, - addr4, - NULL, - &cb_arg) < 0) { + iface, + netlbl_unlhsh_addr4_entry(addr4), + NULL, + &cb_arg) < 0) { iter_addr4--; goto unlabel_staticlistdef_return; } } - list_for_each_entry_rcu(addr6, &iface->addr6_list, list) { - if (!addr6->valid || iter_addr6++ < skip_addr6) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + netlbl_af6list_foreach_rcu(addr6, &iface->addr6_list) { + if (iter_addr6++ < skip_addr6) continue; if (netlbl_unlabel_staticlist_gen(NLBL_UNLABEL_C_STATICLISTDEF, - iface, - NULL, - addr6, - &cb_arg) < 0) { + iface, + NULL, + netlbl_unlhsh_addr6_entry(addr6), + &cb_arg) < 0) { iter_addr6--; goto unlabel_staticlistdef_return; } } +#endif /* IPv6 */ unlabel_staticlistdef_return: rcu_read_unlock(); @@ -1718,25 +1566,27 @@ int netlbl_unlabel_getattr(const struct sk_buff *skb, switch (family) { case PF_INET: { struct iphdr *hdr4; - struct netlbl_unlhsh_addr4 *addr4; + struct netlbl_af4list *addr4; hdr4 = ip_hdr(skb); - addr4 = netlbl_unlhsh_search_addr4(hdr4->saddr, iface); + addr4 = netlbl_af4list_search(hdr4->saddr, + &iface->addr4_list); if (addr4 == NULL) goto unlabel_getattr_nolabel; - secattr->attr.secid = addr4->secid; + secattr->attr.secid = netlbl_unlhsh_addr4_entry(addr4)->secid; break; } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case PF_INET6: { struct ipv6hdr *hdr6; - struct netlbl_unlhsh_addr6 *addr6; + struct netlbl_af6list *addr6; hdr6 = ipv6_hdr(skb); - addr6 = netlbl_unlhsh_search_addr6(&hdr6->saddr, iface); + addr6 = netlbl_af6list_search(&hdr6->saddr, + &iface->addr6_list); if (addr6 == NULL) goto unlabel_getattr_nolabel; - secattr->attr.secid = addr6->secid; + secattr->attr.secid = netlbl_unlhsh_addr6_entry(addr6)->secid; break; } #endif /* IPv6 */ diff --git a/net/wireless/core.c b/net/wireless/core.c index 5cadbeb76a1..24fdd4cd22c 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -13,7 +13,6 @@ #include <linux/debugfs.h> #include <linux/notifier.h> #include <linux/device.h> -#include <linux/list.h> #include <net/genetlink.h> #include <net/cfg80211.h> #include <net/wireless.h> diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index d64e6badc94..982dcae7bbe 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -105,12 +105,12 @@ as-instr = $(call try-run,\ # Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586) cc-option = $(call try-run,\ - $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",$(1),$(2)) + $(CC) $(KBUILD_CFLAGS) $(1) -c -xc /dev/null -o "$$TMP",$(1),$(2)) # cc-option-yn # Usage: flag := $(call cc-option-yn,-march=winchip-c6) cc-option-yn = $(call try-run,\ - $(CC) $(KBUILD_CFLAGS) $(1) -S -xc /dev/null -o "$$TMP",y,n) + $(CC) $(KBUILD_CFLAGS) $(1) -c -xc /dev/null -o "$$TMP",y,n) # cc-option-align # Prefix align with either -falign or -malign diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 4c9890ec252..473f94e56ea 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -629,6 +629,59 @@ static int do_i2c_entry(const char *filename, struct i2c_device_id *id, return 1; } +static const struct dmifield { + const char *prefix; + int field; +} dmi_fields[] = { + { "bvn", DMI_BIOS_VENDOR }, + { "bvr", DMI_BIOS_VERSION }, + { "bd", DMI_BIOS_DATE }, + { "svn", DMI_SYS_VENDOR }, + { "pn", DMI_PRODUCT_NAME }, + { "pvr", DMI_PRODUCT_VERSION }, + { "rvn", DMI_BOARD_VENDOR }, + { "rn", DMI_BOARD_NAME }, + { "rvr", DMI_BOARD_VERSION }, + { "cvn", DMI_CHASSIS_VENDOR }, + { "ct", DMI_CHASSIS_TYPE }, + { "cvr", DMI_CHASSIS_VERSION }, + { NULL, DMI_NONE } +}; + +static void dmi_ascii_filter(char *d, const char *s) +{ + /* Filter out characters we don't want to see in the modalias string */ + for (; *s; s++) + if (*s > ' ' && *s < 127 && *s != ':') + *(d++) = *s; + + *d = 0; +} + + +static int do_dmi_entry(const char *filename, struct dmi_system_id *id, + char *alias) +{ + int i, j; + + sprintf(alias, "dmi*"); + + for (i = 0; i < ARRAY_SIZE(dmi_fields); i++) { + for (j = 0; j < 4; j++) { + if (id->matches[j].slot && + id->matches[j].slot == dmi_fields[i].field) { + sprintf(alias + strlen(alias), ":%s*", + dmi_fields[i].prefix); + dmi_ascii_filter(alias + strlen(alias), + id->matches[j].substr); + strcat(alias, "*"); + } + } + } + + strcat(alias, ":"); + return 1; +} /* Ignore any prefix, eg. some architectures prepend _ */ static inline int sym_is(const char *symbol, const char *name) { @@ -760,6 +813,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info, do_table(symval, sym->st_size, sizeof(struct i2c_device_id), "i2c", do_i2c_entry, mod); + else if (sym_is(symname, "__mod_dmi_device_table")) + do_table(symval, sym->st_size, + sizeof(struct dmi_system_id), "dmi", + do_dmi_entry, mod); free(zeros); } diff --git a/security/inode.c b/security/inode.c index ca4958ebad8..efea5a60546 100644 --- a/security/inode.c +++ b/security/inode.c @@ -20,8 +20,7 @@ #include <linux/init.h> #include <linux/namei.h> #include <linux/security.h> - -#define SECURITYFS_MAGIC 0x73636673 +#include <linux/magic.h> static struct vfsmount *mount; static int mount_count; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4a7374c12d9..576e5119907 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -291,6 +291,7 @@ static void sk_free_security(struct sock *sk) struct sk_security_struct *ssec = sk->sk_security; sk->sk_security = NULL; + selinux_netlbl_sk_security_free(ssec); kfree(ssec); } @@ -324,7 +325,7 @@ enum { Opt_rootcontext = 4, }; -static match_table_t tokens = { +static const match_table_t tokens = { {Opt_context, CONTEXT_STR "%s"}, {Opt_fscontext, FSCONTEXT_STR "%s"}, {Opt_defcontext, DEFCONTEXT_STR "%s"}, @@ -2121,7 +2122,6 @@ static inline void flush_unauthorized_files(struct files_struct *files) long j = -1; int drop_tty = 0; - mutex_lock(&tty_mutex); tty = get_current_tty(); if (tty) { file_list_lock(); @@ -2139,8 +2139,8 @@ static inline void flush_unauthorized_files(struct files_struct *files) } } file_list_unlock(); + tty_kref_put(tty); } - mutex_unlock(&tty_mutex); /* Reset controlling tty. */ if (drop_tty) no_tty(); @@ -3801,6 +3801,7 @@ out: static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { + struct sock *sk = sock->sk; struct inode_security_struct *isec; int err; @@ -3814,7 +3815,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, isec = SOCK_INODE(sock)->i_security; if (isec->sclass == SECCLASS_TCP_SOCKET || isec->sclass == SECCLASS_DCCP_SOCKET) { - struct sock *sk = sock->sk; struct avc_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; @@ -3848,6 +3848,8 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, goto out; } + err = selinux_netlbl_socket_connect(sk, address); + out: return err; } @@ -4077,20 +4079,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, } static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, - struct avc_audit_data *ad, - u16 family, char *addrp) + u16 family) { int err; struct sk_security_struct *sksec = sk->sk_security; u32 peer_sid; u32 sk_sid = sksec->sid; + struct avc_audit_data ad; + char *addrp; + + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = skb->iif; + ad.u.net.family = family; + err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); + if (err) + return err; if (selinux_compat_net) - err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad, + err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad, family, addrp); else err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, - PACKET__RECV, ad); + PACKET__RECV, &ad); if (err) return err; @@ -4099,12 +4109,14 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, if (err) return err; err = avc_has_perm(sk_sid, peer_sid, - SECCLASS_PEER, PEER__RECV, ad); + SECCLASS_PEER, PEER__RECV, &ad); + if (err) + selinux_netlbl_err(skb, err, 0); } else { - err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad); + err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); if (err) return err; - err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad); + err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad); } return err; @@ -4118,6 +4130,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) u32 sk_sid = sksec->sid; struct avc_audit_data ad; char *addrp; + u8 secmark_active; + u8 peerlbl_active; if (family != PF_INET && family != PF_INET6) return 0; @@ -4126,6 +4140,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) family = PF_INET; + /* If any sort of compatibility mode is enabled then handoff processing + * to the selinux_sock_rcv_skb_compat() function to deal with the + * special handling. We do this in an attempt to keep this function + * as fast and as clean as possible. */ + if (selinux_compat_net || !selinux_policycap_netpeer) + return selinux_sock_rcv_skb_compat(sk, skb, family); + + secmark_active = selinux_secmark_enabled(); + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); + if (!secmark_active && !peerlbl_active) + return 0; + AVC_AUDIT_DATA_INIT(&ad, NET); ad.u.net.netif = skb->iif; ad.u.net.family = family; @@ -4133,15 +4159,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (err) return err; - /* If any sort of compatibility mode is enabled then handoff processing - * to the selinux_sock_rcv_skb_compat() function to deal with the - * special handling. We do this in an attempt to keep this function - * as fast and as clean as possible. */ - if (selinux_compat_net || !selinux_policycap_netpeer) - return selinux_sock_rcv_skb_compat(sk, skb, &ad, - family, addrp); - - if (netlbl_enabled() || selinux_xfrm_enabled()) { + if (peerlbl_active) { u32 peer_sid; err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); @@ -4149,13 +4167,17 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return err; err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family, peer_sid, &ad); - if (err) + if (err) { + selinux_netlbl_err(skb, err, 0); return err; + } err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); + if (err) + selinux_netlbl_err(skb, err, 0); } - if (selinux_secmark_enabled()) { + if (secmark_active) { err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, PACKET__RECV, &ad); if (err) @@ -4214,10 +4236,12 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff * u32 peer_secid = SECSID_NULL; u16 family; - if (sock) + if (skb && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + else if (skb && skb->protocol == htons(ETH_P_IPV6)) + family = PF_INET6; + else if (sock) family = sock->sk->sk_family; - else if (skb && skb->sk) - family = skb->sk->sk_family; else goto out; @@ -4275,8 +4299,6 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) sk->sk_family == PF_UNIX) isec->sid = sksec->sid; sksec->sclass = isec->sclass; - - selinux_netlbl_sock_graft(sk, parent); } static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, @@ -4284,10 +4306,15 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, { struct sk_security_struct *sksec = sk->sk_security; int err; + u16 family = sk->sk_family; u32 newsid; u32 peersid; - err = selinux_skb_peerlbl_sid(skb, sk->sk_family, &peersid); + /* handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + + err = selinux_skb_peerlbl_sid(skb, family, &peersid); if (err) return err; if (peersid == SECSID_NULL) { @@ -4322,12 +4349,18 @@ static void selinux_inet_csk_clone(struct sock *newsk, selinux_netlbl_sk_security_reset(newsksec, req->rsk_ops->family); } -static void selinux_inet_conn_established(struct sock *sk, - struct sk_buff *skb) +static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb) { + u16 family = sk->sk_family; struct sk_security_struct *sksec = sk->sk_security; - selinux_skb_peerlbl_sid(skb, sk->sk_family, &sksec->peer_sid); + /* handle mapped IPv4 packets arriving via IPv6 sockets */ + if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) + family = PF_INET; + + selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid); + + selinux_netlbl_inet_conn_established(sk, family); } static void selinux_req_classify_flow(const struct request_sock *req, @@ -4377,39 +4410,54 @@ out: static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, u16 family) { + int err; char *addrp; u32 peer_sid; struct avc_audit_data ad; u8 secmark_active; + u8 netlbl_active; u8 peerlbl_active; if (!selinux_policycap_netpeer) return NF_ACCEPT; secmark_active = selinux_secmark_enabled(); - peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); + netlbl_active = netlbl_enabled(); + peerlbl_active = netlbl_active || selinux_xfrm_enabled(); if (!secmark_active && !peerlbl_active) return NF_ACCEPT; + if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) + return NF_DROP; + AVC_AUDIT_DATA_INIT(&ad, NET); ad.u.net.netif = ifindex; ad.u.net.family = family; if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) return NF_DROP; - if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) - return NF_DROP; - - if (peerlbl_active) - if (selinux_inet_sys_rcv_skb(ifindex, addrp, family, - peer_sid, &ad) != 0) + if (peerlbl_active) { + err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, + peer_sid, &ad); + if (err) { + selinux_netlbl_err(skb, err, 1); return NF_DROP; + } + } if (secmark_active) if (avc_has_perm(peer_sid, skb->secmark, SECCLASS_PACKET, PACKET__FORWARD_IN, &ad)) return NF_DROP; + if (netlbl_active) + /* we do this in the FORWARD path and not the POST_ROUTING + * path because we want to make sure we apply the necessary + * labeling before IPsec is applied so we can leverage AH + * protection */ + if (selinux_netlbl_skbuff_setsid(skb, family, peer_sid) != 0) + return NF_DROP; + return NF_ACCEPT; } @@ -4433,6 +4481,37 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum, } #endif /* IPV6 */ +static unsigned int selinux_ip_output(struct sk_buff *skb, + u16 family) +{ + u32 sid; + + if (!netlbl_enabled()) + return NF_ACCEPT; + + /* we do this in the LOCAL_OUT path and not the POST_ROUTING path + * because we want to make sure we apply the necessary labeling + * before IPsec is applied so we can leverage AH protection */ + if (skb->sk) { + struct sk_security_struct *sksec = skb->sk->sk_security; + sid = sksec->sid; + } else + sid = SECINITSID_KERNEL; + if (selinux_netlbl_skbuff_setsid(skb, family, sid) != 0) + return NF_DROP; + + return NF_ACCEPT; +} + +static unsigned int selinux_ipv4_output(unsigned int hooknum, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + return selinux_ip_output(skb, PF_INET); +} + static int selinux_ip_postroute_iptables_compat(struct sock *sk, int ifindex, struct avc_audit_data *ad, @@ -4500,30 +4579,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk, static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, int ifindex, - struct avc_audit_data *ad, - u16 family, - char *addrp, - u8 proto) + u16 family) { struct sock *sk = skb->sk; struct sk_security_struct *sksec; + struct avc_audit_data ad; + char *addrp; + u8 proto; if (sk == NULL) return NF_ACCEPT; sksec = sk->sk_security; + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = ifindex; + ad.u.net.family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) + return NF_DROP; + if (selinux_compat_net) { if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, - ad, family, addrp)) + &ad, family, addrp)) return NF_DROP; } else { if (avc_has_perm(sksec->sid, skb->secmark, - SECCLASS_PACKET, PACKET__SEND, ad)) + SECCLASS_PACKET, PACKET__SEND, &ad)) return NF_DROP; } if (selinux_policycap_netpeer) - if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto)) + if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) return NF_DROP; return NF_ACCEPT; @@ -4537,23 +4622,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, struct sock *sk; struct avc_audit_data ad; char *addrp; - u8 proto; u8 secmark_active; u8 peerlbl_active; - AVC_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.netif = ifindex; - ad.u.net.family = family; - if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) - return NF_DROP; - /* If any sort of compatibility mode is enabled then handoff processing * to the selinux_ip_postroute_compat() function to deal with the * special handling. We do this in an attempt to keep this function * as fast and as clean as possible. */ if (selinux_compat_net || !selinux_policycap_netpeer) - return selinux_ip_postroute_compat(skb, ifindex, &ad, - family, addrp, proto); + return selinux_ip_postroute_compat(skb, ifindex, family); /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec * packet transformation so allow the packet to pass without any checks @@ -4569,21 +4646,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, if (!secmark_active && !peerlbl_active) return NF_ACCEPT; - /* if the packet is locally generated (skb->sk != NULL) then use the - * socket's label as the peer label, otherwise the packet is being - * forwarded through this system and we need to fetch the peer label - * directly from the packet */ + /* if the packet is being forwarded then get the peer label from the + * packet itself; otherwise check to see if it is from a local + * application or the kernel, if from an application get the peer label + * from the sending socket, otherwise use the kernel's sid */ sk = skb->sk; - if (sk) { + if (sk == NULL) { + switch (family) { + case PF_INET: + if (IPCB(skb)->flags & IPSKB_FORWARDED) + secmark_perm = PACKET__FORWARD_OUT; + else + secmark_perm = PACKET__SEND; + break; + case PF_INET6: + if (IP6CB(skb)->flags & IP6SKB_FORWARDED) + secmark_perm = PACKET__FORWARD_OUT; + else + secmark_perm = PACKET__SEND; + break; + default: + return NF_DROP; + } + if (secmark_perm == PACKET__FORWARD_OUT) { + if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) + return NF_DROP; + } else + peer_sid = SECINITSID_KERNEL; + } else { struct sk_security_struct *sksec = sk->sk_security; peer_sid = sksec->sid; secmark_perm = PACKET__SEND; - } else { - if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) - return NF_DROP; - secmark_perm = PACKET__FORWARD_OUT; } + AVC_AUDIT_DATA_INIT(&ad, NET); + ad.u.net.netif = ifindex; + ad.u.net.family = family; + if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) + return NF_DROP; + if (secmark_active) if (avc_has_perm(peer_sid, skb->secmark, SECCLASS_PACKET, secmark_perm, &ad)) @@ -5657,6 +5758,13 @@ static struct nf_hook_ops selinux_ipv4_ops[] = { .pf = PF_INET, .hooknum = NF_INET_FORWARD, .priority = NF_IP_PRI_SELINUX_FIRST, + }, + { + .hook = selinux_ipv4_output, + .owner = THIS_MODULE, + .pf = PF_INET, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP_PRI_SELINUX_FIRST, } }; diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 487a7d81fe2..b913c8d0603 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -39,6 +39,9 @@ #ifdef CONFIG_NETLABEL void selinux_netlbl_cache_invalidate(void); +void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway); + +void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec); void selinux_netlbl_sk_security_reset(struct sk_security_struct *ssec, int family); @@ -46,8 +49,11 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, u16 family, u32 *type, u32 *sid); +int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, + u16 family, + u32 sid); -void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock); +void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family); int selinux_netlbl_socket_post_create(struct socket *sock); int selinux_netlbl_inode_permission(struct inode *inode, int mask); int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, @@ -57,12 +63,27 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, int selinux_netlbl_socket_setsockopt(struct socket *sock, int level, int optname); +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr); + #else static inline void selinux_netlbl_cache_invalidate(void) { return; } +static inline void selinux_netlbl_err(struct sk_buff *skb, + int error, + int gateway) +{ + return; +} + +static inline void selinux_netlbl_sk_security_free( + struct sk_security_struct *ssec) +{ + return; +} + static inline void selinux_netlbl_sk_security_reset( struct sk_security_struct *ssec, int family) @@ -79,9 +100,21 @@ static inline int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, *sid = SECSID_NULL; return 0; } +static inline int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, + u16 family, + u32 sid) +{ + return 0; +} -static inline void selinux_netlbl_sock_graft(struct sock *sk, - struct socket *sock) +static inline int selinux_netlbl_conn_setsid(struct sock *sk, + struct sockaddr *addr) +{ + return 0; +} + +static inline void selinux_netlbl_inet_conn_established(struct sock *sk, + u16 family) { return; } @@ -107,6 +140,11 @@ static inline int selinux_netlbl_socket_setsockopt(struct socket *sock, { return 0; } +static inline int selinux_netlbl_socket_connect(struct sock *sk, + struct sockaddr *addr) +{ + return 0; +} #endif /* CONFIG_NETLABEL */ #endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 91070ab874c..f8be8d7fa26 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -109,16 +109,19 @@ struct netport_security_struct { }; struct sk_security_struct { - u32 sid; /* SID of this object */ - u32 peer_sid; /* SID of peer */ - u16 sclass; /* sock security class */ #ifdef CONFIG_NETLABEL enum { /* NetLabel state */ NLBL_UNSET = 0, NLBL_REQUIRE, NLBL_LABELED, + NLBL_REQSKB, + NLBL_CONNLABELED, } nlbl_state; + struct netlbl_lsm_secattr *nlbl_secattr; /* NetLabel sec attributes */ #endif + u32 sid; /* SID of this object */ + u32 peer_sid; /* SID of peer */ + u16 sclass; /* sock security class */ }; struct key_security_struct { diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 89b418392f1..f58701a7b72 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -9,7 +9,7 @@ */ /* - * (c) Copyright Hewlett-Packard Development Company, L.P., 2007 + * (c) Copyright Hewlett-Packard Development Company, L.P., 2007, 2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,8 +29,12 @@ #include <linux/spinlock.h> #include <linux/rcupdate.h> +#include <linux/ip.h> +#include <linux/ipv6.h> #include <net/sock.h> #include <net/netlabel.h> +#include <net/ip.h> +#include <net/ipv6.h> #include "objsec.h" #include "security.h" @@ -64,32 +68,69 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb, } /** + * selinux_netlbl_sock_genattr - Generate the NetLabel socket secattr + * @sk: the socket + * + * Description: + * Generate the NetLabel security attributes for a socket, making full use of + * the socket's attribute cache. Returns a pointer to the security attributes + * on success, NULL on failure. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk) +{ + int rc; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr; + + if (sksec->nlbl_secattr != NULL) + return sksec->nlbl_secattr; + + secattr = netlbl_secattr_alloc(GFP_ATOMIC); + if (secattr == NULL) + return NULL; + rc = security_netlbl_sid_to_secattr(sksec->sid, secattr); + if (rc != 0) { + netlbl_secattr_free(secattr); + return NULL; + } + sksec->nlbl_secattr = secattr; + + return secattr; +} + +/** * selinux_netlbl_sock_setsid - Label a socket using the NetLabel mechanism * @sk: the socket to label - * @sid: the SID to use * * Description: - * Attempt to label a socket using the NetLabel mechanism using the given - * SID. Returns zero values on success, negative values on failure. + * Attempt to label a socket using the NetLabel mechanism. Returns zero values + * on success, negative values on failure. * */ -static int selinux_netlbl_sock_setsid(struct sock *sk, u32 sid) +static int selinux_netlbl_sock_setsid(struct sock *sk) { int rc; struct sk_security_struct *sksec = sk->sk_security; - struct netlbl_lsm_secattr secattr; + struct netlbl_lsm_secattr *secattr; - netlbl_secattr_init(&secattr); + if (sksec->nlbl_state != NLBL_REQUIRE) + return 0; - rc = security_netlbl_sid_to_secattr(sid, &secattr); - if (rc != 0) - goto sock_setsid_return; - rc = netlbl_sock_setattr(sk, &secattr); - if (rc == 0) + secattr = selinux_netlbl_sock_genattr(sk); + if (secattr == NULL) + return -ENOMEM; + rc = netlbl_sock_setattr(sk, secattr); + switch (rc) { + case 0: sksec->nlbl_state = NLBL_LABELED; + break; + case -EDESTADDRREQ: + sksec->nlbl_state = NLBL_REQSKB; + rc = 0; + break; + } -sock_setsid_return: - netlbl_secattr_destroy(&secattr); return rc; } @@ -106,6 +147,38 @@ void selinux_netlbl_cache_invalidate(void) } /** + * selinux_netlbl_err - Handle a NetLabel packet error + * @skb: the packet + * @error: the error code + * @gateway: true if host is acting as a gateway, false otherwise + * + * Description: + * When a packet is dropped due to a call to avc_has_perm() pass the error + * code to the NetLabel subsystem so any protocol specific processing can be + * done. This is safe to call even if you are unsure if NetLabel labeling is + * present on the packet, NetLabel is smart enough to only act when it should. + * + */ +void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway) +{ + netlbl_skbuff_err(skb, error, gateway); +} + +/** + * selinux_netlbl_sk_security_free - Free the NetLabel fields + * @sssec: the sk_security_struct + * + * Description: + * Free all of the memory in the NetLabel fields of a sk_security_struct. + * + */ +void selinux_netlbl_sk_security_free(struct sk_security_struct *ssec) +{ + if (ssec->nlbl_secattr != NULL) + netlbl_secattr_free(ssec->nlbl_secattr); +} + +/** * selinux_netlbl_sk_security_reset - Reset the NetLabel fields * @ssec: the sk_security_struct * @family: the socket family @@ -163,35 +236,118 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, } /** - * selinux_netlbl_sock_graft - Netlabel the new socket + * selinux_netlbl_skbuff_setsid - Set the NetLabel on a packet given a sid + * @skb: the packet + * @family: protocol family + * @sid: the SID + * + * Description + * Call the NetLabel mechanism to set the label of a packet using @sid. + * Returns zero on auccess, negative values on failure. + * + */ +int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, + u16 family, + u32 sid) +{ + int rc; + struct netlbl_lsm_secattr secattr_storage; + struct netlbl_lsm_secattr *secattr = NULL; + struct sock *sk; + + /* if this is a locally generated packet check to see if it is already + * being labeled by it's parent socket, if it is just exit */ + sk = skb->sk; + if (sk != NULL) { + struct sk_security_struct *sksec = sk->sk_security; + if (sksec->nlbl_state != NLBL_REQSKB) + return 0; + secattr = sksec->nlbl_secattr; + } + if (secattr == NULL) { + secattr = &secattr_storage; + netlbl_secattr_init(secattr); + rc = security_netlbl_sid_to_secattr(sid, secattr); + if (rc != 0) + goto skbuff_setsid_return; + } + + rc = netlbl_skbuff_setattr(skb, family, secattr); + +skbuff_setsid_return: + if (secattr == &secattr_storage) + netlbl_secattr_destroy(secattr); + return rc; +} + +/** + * selinux_netlbl_inet_conn_established - Netlabel the newly accepted connection * @sk: the new connection - * @sock: the new socket * * Description: - * The connection represented by @sk is being grafted onto @sock so set the - * socket's NetLabel to match the SID of @sk. + * A new connection has been established on @sk so make sure it is labeled + * correctly with the NetLabel susbsystem. * */ -void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) +void selinux_netlbl_inet_conn_established(struct sock *sk, u16 family) { + int rc; struct sk_security_struct *sksec = sk->sk_security; - struct netlbl_lsm_secattr secattr; - u32 nlbl_peer_sid; + struct netlbl_lsm_secattr *secattr; + struct inet_sock *sk_inet = inet_sk(sk); + struct sockaddr_in addr; if (sksec->nlbl_state != NLBL_REQUIRE) return; - netlbl_secattr_init(&secattr); - if (netlbl_sock_getattr(sk, &secattr) == 0 && - secattr.flags != NETLBL_SECATTR_NONE && - security_netlbl_secattr_to_sid(&secattr, &nlbl_peer_sid) == 0) - sksec->peer_sid = nlbl_peer_sid; - netlbl_secattr_destroy(&secattr); + secattr = selinux_netlbl_sock_genattr(sk); + if (secattr == NULL) + return; - /* Try to set the NetLabel on the socket to save time later, if we fail - * here we will pick up the pieces in later calls to - * selinux_netlbl_inode_permission(). */ - selinux_netlbl_sock_setsid(sk, sksec->sid); + rc = netlbl_sock_setattr(sk, secattr); + switch (rc) { + case 0: + sksec->nlbl_state = NLBL_LABELED; + break; + case -EDESTADDRREQ: + /* no PF_INET6 support yet because we don't support any IPv6 + * labeling protocols */ + if (family != PF_INET) { + sksec->nlbl_state = NLBL_UNSET; + return; + } + + addr.sin_family = family; + addr.sin_addr.s_addr = sk_inet->daddr; + if (netlbl_conn_setattr(sk, (struct sockaddr *)&addr, + secattr) != 0) { + /* we failed to label the connected socket (could be + * for a variety of reasons, the actual "why" isn't + * important here) so we have to go to our backup plan, + * labeling the packets individually in the netfilter + * local output hook. this is okay but we need to + * adjust the MSS of the connection to take into + * account any labeling overhead, since we don't know + * the exact overhead at this point we'll use the worst + * case value which is 40 bytes for IPv4 */ + struct inet_connection_sock *sk_conn = inet_csk(sk); + sk_conn->icsk_ext_hdr_len += 40 - + (sk_inet->opt ? sk_inet->opt->optlen : 0); + sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie); + + sksec->nlbl_state = NLBL_REQSKB; + } else + sksec->nlbl_state = NLBL_CONNLABELED; + break; + default: + /* note that we are failing to label the socket which could be + * a bad thing since it means traffic could leave the system + * without the desired labeling, however, all is not lost as + * we have a check in selinux_netlbl_inode_permission() to + * pick up the pieces that we might drop here because we can't + * return an error code */ + break; + } } /** @@ -205,13 +361,7 @@ void selinux_netlbl_sock_graft(struct sock *sk, struct socket *sock) */ int selinux_netlbl_socket_post_create(struct socket *sock) { - struct sock *sk = sock->sk; - struct sk_security_struct *sksec = sk->sk_security; - - if (sksec->nlbl_state != NLBL_REQUIRE) - return 0; - - return selinux_netlbl_sock_setsid(sk, sksec->sid); + return selinux_netlbl_sock_setsid(sock->sk); } /** @@ -246,7 +396,7 @@ int selinux_netlbl_inode_permission(struct inode *inode, int mask) local_bh_disable(); bh_lock_sock_nested(sk); if (likely(sksec->nlbl_state == NLBL_REQUIRE)) - rc = selinux_netlbl_sock_setsid(sk, sksec->sid); + rc = selinux_netlbl_sock_setsid(sk); else rc = 0; bh_unlock_sock(sk); @@ -307,7 +457,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, return 0; if (nlbl_sid != SECINITSID_UNLABELED) - netlbl_skbuff_err(skb, rc); + netlbl_skbuff_err(skb, rc, 0); return rc; } @@ -334,7 +484,8 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, struct netlbl_lsm_secattr secattr; if (level == IPPROTO_IP && optname == IP_OPTIONS && - sksec->nlbl_state == NLBL_LABELED) { + (sksec->nlbl_state == NLBL_LABELED || + sksec->nlbl_state == NLBL_CONNLABELED)) { netlbl_secattr_init(&secattr); lock_sock(sk); rc = netlbl_sock_getattr(sk, &secattr); @@ -346,3 +497,50 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, return rc; } + +/** + * selinux_netlbl_socket_connect - Label a client-side socket on connect + * @sk: the socket to label + * @addr: the destination address + * + * Description: + * Attempt to label a connected socket with NetLabel using the given address. + * Returns zero values on success, negative values on failure. + * + */ +int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) +{ + int rc; + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr; + + if (sksec->nlbl_state != NLBL_REQSKB && + sksec->nlbl_state != NLBL_CONNLABELED) + return 0; + + local_bh_disable(); + bh_lock_sock_nested(sk); + + /* connected sockets are allowed to disconnect when the address family + * is set to AF_UNSPEC, if that is what is happening we want to reset + * the socket */ + if (addr->sa_family == AF_UNSPEC) { + netlbl_sock_delattr(sk); + sksec->nlbl_state = NLBL_REQSKB; + rc = 0; + goto socket_connect_return; + } + secattr = selinux_netlbl_sock_genattr(sk); + if (secattr == NULL) { + rc = -ENOMEM; + goto socket_connect_return; + } + rc = netlbl_conn_setattr(sk, addr, secattr); + if (rc == 0) + sksec->nlbl_state = NLBL_CONNLABELED; + +socket_connect_return: + bh_unlock_sock(sk); + local_bh_enable(); + return rc; +} diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ab0cc0c7b94..343c8ab14af 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2955,7 +2955,7 @@ netlbl_secattr_to_sid_return_cleanup: */ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) { - int rc = -ENOENT; + int rc; struct context *ctx; if (!ss_initialized) @@ -2963,11 +2963,18 @@ int security_netlbl_sid_to_secattr(u32 sid, struct netlbl_lsm_secattr *secattr) read_lock(&policy_rwlock); ctx = sidtab_search(&sidtab, sid); - if (ctx == NULL) + if (ctx == NULL) { + rc = -ENOENT; goto netlbl_sid_to_secattr_failure; + } secattr->domain = kstrdup(policydb.p_type_val_to_name[ctx->type - 1], GFP_ATOMIC); - secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY; + if (secattr->domain == NULL) { + rc = -ENOMEM; + goto netlbl_sid_to_secattr_failure; + } + secattr->attr.secid = sid; + secattr->flags |= NETLBL_SECATTR_DOMAIN_CPY | NETLBL_SECATTR_SECID; mls_export_netlbl_lvl(ctx, secattr); rc = mls_export_netlbl_cat(ctx, secattr); if (rc != 0) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 87d75417ea9..6e2dc0bab70 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2179,7 +2179,10 @@ static int smack_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) * This is the simplist possible security model * for networking. */ - return smk_access(smack, ssp->smk_in, MAY_WRITE); + rc = smk_access(smack, ssp->smk_in, MAY_WRITE); + if (rc != 0) + netlbl_skbuff_err(skb, rc, 0); + return rc; } /** diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index e7c642458ec..c21d8c8bf0c 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -354,9 +354,11 @@ static void smk_cipso_doi(void) doip->tags[rc] = CIPSO_V4_TAG_INVALID; rc = netlbl_cfg_cipsov4_add_map(doip, NULL, &audit_info); - if (rc != 0) + if (rc != 0) { printk(KERN_WARNING "%s:%d add rc = %d\n", __func__, __LINE__, rc); + kfree(doip); + } } /** diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index a7b46ec72f3..1b3534d6768 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -33,9 +33,6 @@ #include <linux/moduleparam.h> #include <linux/mutex.h> #include <sound/memalloc.h> -#ifdef CONFIG_SBUS -#include <asm/sbus.h> -#endif MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>, Jaroslav Kysela <perex@perex.cz>"); @@ -162,39 +159,6 @@ static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, } #endif /* CONFIG_HAS_DMA */ -#ifdef CONFIG_SBUS - -static void *snd_malloc_sbus_pages(struct device *dev, size_t size, - dma_addr_t *dma_addr) -{ - struct sbus_dev *sdev = (struct sbus_dev *)dev; - int pg; - void *res; - - if (WARN_ON(!dma_addr)) - return NULL; - pg = get_order(size); - res = sbus_alloc_consistent(sdev, PAGE_SIZE * (1 << pg), dma_addr); - if (res != NULL) - inc_snd_pages(pg); - return res; -} - -static void snd_free_sbus_pages(struct device *dev, size_t size, - void *ptr, dma_addr_t dma_addr) -{ - struct sbus_dev *sdev = (struct sbus_dev *)dev; - int pg; - - if (ptr == NULL) - return; - pg = get_order(size); - dec_snd_pages(pg); - sbus_free_consistent(sdev, PAGE_SIZE * (1 << pg), ptr, dma_addr); -} - -#endif /* CONFIG_SBUS */ - /* * * ALSA generic memory management @@ -231,11 +195,6 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, dmab->area = snd_malloc_pages(size, (unsigned long)device); dmab->addr = 0; break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - dmab->area = snd_malloc_sbus_pages(device, size, &dmab->addr); - break; -#endif #ifdef CONFIG_HAS_DMA case SNDRV_DMA_TYPE_DEV: dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); @@ -306,11 +265,6 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) case SNDRV_DMA_TYPE_CONTINUOUS: snd_free_pages(dmab->area, dmab->bytes); break; -#ifdef CONFIG_SBUS - case SNDRV_DMA_TYPE_SBUS: - snd_free_sbus_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); - break; -#endif #ifdef CONFIG_HAS_DMA case SNDRV_DMA_TYPE_DEV: snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); @@ -419,7 +373,7 @@ static int snd_mem_proc_read(struct seq_file *seq, void *offset) long pages = snd_allocated_pages >> (PAGE_SHIFT-12); struct snd_mem_list *mem; int devno; - static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG", "SBUS" }; + static char *types[] = { "UNKNOWN", "CONT", "DEV", "DEV-SG" }; mutex_lock(&list_mutex); seq_printf(seq, "pages : %li bytes (%li pages per %likB)\n", diff --git a/sound/oss/ac97_codec.c b/sound/oss/ac97_codec.c index b63839e8f9b..456a1b4d783 100644 --- a/sound/oss/ac97_codec.c +++ b/sound/oss/ac97_codec.c @@ -30,7 +30,7 @@ ************************************************************************** * * History - * May 02, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * May 02, 2003 Liam Girdwood <lrg@slimlogic.co.uk> * Removed non existant WM9700 * Added support for WM9705, WM9708, WM9709, WM9710, WM9711 * WM9712 and WM9717 diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index 6ce3cbe98a6..6e831aff1bd 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -476,7 +476,7 @@ static int patch_yamaha_ymf753(struct snd_ac97 * ac97) } /* - * May 2, 2003 Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * May 2, 2003 Liam Girdwood <lrg@slimlogic.co.uk> * removed broken wolfson00 patch. * added support for WM9705,WM9708,WM9709,WM9710,WM9711,WM9712 and WM9717. */ diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index c461baa83c2..c5906551311 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -322,8 +322,8 @@ static hda_nid_t stac92hd71bxx_mux_nids[2] = { 0x1a, 0x1b }; -static hda_nid_t stac92hd71bxx_dmux_nids[1] = { - 0x1c, +static hda_nid_t stac92hd71bxx_dmux_nids[2] = { + 0x1c, 0x1d, }; static hda_nid_t stac92hd71bxx_smux_nids[2] = { @@ -861,20 +861,18 @@ static struct hda_verb stac92hd71bxx_core_init[] = { { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* connect headphone jack to dac1 */ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ /* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, }; -#define HD_DISABLE_PORTF 3 +#define HD_DISABLE_PORTF 2 static struct hda_verb stac92hd71bxx_analog_core_init[] = { /* start of config #1 */ /* connect port 0f to audio mixer */ { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2}, - { 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */ /* unmute right and left channels for node 0x0f */ { 0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, /* start of config #2 */ @@ -883,10 +881,6 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = { { 0x28, AC_VERB_SET_VOLUME_KNOB_CONTROL, 0xff}, /* connect headphone jack to dac1 */ { 0x0a, AC_VERB_SET_CONNECT_SEL, 0x01}, - /* connect port 0d to audio mixer */ - { 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2}, - /* unmute dac0 input in audio mixer */ - { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f}, /* unmute right and left channels for nodes 0x0a, 0xd */ { 0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, { 0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, @@ -1107,6 +1101,7 @@ static struct snd_kcontrol_new stac92hd83xxx_mixer[] = { static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { STAC_INPUT_SOURCE(2), + STAC_ANALOG_LOOPBACK(0xFA0, 0x7A0, 2), HDA_CODEC_VOLUME_IDX("Capture Volume", 0x0, 0x1c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE_IDX("Capture Switch", 0x0, 0x1c, 0x0, HDA_OUTPUT), @@ -1119,8 +1114,17 @@ static struct snd_kcontrol_new stac92hd71bxx_analog_mixer[] = { HDA_CODEC_MUTE("PC Beep Switch", 0x17, 0x2, HDA_INPUT), */ - HDA_CODEC_MUTE("Analog Loopback 1", 0x17, 0x3, HDA_INPUT), - HDA_CODEC_MUTE("Analog Loopback 2", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_MUTE("Import0 Mux Capture Switch", 0x17, 0x0, HDA_INPUT), + HDA_CODEC_VOLUME("Import0 Mux Capture Volume", 0x17, 0x0, HDA_INPUT), + + HDA_CODEC_MUTE("Import1 Mux Capture Switch", 0x17, 0x1, HDA_INPUT), + HDA_CODEC_VOLUME("Import1 Mux Capture Volume", 0x17, 0x1, HDA_INPUT), + + HDA_CODEC_MUTE("DAC0 Capture Switch", 0x17, 0x3, HDA_INPUT), + HDA_CODEC_VOLUME("DAC0 Capture Volume", 0x17, 0x3, HDA_INPUT), + + HDA_CODEC_MUTE("DAC1 Capture Switch", 0x17, 0x4, HDA_INPUT), + HDA_CODEC_VOLUME("DAC1 Capture Volume", 0x17, 0x4, HDA_INPUT), { } /* end */ }; @@ -1649,7 +1653,7 @@ static struct snd_pci_quirk stac92hd83xxx_cfg_tbl[] = { static unsigned int ref92hd71bxx_pin_configs[11] = { 0x02214030, 0x02a19040, 0x01a19020, 0x01014010, - 0x0181302e, 0x01114010, 0x01019020, 0x90a000f0, + 0x0181302e, 0x01014010, 0x01019020, 0x90a000f0, 0x90a000f0, 0x01452050, 0x01452050, }; @@ -3000,7 +3004,7 @@ static int stac92xx_auto_create_mono_output_ctls(struct hda_codec *codec) /* labels for amp mux outputs */ static const char *stac92xx_amp_labels[3] = { - "Front Microphone", "Microphone", "Line In" + "Front Microphone", "Microphone", "Line In", }; /* create amp out controls mux on capable codecs */ @@ -4327,6 +4331,16 @@ static struct hda_codec_ops stac92hd71bxx_patch_ops = { #endif }; +static struct hda_input_mux stac92hd71bxx_dmux = { + .num_items = 4, + .items = { + { "Analog Inputs", 0x00 }, + { "Mixer", 0x01 }, + { "Digital Mic 1", 0x02 }, + { "Digital Mic 2", 0x03 }, + } +}; + static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; @@ -4341,6 +4355,8 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->num_pins = ARRAY_SIZE(stac92hd71bxx_pin_nids); spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids); spec->pin_nids = stac92hd71bxx_pin_nids; + memcpy(&spec->private_dimux, &stac92hd71bxx_dmux, + sizeof(stac92hd71bxx_dmux)); spec->board_config = snd_hda_check_board_config(codec, STAC_92HD71BXX_MODELS, stac92hd71bxx_models, @@ -4392,6 +4408,7 @@ again: /* no output amps */ spec->num_pwrs = 0; spec->mixer = stac92hd71bxx_analog_mixer; + spec->dinput_mux = &spec->private_dimux; /* disable VSW */ spec->init = &stac92hd71bxx_analog_core_init[HD_DISABLE_PORTF]; @@ -4409,12 +4426,13 @@ again: spec->num_pwrs = 0; /* fallthru */ default: + spec->dinput_mux = &spec->private_dimux; spec->mixer = stac92hd71bxx_analog_mixer; spec->init = stac92hd71bxx_analog_core_init; codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs; } - spec->aloopback_mask = 0x20; + spec->aloopback_mask = 0x50; spec->aloopback_shift = 0; if (spec->board_config > STAC_92HD71BXX_REF) { @@ -4456,6 +4474,10 @@ again: spec->multiout.num_dacs = 1; spec->multiout.hp_nid = 0x11; spec->multiout.dac_nids = stac92hd71bxx_dac_nids; + if (spec->dinput_mux) + spec->private_dimux.num_items += + spec->num_dmics - + (ARRAY_SIZE(stac92hd71bxx_dmic_nids) - 1); err = stac92xx_parse_auto_config(codec, 0x21, 0x23); if (!err) { diff --git a/sound/soc/at91/Kconfig b/sound/soc/at91/Kconfig index 905186502e0..85a883299c2 100644 --- a/sound/soc/at91/Kconfig +++ b/sound/soc/at91/Kconfig @@ -8,20 +8,3 @@ config SND_AT91_SOC config SND_AT91_SOC_SSC tristate - -config SND_AT91_SOC_ETI_B1_WM8731 - tristate "SoC Audio support for WM8731-based Endrelia ETI-B1 boards" - depends on SND_AT91_SOC && (MACH_ETI_B1 || MACH_ETI_C1) - select SND_AT91_SOC_SSC - select SND_SOC_WM8731 - help - Say Y if you want to add support for SoC audio on WM8731-based - Endrelia Technologies Inc ETI-B1 or ETI-C1 boards. - -config SND_AT91_SOC_ETI_SLAVE - bool "Run codec in slave Mode on Endrelia boards" - depends on SND_AT91_SOC_ETI_B1_WM8731 - default n - help - Say Y if you want to run with the AT91 SSC generating the BCLK - and LRC signals on Endrelia boards. diff --git a/sound/soc/at91/Makefile b/sound/soc/at91/Makefile index f23da17cc32..b817f11df28 100644 --- a/sound/soc/at91/Makefile +++ b/sound/soc/at91/Makefile @@ -4,8 +4,3 @@ snd-soc-at91-ssc-objs := at91-ssc.o obj-$(CONFIG_SND_AT91_SOC) += snd-soc-at91.o obj-$(CONFIG_SND_AT91_SOC_SSC) += snd-soc-at91-ssc.o - -# AT91 Machine Support -snd-soc-eti-b1-wm8731-objs := eti_b1_wm8731.o - -obj-$(CONFIG_SND_AT91_SOC_ETI_B1_WM8731) += snd-soc-eti-b1-wm8731.o diff --git a/sound/soc/at91/at91-ssc.c b/sound/soc/at91/at91-ssc.c index a5b1a79ebff..1b61cc46126 100644 --- a/sound/soc/at91/at91-ssc.c +++ b/sound/soc/at91/at91-ssc.c @@ -5,7 +5,7 @@ * Endrelia Technologies Inc. * * Based on pxa2xx Platform drivers by - * Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c deleted file mode 100644 index 684781e4088..00000000000 --- a/sound/soc/at91/eti_b1_wm8731.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * eti_b1_wm8731 -- SoC audio for AT91RM9200-based Endrelia ETI_B1 board. - * - * Author: Frank Mandarino <fmandarino@endrelia.com> - * Endrelia Technologies Inc. - * Created: Mar 29, 2006 - * - * Based on corgi.c by: - * - * Copyright 2005 Wolfson Microelectronics PLC. - * Copyright 2005 Openedhand Ltd. - * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> - * Richard Purdie <richard@openedhand.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/clk.h> -#include <linux/timer.h> -#include <linux/interrupt.h> -#include <linux/platform_device.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include <sound/soc-dapm.h> - -#include <mach/hardware.h> -#include <mach/gpio.h> - -#include "../codecs/wm8731.h" -#include "at91-pcm.h" -#include "at91-ssc.h" - -#if 0 -#define DBG(x...) printk(KERN_INFO "eti_b1_wm8731: " x) -#else -#define DBG(x...) -#endif - -static struct clk *pck1_clk; -static struct clk *pllb_clk; - - -static int eti_b1_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - int ret; - - /* cpu clock is the AT91 master clock sent to the SSC */ - ret = snd_soc_dai_set_sysclk(cpu_dai, AT91_SYSCLK_MCK, - 60000000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* codec system clock is supplied by PCK1, set to 12MHz */ - ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK, - 12000000, SND_SOC_CLOCK_IN); - if (ret < 0) - return ret; - - /* Start PCK1 clock. */ - clk_enable(pck1_clk); - DBG("pck1 started\n"); - - return 0; -} - -static void eti_b1_shutdown(struct snd_pcm_substream *substream) -{ - /* Stop PCK1 clock. */ - clk_disable(pck1_clk); - DBG("pck1 stopped\n"); -} - -static int eti_b1_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; - int ret; - -#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE - unsigned int rate; - int cmr_div, period; - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS); - if (ret < 0) - return ret; - - /* - * The SSC clock dividers depend on the sample rate. The CMR.DIV - * field divides the system master clock MCK to drive the SSC TK - * signal which provides the codec BCLK. The TCMR.PERIOD and - * RCMR.PERIOD fields further divide the BCLK signal to drive - * the SSC TF and RF signals which provide the codec DACLRC and - * ADCLRC clocks. - * - * The dividers were determined through trial and error, where a - * CMR.DIV value is chosen such that the resulting BCLK value is - * divisible, or almost divisible, by (2 * sample rate), and then - * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1. - */ - rate = params_rate(params); - - switch (rate) { - case 8000: - cmr_div = 25; /* BCLK = 60MHz/(2*25) = 1.2MHz */ - period = 74; /* LRC = BCLK/(2*(74+1)) = 8000Hz */ - break; - case 32000: - cmr_div = 7; /* BCLK = 60MHz/(2*7) ~= 4.28571428MHz */ - period = 66; /* LRC = BCLK/(2*(66+1)) = 31982.942Hz */ - break; - case 48000: - cmr_div = 13; /* BCLK = 60MHz/(2*13) ~= 2.3076923MHz */ - period = 23; /* LRC = BCLK/(2*(23+1)) = 48076.923Hz */ - break; - default: - printk(KERN_WARNING "unsupported rate %d on ETI-B1 board\n", rate); - return -EINVAL; - } - - /* set the MCK divider for BCLK */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, AT91SSC_CMR_DIV, cmr_div); - if (ret < 0) - return ret; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - /* set the BCLK divider for DACLRC */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - AT91SSC_TCMR_PERIOD, period); - } else { - /* set the BCLK divider for ADCLRC */ - ret = snd_soc_dai_set_clkdiv(cpu_dai, - AT91SSC_RCMR_PERIOD, period); - } - if (ret < 0) - return ret; - -#else /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ - /* - * Codec in Master Mode. - */ - - /* set codec DAI configuration */ - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - - /* set cpu DAI configuration */ - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); - if (ret < 0) - return ret; - -#endif /* CONFIG_SND_AT91_SOC_ETI_SLAVE */ - - return 0; -} - -static struct snd_soc_ops eti_b1_ops = { - .startup = eti_b1_startup, - .hw_params = eti_b1_hw_params, - .shutdown = eti_b1_shutdown, -}; - - -static const struct snd_soc_dapm_widget eti_b1_dapm_widgets[] = { - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), -}; - -static const struct snd_soc_dapm_route intercon[] = { - - /* speaker connected to LHPOUT */ - {"Ext Spk", NULL, "LHPOUT"}, - - /* mic is connected to Mic Jack, with WM8731 Mic Bias */ - {"MICIN", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Int Mic"}, -}; - -/* - * Logic for a wm8731 as connected on a Endrelia ETI-B1 board. - */ -static int eti_b1_wm8731_init(struct snd_soc_codec *codec) -{ - DBG("eti_b1_wm8731_init() called\n"); - - /* Add specific widgets */ - snd_soc_dapm_new_controls(codec, eti_b1_dapm_widgets, - ARRAY_SIZE(eti_b1_dapm_widgets)); - - /* Set up specific audio path interconnects */ - snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon)); - - /* not connected */ - snd_soc_dapm_disable_pin(codec, "RLINEIN"); - snd_soc_dapm_disable_pin(codec, "LLINEIN"); - - /* always connected */ - snd_soc_dapm_enable_pin(codec, "Int Mic"); - snd_soc_dapm_enable_pin(codec, "Ext Spk"); - - snd_soc_dapm_sync(codec); - - return 0; -} - -static struct snd_soc_dai_link eti_b1_dai = { - .name = "WM8731", - .stream_name = "WM8731 PCM", - .cpu_dai = &at91_ssc_dai[1], - .codec_dai = &wm8731_dai, - .init = eti_b1_wm8731_init, - .ops = &eti_b1_ops, -}; - -static struct snd_soc_machine snd_soc_machine_eti_b1 = { - .name = "ETI_B1_WM8731", - .dai_link = &eti_b1_dai, - .num_links = 1, -}; - -static struct wm8731_setup_data eti_b1_wm8731_setup = { - .i2c_bus = 0, - .i2c_address = 0x1a, -}; - -static struct snd_soc_device eti_b1_snd_devdata = { - .machine = &snd_soc_machine_eti_b1, - .platform = &at91_soc_platform, - .codec_dev = &soc_codec_dev_wm8731, - .codec_data = &eti_b1_wm8731_setup, -}; - -static struct platform_device *eti_b1_snd_device; - -static int __init eti_b1_init(void) -{ - int ret; - struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; - - if (!request_mem_region(AT91RM9200_BASE_SSC1, SZ_16K, "soc-audio")) { - DBG("SSC1 memory region is busy\n"); - return -EBUSY; - } - - ssc->base = ioremap(AT91RM9200_BASE_SSC1, SZ_16K); - if (!ssc->base) { - DBG("SSC1 memory ioremap failed\n"); - ret = -ENOMEM; - goto fail_release_mem; - } - - ssc->pid = AT91RM9200_ID_SSC1; - - eti_b1_snd_device = platform_device_alloc("soc-audio", -1); - if (!eti_b1_snd_device) { - DBG("platform device allocation failed\n"); - ret = -ENOMEM; - goto fail_io_unmap; - } - - platform_set_drvdata(eti_b1_snd_device, &eti_b1_snd_devdata); - eti_b1_snd_devdata.dev = &eti_b1_snd_device->dev; - - ret = platform_device_add(eti_b1_snd_device); - if (ret) { - DBG("platform device add failed\n"); - platform_device_put(eti_b1_snd_device); - goto fail_io_unmap; - } - - at91_set_A_periph(AT91_PIN_PB6, 0); /* TF1 */ - at91_set_A_periph(AT91_PIN_PB7, 0); /* TK1 */ - at91_set_A_periph(AT91_PIN_PB8, 0); /* TD1 */ - at91_set_A_periph(AT91_PIN_PB9, 0); /* RD1 */ -/* at91_set_A_periph(AT91_PIN_PB10, 0);*/ /* RK1 */ - at91_set_A_periph(AT91_PIN_PB11, 0); /* RF1 */ - - /* - * Set PCK1 parent to PLLB and its rate to 12 Mhz. - */ - pllb_clk = clk_get(NULL, "pllb"); - pck1_clk = clk_get(NULL, "pck1"); - - clk_set_parent(pck1_clk, pllb_clk); - clk_set_rate(pck1_clk, 12000000); - - DBG("MCLK rate %luHz\n", clk_get_rate(pck1_clk)); - - /* assign the GPIO pin to PCK1 */ - at91_set_B_periph(AT91_PIN_PA24, 0); - -#ifdef CONFIG_SND_AT91_SOC_ETI_SLAVE - printk(KERN_INFO "eti_b1_wm8731: Codec in Slave Mode\n"); -#else - printk(KERN_INFO "eti_b1_wm8731: Codec in Master Mode\n"); -#endif - return ret; - -fail_io_unmap: - iounmap(ssc->base); -fail_release_mem: - release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); - return ret; -} - -static void __exit eti_b1_exit(void) -{ - struct at91_ssc_periph *ssc = eti_b1_dai.cpu_dai->private_data; - - clk_put(pck1_clk); - clk_put(pllb_clk); - - platform_device_unregister(eti_b1_snd_device); - - iounmap(ssc->base); - release_mem_region(AT91RM9200_BASE_SSC1, SZ_16K); -} - -module_init(eti_b1_init); -module_exit(eti_b1_exit); - -/* Module information */ -MODULE_AUTHOR("Frank Mandarino <fmandarino@endrelia.com>"); -MODULE_DESCRIPTION("ALSA SoC ETI-B1-WM8731"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index f98331d099e..dc006206f62 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig @@ -17,6 +17,22 @@ config SND_BF5XX_SOC_SSM2602 help Say Y if you want to add support for SoC audio on BF527-EZKIT. +config SND_BF5XX_SOC_AD73311 + tristate "SoC AD73311 Audio support for Blackfin" + depends on SND_BF5XX_I2S + select SND_BF5XX_SOC_I2S + select SND_SOC_AD73311 + help + Say Y if you want to add support for AD73311 codec on Blackfin. + +config SND_BFIN_AD73311_SE + int "PF pin for AD73311L Chip Select" + depends on SND_BF5XX_SOC_AD73311 + default 4 + help + Enter the GPIO used to control AD73311's SE pin. Acceptable + values are 0 to 7 + config SND_BF5XX_AC97 tristate "SoC AC97 Audio for the ADI BF5xx chip" depends on BLACKFIN && SND_SOC diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 9ea8bd9e0ba..97bb37a6359 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile @@ -14,7 +14,8 @@ obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o # Blackfin Machine Support snd-ad1980-objs := bf5xx-ad1980.o snd-ssm2602-objs := bf5xx-ssm2602.o - +snd-ad73311-objs := bf5xx-ad73311.o obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o +obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 51f4907c483..25e50d2ea1e 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c @@ -56,6 +56,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, sport->tx_pos += runtime->period_size; if (sport->tx_pos >= runtime->buffer_size) sport->tx_pos %= runtime->buffer_size; + sport->tx_delay_pos = sport->tx_pos; } else { bf5xx_ac97_to_pcm( (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos, @@ -72,7 +73,15 @@ static void bf5xx_dma_irq(void *data) struct snd_pcm_substream *pcm = data; #if defined(CONFIG_SND_MMAP_SUPPORT) struct snd_pcm_runtime *runtime = pcm->runtime; + struct sport_device *sport = runtime->private_data; bf5xx_mmap_copy(pcm, runtime->period_size); + if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (sport->once == 0) { + snd_pcm_period_elapsed(pcm); + bf5xx_mmap_copy(pcm, runtime->period_size); + sport->once = 1; + } + } #endif snd_pcm_period_elapsed(pcm); } @@ -114,6 +123,10 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + memset(runtime->dma_area, 0, runtime->buffer_size); snd_pcm_lib_free_pages(substream); return 0; } @@ -127,16 +140,11 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) * SPORT working in TMD mode(include AC97). */ #if defined(CONFIG_SND_MMAP_SUPPORT) - size_t size = bf5xx_pcm_hardware.buffer_bytes_max - * sizeof(struct ac97_frame) / 4; - /*clean up intermediate buffer*/ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - memset(sport->tx_dma_buf, 0, size); sport_set_tx_callback(sport, bf5xx_dma_irq, substream); sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, runtime->period_size * sizeof(struct ac97_frame)); } else { - memset(sport->rx_dma_buf, 0, size); sport_set_rx_callback(sport, bf5xx_dma_irq, substream); sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods, runtime->period_size * sizeof(struct ac97_frame)); @@ -164,8 +172,12 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) pr_debug("%s enter\n", __func__); switch (cmd) { case SNDRV_PCM_TRIGGER_START: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + bf5xx_mmap_copy(substream, runtime->period_size); + snd_pcm_period_elapsed(substream); + sport->tx_delay_pos = 0; sport_tx_start(sport); + } else sport_rx_start(sport); break; @@ -198,7 +210,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) #if defined(CONFIG_SND_MMAP_SUPPORT) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - curr = sport->tx_pos; + curr = sport->tx_delay_pos; else curr = sport->rx_pos; #else @@ -237,6 +249,21 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) return ret; } +static int bf5xx_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct sport_device *sport = runtime->private_data; + + pr_debug("%s enter\n", __func__); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + sport->once = 0; + memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); + } else + memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); + + return 0; +} + #ifdef CONFIG_SND_MMAP_SUPPORT static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) @@ -272,6 +299,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, struct snd_pcm_ops bf5xx_pcm_ac97_ops = { .open = bf5xx_pcm_open, + .close = bf5xx_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = bf5xx_pcm_hw_params, .hw_free = bf5xx_pcm_hw_free, diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c index c782e311fd5..5e5aafb6485 100644 --- a/sound/soc/blackfin/bf5xx-ac97.c +++ b/sound/soc/blackfin/bf5xx-ac97.c @@ -129,7 +129,6 @@ static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data) struct ac97_frame *nextwrite; sport_incfrag(sport, &nextfrag, 1); - sport_incfrag(sport, &nextfrag, 1); nextwrite = (struct ac97_frame *)(sport->tx_buf + \ nextfrag * sport->tx_fragsize); diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c new file mode 100644 index 00000000000..622c9b90953 --- /dev/null +++ b/sound/soc/blackfin/bf5xx-ad73311.c @@ -0,0 +1,240 @@ +/* + * File: sound/soc/blackfin/bf5xx-ad73311.c + * Author: Cliff Cai <Cliff.Cai@analog.com> + * + * Created: Thur Sep 25 2008 + * Description: Board driver for ad73311 sound chip + * + * Modified: + * Copyright 2008 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/gpio.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/pcm_params.h> + +#include <asm/blackfin.h> +#include <asm/cacheflush.h> +#include <asm/irq.h> +#include <asm/dma.h> +#include <asm/portmux.h> + +#include "../codecs/ad73311.h" +#include "bf5xx-sport.h" +#include "bf5xx-i2s-pcm.h" +#include "bf5xx-i2s.h" + +#if CONFIG_SND_BF5XX_SPORT_NUM == 0 +#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1 +#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1 +#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2 +#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16 +#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT +#else +#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1 +#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1 +#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2 +#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16 +#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT +#endif + +#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE + +static struct snd_soc_machine bf5xx_ad73311; + +static int snd_ad73311_startup(void) +{ + pr_debug("%s enter\n", __func__); + + /* Pull up SE pin on AD73311L */ + gpio_set_value(GPIO_SE, 1); + return 0; +} + +static int snd_ad73311_configure(void) +{ + unsigned short ctrl_regs[6]; + unsigned short status = 0; + int count = 0; + + /* DMCLK = MCLK = 16.384 MHz + * SCLK = DMCLK/8 = 2.048 MHz + * Sample Rate = DMCLK/2048 = 8 KHz + */ + ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \ + REGB_SCDIV(0) | REGB_DIRATE(0); + ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \ + REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ; + ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \ + REGD_IGS(2); + ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f); + ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ; + ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA; + + local_irq_disable(); + snd_ad73311_startup(); + udelay(1); + + bfin_write_SPORT_TCR1(TFSR); + bfin_write_SPORT_TCR2(0xF); + SSYNC(); + + /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to + * FIFO before enable SPORT to transfer the data + */ + for (count = 0; count < 6; count++) + bfin_write_SPORT_TX16(ctrl_regs[count]); + SSYNC(); + bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN); + SSYNC(); + + /* When TUVF is set, the data is already send out */ + while (!(status & TUVF) && count++ < 10000) { + udelay(1); + status = bfin_read_SPORT_STAT(); + SSYNC(); + } + bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN); + SSYNC(); + local_irq_enable(); + + if (count == 10000) { + printk(KERN_ERR "ad73311: failed to configure codec\n"); + return -1; + } + return 0; +} + +static int bf5xx_probe(struct platform_device *pdev) +{ + int err; + if (gpio_request(GPIO_SE, "AD73311_SE")) { + printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE); + return -EBUSY; + } + + gpio_direction_output(GPIO_SE, 0); + + err = snd_ad73311_configure(); + if (err < 0) + return -EFAULT; + + return 0; +} + +static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + + pr_debug("%s enter\n", __func__); + cpu_dai->private_data = sport_handle; + return 0; +} + +static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int ret = 0; + + pr_debug("%s rate %d format %x\n", __func__, params_rate(params), + params_format(params)); + + /* set cpu DAI configuration */ + ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + return ret; + + return 0; +} + + +static struct snd_soc_ops bf5xx_ad73311_ops = { + .startup = bf5xx_ad73311_startup, + .hw_params = bf5xx_ad73311_hw_params, +}; + +static struct snd_soc_dai_link bf5xx_ad73311_dai = { + .name = "ad73311", + .stream_name = "AD73311", + .cpu_dai = &bf5xx_i2s_dai, + .codec_dai = &ad73311_dai, + .ops = &bf5xx_ad73311_ops, +}; + +static struct snd_soc_machine bf5xx_ad73311 = { + .name = "bf5xx_ad73311", + .probe = bf5xx_probe, + .dai_link = &bf5xx_ad73311_dai, + .num_links = 1, +}; + +static struct snd_soc_device bf5xx_ad73311_snd_devdata = { + .machine = &bf5xx_ad73311, + .platform = &bf5xx_i2s_soc_platform, + .codec_dev = &soc_codec_dev_ad73311, +}; + +static struct platform_device *bf52x_ad73311_snd_device; + +static int __init bf5xx_ad73311_init(void) +{ + int ret; + + pr_debug("%s enter\n", __func__); + bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1); + if (!bf52x_ad73311_snd_device) + return -ENOMEM; + + platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata); + bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev; + ret = platform_device_add(bf52x_ad73311_snd_device); + + if (ret) + platform_device_put(bf52x_ad73311_snd_device); + + return ret; +} + +static void __exit bf5xx_ad73311_exit(void) +{ + pr_debug("%s enter\n", __func__); + platform_device_unregister(bf52x_ad73311_snd_device); +} + +module_init(bf5xx_ad73311_init); +module_exit(bf5xx_ad73311_exit); + +/* Module information */ +MODULE_AUTHOR("Cliff Cai"); +MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c index 43a4092eeb8..827587f0818 100644 --- a/sound/soc/blackfin/bf5xx-i2s.c +++ b/sound/soc/blackfin/bf5xx-i2s.c @@ -70,6 +70,13 @@ static struct sport_param sport_params[2] = { } }; +static u16 sport_req[][7] = { + { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, + P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}, + { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, + P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}, +}; + static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) { @@ -78,6 +85,14 @@ static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, /* interface format:support I2S,slave mode */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: + bf5xx_i2s.tcr1 |= TFSR | TCKFE; + bf5xx_i2s.rcr1 |= RFSR | RCKFE; + bf5xx_i2s.tcr2 |= TSFSE; + bf5xx_i2s.rcr2 |= RSFSE; + break; + case SND_SOC_DAIFMT_DSP_A: + bf5xx_i2s.tcr1 |= TFSR; + bf5xx_i2s.rcr1 |= RFSR; break; case SND_SOC_DAIFMT_LEFT_J: ret = -EINVAL; @@ -127,14 +142,17 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, case SNDRV_PCM_FORMAT_S16_LE: bf5xx_i2s.tcr2 |= 15; bf5xx_i2s.rcr2 |= 15; + sport_handle->wdsize = 2; break; case SNDRV_PCM_FORMAT_S24_LE: bf5xx_i2s.tcr2 |= 23; bf5xx_i2s.rcr2 |= 23; + sport_handle->wdsize = 3; break; case SNDRV_PCM_FORMAT_S32_LE: bf5xx_i2s.tcr2 |= 31; bf5xx_i2s.rcr2 |= 31; + sport_handle->wdsize = 4; break; } @@ -145,17 +163,17 @@ static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream, * need to configure both of them at the time when the first * stream is opened. * - * CPU DAI format:I2S, slave mode. + * CPU DAI:slave mode. */ - ret = sport_config_rx(sport_handle, RFSR | RCKFE, - RSFSE|bf5xx_i2s.rcr2, 0, 0); + ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1, + bf5xx_i2s.rcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; } - ret = sport_config_tx(sport_handle, TFSR | TCKFE, - TSFSE|bf5xx_i2s.tcr2, 0, 0); + ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1, + bf5xx_i2s.tcr2, 0, 0); if (ret) { pr_err("SPORT is busy!\n"); return -EBUSY; @@ -174,13 +192,6 @@ static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream) static int bf5xx_i2s_probe(struct platform_device *pdev, struct snd_soc_dai *dai) { - u16 sport_req[][7] = { - { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, - P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}, - { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, - P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}, - }; - pr_debug("%s enter\n", __func__); if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) { pr_err("Requesting Peripherals failed\n"); @@ -198,6 +209,13 @@ static int bf5xx_i2s_probe(struct platform_device *pdev, return 0; } +static void bf5xx_i2s_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + pr_debug("%s enter\n", __func__); + peripheral_free_list(&sport_req[sport_num][0]); +} + #ifdef CONFIG_PM static int bf5xx_i2s_suspend(struct platform_device *dev, struct snd_soc_dai *dai) @@ -263,15 +281,16 @@ struct snd_soc_dai bf5xx_i2s_dai = { .id = 0, .type = SND_SOC_DAI_I2S, .probe = bf5xx_i2s_probe, + .remove = bf5xx_i2s_remove, .suspend = bf5xx_i2s_suspend, .resume = bf5xx_i2s_resume, .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = BF5XX_I2S_RATES, .formats = BF5XX_I2S_FORMATS,}, .capture = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = BF5XX_I2S_RATES, .formats = BF5XX_I2S_FORMATS,}, diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h index 4c163454bbf..fcadcc081f7 100644 --- a/sound/soc/blackfin/bf5xx-sport.h +++ b/sound/soc/blackfin/bf5xx-sport.h @@ -123,6 +123,8 @@ struct sport_device { int rx_pos; unsigned int tx_buffer_size; unsigned int rx_buffer_size; + int tx_delay_pos; + int once; #endif void *private_data; }; diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e0b9869df0f..4975d8573e4 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -3,9 +3,11 @@ config SND_SOC_ALL_CODECS depends on I2C select SPI select SPI_MASTER + select SND_SOC_AD73311 select SND_SOC_AK4535 select SND_SOC_CS4270 select SND_SOC_SSM2602 + select SND_SOC_TLV320AIC23 select SND_SOC_TLV320AIC26 select SND_SOC_TLV320AIC3X select SND_SOC_UDA1380 @@ -34,6 +36,9 @@ config SND_SOC_AC97_CODEC config SND_SOC_AD1980 tristate +config SND_SOC_AD73311 + tristate + config SND_SOC_AK4535 tristate @@ -58,9 +63,13 @@ config SND_SOC_CS4270_VD33_ERRATA config SND_SOC_SSM2602 tristate +config SND_SOC_TLV320AIC23 + tristate + depends on I2C + config SND_SOC_TLV320AIC26 tristate "TI TLV320AIC26 Codec support" - depends on SND_SOC && SPI + depends on SPI config SND_SOC_TLV320AIC3X tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index f977978a340..90f0a585fc7 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -1,8 +1,10 @@ snd-soc-ac97-objs := ac97.o snd-soc-ad1980-objs := ad1980.o +snd-soc-ad73311-objs := ad73311.o snd-soc-ak4535-objs := ak4535.o snd-soc-cs4270-objs := cs4270.o snd-soc-ssm2602-objs := ssm2602.o +snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic26-objs := tlv320aic26.o snd-soc-tlv320aic3x-objs := tlv320aic3x.o snd-soc-uda1380-objs := uda1380.o @@ -20,9 +22,11 @@ snd-soc-wm9713-objs := wm9713.o obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o +obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o +obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC26) += snd-soc-tlv320aic26.o obj-$(CONFIG_SND_SOC_TLV320AIC3X) += snd-soc-tlv320aic3x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c index 61fd96ca7bc..bd1ebdc6c86 100644 --- a/sound/soc/codecs/ac97.c +++ b/sound/soc/codecs/ac97.c @@ -2,8 +2,7 @@ * ac97.c -- ALSA Soc AC97 codec support * * Copyright 2005 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c index 4e09c1f2c06..1397b8e06c0 100644 --- a/sound/soc/codecs/ad1980.c +++ b/sound/soc/codecs/ad1980.c @@ -13,7 +13,6 @@ #include <linux/init.h> #include <linux/module.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/device.h> #include <sound/core.h> diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c new file mode 100644 index 00000000000..37af8607b00 --- /dev/null +++ b/sound/soc/codecs/ad73311.c @@ -0,0 +1,107 @@ +/* + * ad73311.c -- ALSA Soc AD73311 codec support + * + * Copyright: Analog Device Inc. + * Author: Cliff Cai <cliff.cai@analog.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * Revision history + * 25th Sep 2008 Initial version. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "ad73311.h" + +struct snd_soc_dai ad73311_dai = { + .name = "AD73311", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, +}; +EXPORT_SYMBOL_GPL(ad73311_dai); + +static int ad73311_soc_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + mutex_init(&codec->mutex); + codec->name = "AD73311"; + codec->owner = THIS_MODULE; + codec->dai = &ad73311_dai; + codec->num_dai = 1; + socdev->codec = codec; + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "ad73311: failed to create pcms\n"); + goto pcm_err; + } + + ret = snd_soc_register_card(socdev); + if (ret < 0) { + printk(KERN_ERR "ad73311: failed to register card\n"); + goto register_err; + } + + return ret; + +register_err: + snd_soc_free_pcms(socdev); +pcm_err: + kfree(socdev->codec); + socdev->codec = NULL; + return ret; +} + +static int ad73311_soc_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec == NULL) + return 0; + snd_soc_free_pcms(socdev); + kfree(codec); + return 0; +} + +struct snd_soc_codec_device soc_codec_dev_ad73311 = { + .probe = ad73311_soc_probe, + .remove = ad73311_soc_remove, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_ad73311); + +MODULE_DESCRIPTION("ASoC ad73311 driver"); +MODULE_AUTHOR("Cliff Cai "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h new file mode 100644 index 00000000000..507ce0c30ed --- /dev/null +++ b/sound/soc/codecs/ad73311.h @@ -0,0 +1,90 @@ +/* + * File: sound/soc/codec/ad73311.h + * Based on: + * Author: Cliff Cai <cliff.cai@analog.com> + * + * Created: Thur Sep 25, 2008 + * Description: definitions for AD73311 registers + * + * + * Modified: + * Copyright 2006 Analog Devices Inc. + * + * Bugs: Enter bugs at http://blackfin.uclinux.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AD73311_H__ +#define __AD73311_H__ + +#define AD_CONTROL 0x8000 +#define AD_DATA 0x0000 +#define AD_READ 0x4000 +#define AD_WRITE 0x0000 + +/* Control register A */ +#define CTRL_REG_A (0 << 8) + +#define REGA_MODE_PRO 0x00 +#define REGA_MODE_DATA 0x01 +#define REGA_MODE_MIXED 0x03 +#define REGA_DLB 0x04 +#define REGA_SLB 0x08 +#define REGA_DEVC(x) ((x & 0x7) << 4) +#define REGA_RESET 0x80 + +/* Control register B */ +#define CTRL_REG_B (1 << 8) + +#define REGB_DIRATE(x) (x & 0x3) +#define REGB_SCDIV(x) ((x & 0x3) << 2) +#define REGB_MCDIV(x) ((x & 0x7) << 4) +#define REGB_CEE (1 << 7) + +/* Control register C */ +#define CTRL_REG_C (2 << 8) + +#define REGC_PUDEV (1 << 0) +#define REGC_PUADC (1 << 3) +#define REGC_PUDAC (1 << 4) +#define REGC_PUREF (1 << 5) +#define REGC_REFUSE (1 << 6) + +/* Control register D */ +#define CTRL_REG_D (3 << 8) + +#define REGD_IGS(x) (x & 0x7) +#define REGD_RMOD (1 << 3) +#define REGD_OGS(x) ((x & 0x7) << 4) +#define REGD_MUTE (x << 7) + +/* Control register E */ +#define CTRL_REG_E (4 << 8) + +#define REGE_DA(x) (x & 0x1f) +#define REGE_IBYP (1 << 5) + +/* Control register F */ +#define CTRL_REG_F (5 << 8) + +#define REGF_SEEN (1 << 5) +#define REGF_INV (1 << 6) +#define REGF_ALB (1 << 7) + +extern struct snd_soc_dai ad73311_dai; +extern struct snd_soc_codec_device soc_codec_dev_ad73311; +#endif diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 088cf992772..2a89b5888e1 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -28,7 +28,6 @@ #include "ak4535.h" -#define AUDIO_NAME "ak4535" #define AK4535_VERSION "0.3" struct snd_soc_codec_device soc_codec_dev_ak4535; diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c index 940ce1c3522..44ef0dacd56 100644 --- a/sound/soc/codecs/ssm2602.c +++ b/sound/soc/codecs/ssm2602.c @@ -42,7 +42,6 @@ #include "ssm2602.h" -#define AUDIO_NAME "ssm2602" #define SSM2602_VERSION "0.1" struct snd_soc_codec_device soc_codec_dev_ssm2602; diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c new file mode 100644 index 00000000000..bac7815e00f --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.c @@ -0,0 +1,714 @@ +/* + * ALSA SoC TLV320AIC23 codec driver + * + * Author: Arun KS, <arunks@mistralsolutions.com> + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd., + * + * Based on sound/soc/codecs/wm8731.c by Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Notes: + * The AIC23 is a driver for a low power stereo audio + * codec tlv320aic23 + * + * The machine layer should disable unsupported inputs/outputs by + * snd_soc_dapm_disable_pin(codec, "LHPOUT"), etc. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <sound/initval.h> + +#include "tlv320aic23.h" + +#define AIC23_VERSION "0.1" + +struct tlv320aic23_srate_reg_info { + u32 sample_rate; + u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ + u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ +}; + +/* + * AIC23 register cache + */ +static const u16 tlv320aic23_reg[] = { + 0x0097, 0x0097, 0x00F9, 0x00F9, /* 0 */ + 0x001A, 0x0004, 0x0007, 0x0001, /* 4 */ + 0x0020, 0x0000, 0x0000, 0x0000, /* 8 */ + 0x0000, 0x0000, 0x0000, 0x0000, /* 12 */ +}; + +/* + * read tlv320aic23 register cache + */ +static inline unsigned int tlv320aic23_read_reg_cache(struct snd_soc_codec + *codec, unsigned int reg) +{ + u16 *cache = codec->reg_cache; + if (reg >= ARRAY_SIZE(tlv320aic23_reg)) + return -1; + return cache[reg]; +} + +/* + * write tlv320aic23 register cache + */ +static inline void tlv320aic23_write_reg_cache(struct snd_soc_codec *codec, + u8 reg, u16 value) +{ + u16 *cache = codec->reg_cache; + if (reg >= ARRAY_SIZE(tlv320aic23_reg)) + return; + cache[reg] = value; +} + +/* + * write to the tlv320aic23 register space + */ +static int tlv320aic23_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + + u8 data; + + /* TLV320AIC23 has 7 bit address and 9 bits of data + * so we need to switch one data bit into reg and rest + * of data into val + */ + + if ((reg < 0 || reg > 9) && (reg != 15)) { + printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg); + return -1; + } + + data = (reg << 1) | (value >> 8 & 0x01); + + tlv320aic23_write_reg_cache(codec, reg, value); + + if (codec->hw_write(codec->control_data, data, + (value & 0xff)) == 0) + return 0; + + printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__, + value, reg); + + return -EIO; +} + +static const char *rec_src_text[] = { "Line", "Mic" }; +static const char *deemph_text[] = {"None", "32Khz", "44.1Khz", "48Khz"}; + +static const struct soc_enum rec_src_enum = + SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); + +static const struct snd_kcontrol_new tlv320aic23_rec_src_mux_controls = +SOC_DAPM_ENUM("Input Select", rec_src_enum); + +static const struct soc_enum tlv320aic23_rec_src = + SOC_ENUM_SINGLE(TLV320AIC23_ANLG, 2, 2, rec_src_text); +static const struct soc_enum tlv320aic23_deemph = + SOC_ENUM_SINGLE(TLV320AIC23_DIGT, 1, 4, deemph_text); + +static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -12100, 100, 0); +static const DECLARE_TLV_DB_SCALE(input_gain_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(sidetone_vol_tlv, -1800, 300, 0); + +static int snd_soc_tlv320aic23_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 val, reg; + + val = (ucontrol->value.integer.value[0] & 0x07); + + /* linear conversion to userspace + * 000 = -6db + * 001 = -9db + * 010 = -12db + * 011 = -18db (Min) + * 100 = 0db (Max) + */ + val = (val >= 4) ? 4 : (3 - val); + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (~0x1C0); + tlv320aic23_write(codec, TLV320AIC23_ANLG, reg | (val << 6)); + + return 0; +} + +static int snd_soc_tlv320aic23_get_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + u16 val; + + val = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG) & (0x1C0); + val = val >> 6; + val = (val >= 4) ? 4 : (3 - val); + ucontrol->value.integer.value[0] = val; + return 0; + +} + +#define SOC_TLV320AIC23_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, .get = snd_soc_tlv320aic23_get_volsw,\ + .put = snd_soc_tlv320aic23_put_volsw, \ + .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + +static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = { + SOC_DOUBLE_R_TLV("Digital Playback Volume", TLV320AIC23_LCHNVOL, + TLV320AIC23_RCHNVOL, 0, 127, 0, out_gain_tlv), + SOC_SINGLE("Digital Playback Switch", TLV320AIC23_DIGT, 3, 1, 1), + SOC_DOUBLE_R("Line Input Switch", TLV320AIC23_LINVOL, + TLV320AIC23_RINVOL, 7, 1, 0), + SOC_DOUBLE_R_TLV("Line Input Volume", TLV320AIC23_LINVOL, + TLV320AIC23_RINVOL, 0, 31, 0, input_gain_tlv), + SOC_SINGLE("Mic Input Switch", TLV320AIC23_ANLG, 1, 1, 1), + SOC_SINGLE("Mic Booster Switch", TLV320AIC23_ANLG, 0, 1, 0), + SOC_TLV320AIC23_SINGLE_TLV("Sidetone Volume", TLV320AIC23_ANLG, + 6, 4, 0, sidetone_vol_tlv), + SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph), +}; + +/* add non dapm controls */ +static int tlv320aic23_add_controls(struct snd_soc_codec *codec) +{ + + int err, i; + + for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) { + err = snd_ctl_add(codec->card, + snd_soc_cnew(&tlv320aic23_snd_controls[i], + codec, NULL)); + if (err < 0) + return err; + } + + return 0; + +} + +/* PGA Mixer controls for Line and Mic switch */ +static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = { + SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0), + SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0), + SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0), +}; + +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1), + SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1), + SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0, + &tlv320aic23_rec_src_mux_controls), + SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1, + &tlv320aic23_output_mixer_controls[0], + ARRAY_SIZE(tlv320aic23_output_mixer_controls)), + SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0), + + SND_SOC_DAPM_OUTPUT("LHPOUT"), + SND_SOC_DAPM_OUTPUT("RHPOUT"), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), + + SND_SOC_DAPM_INPUT("LLINEIN"), + SND_SOC_DAPM_INPUT("RLINEIN"), + + SND_SOC_DAPM_INPUT("MICIN"), +}; + +static const struct snd_soc_dapm_route intercon[] = { + /* Output Mixer */ + {"Output Mixer", "Line Bypass Switch", "Line Input"}, + {"Output Mixer", "Playback Switch", "DAC"}, + {"Output Mixer", "Mic Sidetone Switch", "Mic Input"}, + + /* Outputs */ + {"RHPOUT", NULL, "Output Mixer"}, + {"LHPOUT", NULL, "Output Mixer"}, + {"LOUT", NULL, "Output Mixer"}, + {"ROUT", NULL, "Output Mixer"}, + + /* Inputs */ + {"Line Input", "NULL", "LLINEIN"}, + {"Line Input", "NULL", "RLINEIN"}, + + {"Mic Input", "NULL", "MICIN"}, + + /* input mux */ + {"Capture Source", "Line", "Line Input"}, + {"Capture Source", "Mic", "Mic Input"}, + {"ADC", NULL, "Capture Source"}, + +}; + +/* tlv320aic23 related */ +static const struct tlv320aic23_srate_reg_info srate_reg_info[] = { + {4000, 0x06, 1}, /* 4000 */ + {8000, 0x06, 0}, /* 8000 */ + {16000, 0x0C, 1}, /* 16000 */ + {22050, 0x11, 1}, /* 22050 */ + {24000, 0x00, 1}, /* 24000 */ + {32000, 0x0C, 0}, /* 32000 */ + {44100, 0x11, 0}, /* 44100 */ + {48000, 0x00, 0}, /* 48000 */ + {88200, 0x1F, 0}, /* 88200 */ + {96000, 0x0E, 0}, /* 96000 */ +}; + +static int tlv320aic23_add_widgets(struct snd_soc_codec *codec) +{ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* set up audio path interconnects */ + snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon)); + + snd_soc_dapm_new_widgets(codec); + return 0; +} + +static int tlv320aic23_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + u16 iface_reg, data; + u8 count = 0; + + iface_reg = + tlv320aic23_read_reg_cache(codec, + TLV320AIC23_DIGT_FMT) & ~(0x03 << 2); + + /* Search for the right sample rate */ + /* Verify what happens if the rate is not supported + * now it goes to 96Khz */ + while ((srate_reg_info[count].sample_rate != params_rate(params)) && + (count < ARRAY_SIZE(srate_reg_info))) { + count++; + } + + data = (srate_reg_info[count].divider << TLV320AIC23_CLKIN_SHIFT) | + (srate_reg_info[count]. control << TLV320AIC23_BOSR_SHIFT) | + TLV320AIC23_USB_CLK_ON; + + tlv320aic23_write(codec, TLV320AIC23_SRATE, data); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + iface_reg |= (0x01 << 2); + break; + case SNDRV_PCM_FORMAT_S24_LE: + iface_reg |= (0x02 << 2); + break; + case SNDRV_PCM_FORMAT_S32_LE: + iface_reg |= (0x03 << 2); + break; + } + tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg); + + return 0; +} + +static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + + /* set active */ + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001); + + return 0; +} + +static void tlv320aic23_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_device *socdev = rtd->socdev; + struct snd_soc_codec *codec = socdev->codec; + + /* deactivate */ + if (!codec->active) { + udelay(50); + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); + } +} + +static int tlv320aic23_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + u16 reg; + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT); + if (mute) + reg |= TLV320AIC23_DACM_MUTE; + + else + reg &= ~TLV320AIC23_DACM_MUTE; + + tlv320aic23_write(codec, TLV320AIC23_DIGT, reg); + + return 0; +} + +static int tlv320aic23_set_dai_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + u16 iface_reg; + + iface_reg = + tlv320aic23_read_reg_cache(codec, TLV320AIC23_DIGT_FMT) & (~0x03); + + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + iface_reg |= TLV320AIC23_MS_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + iface_reg |= TLV320AIC23_FOR_I2S; + break; + case SND_SOC_DAIFMT_DSP_A: + iface_reg |= TLV320AIC23_FOR_DSP; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + iface_reg |= TLV320AIC23_FOR_LJUST; + break; + default: + return -EINVAL; + + } + + tlv320aic23_write(codec, TLV320AIC23_DIGT_FMT, iface_reg); + + return 0; +} + +static int tlv320aic23_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = codec_dai->codec; + + switch (freq) { + case 12000000: + return 0; + } + return -EINVAL; +} + +static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + u16 reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_PWR) & 0xff7f; + + switch (level) { + case SND_SOC_BIAS_ON: + /* vref/mid, osc on, dac unmute */ + tlv320aic23_write(codec, TLV320AIC23_PWR, reg); + break; + case SND_SOC_BIAS_PREPARE: + break; + case SND_SOC_BIAS_STANDBY: + /* everything off except vref/vmid, */ + tlv320aic23_write(codec, TLV320AIC23_PWR, reg | 0x0040); + break; + case SND_SOC_BIAS_OFF: + /* everything off, dac mute, inactive */ + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); + tlv320aic23_write(codec, TLV320AIC23_PWR, 0xffff); + break; + } + codec->bias_level = level; + return 0; +} + +#define AIC23_RATES SNDRV_PCM_RATE_8000_96000 +#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct snd_soc_dai tlv320aic23_dai = { + .name = "tlv320aic23", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = AIC23_RATES, + .formats = AIC23_FORMATS,}, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = AIC23_RATES, + .formats = AIC23_FORMATS,}, + .ops = { + .prepare = tlv320aic23_pcm_prepare, + .hw_params = tlv320aic23_hw_params, + .shutdown = tlv320aic23_shutdown, + }, + .dai_ops = { + .digital_mute = tlv320aic23_mute, + .set_fmt = tlv320aic23_set_dai_fmt, + .set_sysclk = tlv320aic23_set_dai_sysclk, + } +}; +EXPORT_SYMBOL_GPL(tlv320aic23_dai); + +static int tlv320aic23_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0); + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); + + return 0; +} + +static int tlv320aic23_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + int i; + u16 reg; + + /* Sync reg_cache with the hardware */ + for (reg = 0; reg < ARRAY_SIZE(tlv320aic23_reg); i++) { + u16 val = tlv320aic23_read_reg_cache(codec, reg); + tlv320aic23_write(codec, reg, val); + } + + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + tlv320aic23_set_bias_level(codec, codec->suspend_bias_level); + + return 0; +} + +/* + * initialise the AIC23 driver + * register the mixer and dsp interfaces with the kernel + */ +static int tlv320aic23_init(struct snd_soc_device *socdev) +{ + struct snd_soc_codec *codec = socdev->codec; + int ret = 0; + u16 reg; + + codec->name = "tlv320aic23"; + codec->owner = THIS_MODULE; + codec->read = tlv320aic23_read_reg_cache; + codec->write = tlv320aic23_write; + codec->set_bias_level = tlv320aic23_set_bias_level; + codec->dai = &tlv320aic23_dai; + codec->num_dai = 1; + codec->reg_cache_size = ARRAY_SIZE(tlv320aic23_reg); + codec->reg_cache = + kmemdup(tlv320aic23_reg, sizeof(tlv320aic23_reg), GFP_KERNEL); + if (codec->reg_cache == NULL) + return -ENOMEM; + + /* Reset codec */ + tlv320aic23_write(codec, TLV320AIC23_RESET, 0); + + /* register pcms */ + ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); + if (ret < 0) { + printk(KERN_ERR "tlv320aic23: failed to create pcms\n"); + goto pcm_err; + } + + /* power on device */ + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + + tlv320aic23_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K); + + /* Unmute input */ + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_LINVOL); + tlv320aic23_write(codec, TLV320AIC23_LINVOL, + (reg & (~TLV320AIC23_LIM_MUTED)) | + (TLV320AIC23_LRS_ENABLED)); + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_RINVOL); + tlv320aic23_write(codec, TLV320AIC23_RINVOL, + (reg & (~TLV320AIC23_LIM_MUTED)) | + TLV320AIC23_LRS_ENABLED); + + reg = tlv320aic23_read_reg_cache(codec, TLV320AIC23_ANLG); + tlv320aic23_write(codec, TLV320AIC23_ANLG, + (reg) & (~TLV320AIC23_BYPASS_ON) & + (~TLV320AIC23_MICM_MUTED)); + + /* Default output volume */ + tlv320aic23_write(codec, TLV320AIC23_LCHNVOL, + TLV320AIC23_DEFAULT_OUT_VOL & + TLV320AIC23_OUT_VOL_MASK); + tlv320aic23_write(codec, TLV320AIC23_RCHNVOL, + TLV320AIC23_DEFAULT_OUT_VOL & + TLV320AIC23_OUT_VOL_MASK); + + tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1); + + tlv320aic23_add_controls(codec); + tlv320aic23_add_widgets(codec); + ret = snd_soc_register_card(socdev); + if (ret < 0) { + printk(KERN_ERR "tlv320aic23: failed to register card\n"); + goto card_err; + } + + return ret; + +card_err: + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +pcm_err: + kfree(codec->reg_cache); + return ret; +} +static struct snd_soc_device *tlv320aic23_socdev; + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +/* + * If the i2c layer weren't so broken, we could pass this kind of data + * around + */ +static int tlv320aic23_codec_probe(struct i2c_client *i2c, + const struct i2c_device_id *i2c_id) +{ + struct snd_soc_device *socdev = tlv320aic23_socdev; + struct snd_soc_codec *codec = socdev->codec; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EINVAL; + + i2c_set_clientdata(i2c, codec); + codec->control_data = i2c; + + ret = tlv320aic23_init(socdev); + if (ret < 0) { + printk(KERN_ERR "tlv320aic23: failed to initialise AIC23\n"); + goto err; + } + return ret; + +err: + kfree(codec); + kfree(i2c); + return ret; +} +static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) +{ + put_device(&i2c->dev); + return 0; +} + +static const struct i2c_device_id tlv320aic23_id[] = { + {"tlv320aic23", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); + +static struct i2c_driver tlv320aic23_i2c_driver = { + .driver = { + .name = "tlv320aic23", + }, + .probe = tlv320aic23_codec_probe, + .remove = __exit_p(tlv320aic23_i2c_remove), + .id_table = tlv320aic23_id, +}; + +#endif + +static int tlv320aic23_probe(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + int ret = 0; + + printk(KERN_INFO "AIC23 Audio Codec %s\n", AIC23_VERSION); + + codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); + if (codec == NULL) + return -ENOMEM; + + socdev->codec = codec; + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + tlv320aic23_socdev = socdev; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + codec->hw_write = (hw_write_t) i2c_smbus_write_byte_data; + codec->hw_read = NULL; + ret = i2c_add_driver(&tlv320aic23_i2c_driver); + if (ret != 0) + printk(KERN_ERR "can't add i2c driver"); +#endif + return ret; +} + +static int tlv320aic23_remove(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec = socdev->codec; + + if (codec->control_data) + tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF); + + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&tlv320aic23_i2c_driver); +#endif + kfree(codec->reg_cache); + kfree(codec); + + return 0; +} +struct snd_soc_codec_device soc_codec_dev_tlv320aic23 = { + .probe = tlv320aic23_probe, + .remove = tlv320aic23_remove, + .suspend = tlv320aic23_suspend, + .resume = tlv320aic23_resume, +}; +EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320aic23); + +MODULE_DESCRIPTION("ASoC TLV320AIC23 codec driver"); +MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tlv320aic23.h b/sound/soc/codecs/tlv320aic23.h new file mode 100644 index 00000000000..79d1faf8e57 --- /dev/null +++ b/sound/soc/codecs/tlv320aic23.h @@ -0,0 +1,122 @@ +/* + * ALSA SoC TLV320AIC23 codec driver + * + * Author: Arun KS, <arunks@mistralsolutions.com> + * Copyright: (C) 2008 Mistral Solutions Pvt Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _TLV320AIC23_H +#define _TLV320AIC23_H + +/* Codec TLV320AIC23 */ +#define TLV320AIC23_LINVOL 0x00 +#define TLV320AIC23_RINVOL 0x01 +#define TLV320AIC23_LCHNVOL 0x02 +#define TLV320AIC23_RCHNVOL 0x03 +#define TLV320AIC23_ANLG 0x04 +#define TLV320AIC23_DIGT 0x05 +#define TLV320AIC23_PWR 0x06 +#define TLV320AIC23_DIGT_FMT 0x07 +#define TLV320AIC23_SRATE 0x08 +#define TLV320AIC23_ACTIVE 0x09 +#define TLV320AIC23_RESET 0x0F + +/* Left (right) line input volume control register */ +#define TLV320AIC23_LRS_ENABLED 0x0100 +#define TLV320AIC23_LIM_MUTED 0x0080 +#define TLV320AIC23_LIV_DEFAULT 0x0017 +#define TLV320AIC23_LIV_MAX 0x001f +#define TLV320AIC23_LIV_MIN 0x0000 + +/* Left (right) channel headphone volume control register */ +#define TLV320AIC23_LZC_ON 0x0080 +#define TLV320AIC23_LHV_DEFAULT 0x0079 +#define TLV320AIC23_LHV_MAX 0x007f +#define TLV320AIC23_LHV_MIN 0x0000 + +/* Analog audio path control register */ +#define TLV320AIC23_STA_REG(x) ((x)<<6) +#define TLV320AIC23_STE_ENABLED 0x0020 +#define TLV320AIC23_DAC_SELECTED 0x0010 +#define TLV320AIC23_BYPASS_ON 0x0008 +#define TLV320AIC23_INSEL_MIC 0x0004 +#define TLV320AIC23_MICM_MUTED 0x0002 +#define TLV320AIC23_MICB_20DB 0x0001 + +/* Digital audio path control register */ +#define TLV320AIC23_DACM_MUTE 0x0008 +#define TLV320AIC23_DEEMP_32K 0x0002 +#define TLV320AIC23_DEEMP_44K 0x0004 +#define TLV320AIC23_DEEMP_48K 0x0006 +#define TLV320AIC23_ADCHP_ON 0x0001 + +/* Power control down register */ +#define TLV320AIC23_DEVICE_PWR_OFF 0x0080 +#define TLV320AIC23_CLK_OFF 0x0040 +#define TLV320AIC23_OSC_OFF 0x0020 +#define TLV320AIC23_OUT_OFF 0x0010 +#define TLV320AIC23_DAC_OFF 0x0008 +#define TLV320AIC23_ADC_OFF 0x0004 +#define TLV320AIC23_MIC_OFF 0x0002 +#define TLV320AIC23_LINE_OFF 0x0001 + +/* Digital audio interface register */ +#define TLV320AIC23_MS_MASTER 0x0040 +#define TLV320AIC23_LRSWAP_ON 0x0020 +#define TLV320AIC23_LRP_ON 0x0010 +#define TLV320AIC23_IWL_16 0x0000 +#define TLV320AIC23_IWL_20 0x0004 +#define TLV320AIC23_IWL_24 0x0008 +#define TLV320AIC23_IWL_32 0x000C +#define TLV320AIC23_FOR_I2S 0x0002 +#define TLV320AIC23_FOR_DSP 0x0003 +#define TLV320AIC23_FOR_LJUST 0x0001 + +/* Sample rate control register */ +#define TLV320AIC23_CLKOUT_HALF 0x0080 +#define TLV320AIC23_CLKIN_HALF 0x0040 +#define TLV320AIC23_BOSR_384fs 0x0002 /* BOSR_272fs in USB mode */ +#define TLV320AIC23_USB_CLK_ON 0x0001 +#define TLV320AIC23_SR_MASK 0xf +#define TLV320AIC23_CLKOUT_SHIFT 7 +#define TLV320AIC23_CLKIN_SHIFT 6 +#define TLV320AIC23_SR_SHIFT 2 +#define TLV320AIC23_BOSR_SHIFT 1 + +/* Digital interface register */ +#define TLV320AIC23_ACT_ON 0x0001 + +/* + * AUDIO related MACROS + */ + +#define TLV320AIC23_DEFAULT_OUT_VOL 0x70 +#define TLV320AIC23_DEFAULT_IN_VOLUME 0x10 + +#define TLV320AIC23_OUT_VOL_MIN TLV320AIC23_LHV_MIN +#define TLV320AIC23_OUT_VOL_MAX TLV320AIC23_LHV_MAX +#define TLV320AIC23_OUT_VO_RANGE (TLV320AIC23_OUT_VOL_MAX - \ + TLV320AIC23_OUT_VOL_MIN) +#define TLV320AIC23_OUT_VOL_MASK TLV320AIC23_OUT_VOL_MAX + +#define TLV320AIC23_IN_VOL_MIN TLV320AIC23_LIV_MIN +#define TLV320AIC23_IN_VOL_MAX TLV320AIC23_LIV_MAX +#define TLV320AIC23_IN_VOL_RANGE (TLV320AIC23_IN_VOL_MAX - \ + TLV320AIC23_IN_VOL_MIN) +#define TLV320AIC23_IN_VOL_MASK TLV320AIC23_IN_VOL_MAX + +#define TLV320AIC23_SIDETONE_MASK 0x1c0 +#define TLV320AIC23_SIDETONE_0 0x100 +#define TLV320AIC23_SIDETONE_6 0x000 +#define TLV320AIC23_SIDETONE_9 0x040 +#define TLV320AIC23_SIDETONE_12 0x080 +#define TLV320AIC23_SIDETONE_18 0x0c0 + +extern struct snd_soc_dai tlv320aic23_dai; +extern struct snd_soc_codec_device soc_codec_dev_tlv320aic23; + +#endif /* _TLV320AIC23_H */ diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 566a427c928..05336ed7e49 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -48,7 +48,6 @@ #include "tlv320aic3x.h" -#define AUDIO_NAME "aic3x" #define AIC3X_VERSION "0.2" /* codec private data */ @@ -991,7 +990,7 @@ EXPORT_SYMBOL_GPL(aic3x_headset_detected); SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) struct snd_soc_dai aic3x_dai = { - .name = "aic3x", + .name = "tlv320aic3x", .playback = { .stream_name = "Playback", .channels_min = 1, @@ -1055,7 +1054,7 @@ static int aic3x_init(struct snd_soc_device *socdev) struct aic3x_setup_data *setup = socdev->codec_data; int reg, ret = 0; - codec->name = "aic3x"; + codec->name = "tlv320aic3x"; codec->owner = THIS_MODULE; codec->read = aic3x_read_reg_cache; codec->write = aic3x_write; diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index d206d7f892b..a69ee72a7af 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -36,7 +36,6 @@ #include "uda1380.h" #define UDA1380_VERSION "0.6" -#define AUDIO_NAME "uda1380" /* * uda1380 register cache diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index 9a37c8d95ed..d8ca2da8d63 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -3,7 +3,7 @@ * * Copyright 2006 Wolfson Microelectronics PLC. * - * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,6 +18,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/spi/spi.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -27,7 +28,6 @@ #include "wm8510.h" -#define AUDIO_NAME "wm8510" #define WM8510_VERSION "0.6" struct snd_soc_codec_device soc_codec_dev_wm8510; @@ -55,6 +55,9 @@ static const u16 wm8510_reg[WM8510_CACHEREGNUM] = { 0x0001, }; +#define WM8510_POWER1_BIASEN 0x08 +#define WM8510_POWER1_BUFIOEN 0x10 + /* * read wm8510 register cache */ @@ -224,9 +227,9 @@ SND_SOC_DAPM_PGA("SpkN Out", WM8510_POWER3, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("SpkP Out", WM8510_POWER3, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("Mono Out", WM8510_POWER3, 7, 0, NULL, 0), -SND_SOC_DAPM_PGA("Mic PGA", WM8510_POWER2, 2, 0, - &wm8510_micpga_controls[0], - ARRAY_SIZE(wm8510_micpga_controls)), +SND_SOC_DAPM_MIXER("Mic PGA", WM8510_POWER2, 2, 0, + &wm8510_micpga_controls[0], + ARRAY_SIZE(wm8510_micpga_controls)), SND_SOC_DAPM_MIXER("Boost Mixer", WM8510_POWER2, 4, 0, &wm8510_boost_controls[0], ARRAY_SIZE(wm8510_boost_controls)), @@ -526,23 +529,35 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute) static int wm8510_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3; switch (level) { case SND_SOC_BIAS_ON: - wm8510_write(codec, WM8510_POWER1, 0x1ff); - wm8510_write(codec, WM8510_POWER2, 0x1ff); - wm8510_write(codec, WM8510_POWER3, 0x1ff); - break; case SND_SOC_BIAS_PREPARE: + power1 |= 0x1; /* VMID 50k */ + wm8510_write(codec, WM8510_POWER1, power1); + break; + case SND_SOC_BIAS_STANDBY: + power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; + + if (codec->bias_level == SND_SOC_BIAS_OFF) { + /* Initial cap charge at VMID 5k */ + wm8510_write(codec, WM8510_POWER1, power1 | 0x3); + mdelay(100); + } + + power1 |= 0x2; /* VMID 500k */ + wm8510_write(codec, WM8510_POWER1, power1); break; + case SND_SOC_BIAS_OFF: - /* everything off, dac mute, inactive */ - wm8510_write(codec, WM8510_POWER1, 0x0); - wm8510_write(codec, WM8510_POWER2, 0x0); - wm8510_write(codec, WM8510_POWER3, 0x0); + wm8510_write(codec, WM8510_POWER1, 0); + wm8510_write(codec, WM8510_POWER2, 0); + wm8510_write(codec, WM8510_POWER3, 0); break; } + codec->bias_level = level; return 0; } @@ -640,6 +655,7 @@ static int wm8510_init(struct snd_soc_device *socdev) } /* power on device */ + codec->bias_level = SND_SOC_BIAS_OFF; wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm8510_add_controls(codec); wm8510_add_widgets(codec); @@ -747,6 +763,62 @@ err_driver: } #endif +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8510_spi_probe(struct spi_device *spi) +{ + struct snd_soc_device *socdev = wm8510_socdev; + struct snd_soc_codec *codec = socdev->codec; + int ret; + + codec->control_data = spi; + + ret = wm8510_init(socdev); + if (ret < 0) + dev_err(&spi->dev, "failed to initialise WM8510\n"); + + return ret; +} + +static int __devexit wm8510_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver wm8510_spi_driver = { + .driver = { + .name = "wm8510", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm8510_spi_probe, + .remove = __devexit_p(wm8510_spi_remove), +}; + +static int wm8510_spi_write(struct spi_device *spi, const char *data, int len) +{ + struct spi_transfer t; + struct spi_message m; + u8 msg[2]; + + if (len <= 0) + return 0; + + msg[0] = data[0]; + msg[1] = data[1]; + + spi_message_init(&m); + memset(&t, 0, (sizeof t)); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; +} +#endif /* CONFIG_SPI_MASTER */ + static int wm8510_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -772,8 +844,14 @@ static int wm8510_probe(struct platform_device *pdev) codec->hw_write = (hw_write_t)i2c_master_send; ret = wm8510_add_i2c_device(pdev, setup); } -#else - /* Add other interfaces here */ +#endif +#if defined(CONFIG_SPI_MASTER) + if (setup->spi) { + codec->hw_write = (hw_write_t)wm8510_spi_write; + ret = spi_register_driver(&wm8510_spi_driver); + if (ret != 0) + printk(KERN_ERR "can't add spi driver"); + } #endif if (ret != 0) @@ -796,6 +874,9 @@ static int wm8510_remove(struct platform_device *pdev) i2c_unregister_device(codec->control_data); i2c_del_driver(&wm8510_i2c_driver); #endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8510_spi_driver); +#endif kfree(codec); return 0; diff --git a/sound/soc/codecs/wm8510.h b/sound/soc/codecs/wm8510.h index c5368396045..bdefcf5c69f 100644 --- a/sound/soc/codecs/wm8510.h +++ b/sound/soc/codecs/wm8510.h @@ -94,6 +94,7 @@ #define WM8510_MCLKDIV_12 (7 << 5) struct wm8510_setup_data { + int spi; int i2c_bus; unsigned short i2c_address; }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index df1ffbe305b..627ebfb4209 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -18,7 +18,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> @@ -36,7 +35,6 @@ #include "wm8580.h" -#define AUDIO_NAME "wm8580" #define WM8580_VERSION "0.1" struct pll_state { diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 7b64d9a7ff7..7f8a7e36b33 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -29,7 +29,6 @@ #include "wm8731.h" -#define AUDIO_NAME "wm8731" #define WM8731_VERSION "0.13" struct snd_soc_codec_device soc_codec_dev_wm8731; diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 4892e398a59..9b7296ee5b0 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -29,7 +29,6 @@ #include "wm8750.h" -#define AUDIO_NAME "WM8750" #define WM8750_VERSION "0.12" /* codec private data */ diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 8c4df44f334..d426eaa2218 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -2,8 +2,7 @@ * wm8753.c -- WM8753 ALSA Soc Audio driver * * Copyright 2003 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -40,6 +39,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/platform_device.h> +#include <linux/spi/spi.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -51,7 +51,6 @@ #include "wm8753.h" -#define AUDIO_NAME "wm8753" #define WM8753_VERSION "0.16" static int caps_charge = 2000; @@ -1719,6 +1718,63 @@ err_driver: } #endif +#if defined(CONFIG_SPI_MASTER) +static int __devinit wm8753_spi_probe(struct spi_device *spi) +{ + struct snd_soc_device *socdev = wm8753_socdev; + struct snd_soc_codec *codec = socdev->codec; + int ret; + + codec->control_data = spi; + + ret = wm8753_init(socdev); + if (ret < 0) + dev_err(&spi->dev, "failed to initialise WM8753\n"); + + return ret; +} + +static int __devexit wm8753_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct spi_driver wm8753_spi_driver = { + .driver = { + .name = "wm8753", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = wm8753_spi_probe, + .remove = __devexit_p(wm8753_spi_remove), +}; + +static int wm8753_spi_write(struct spi_device *spi, const char *data, int len) +{ + struct spi_transfer t; + struct spi_message m; + u8 msg[2]; + + if (len <= 0) + return 0; + + msg[0] = data[0]; + msg[1] = data[1]; + + spi_message_init(&m); + memset(&t, 0, (sizeof t)); + + t.tx_buf = &msg[0]; + t.len = len; + + spi_message_add_tail(&t, &m); + spi_sync(spi, &m); + + return len; +} +#endif + + static int wm8753_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); @@ -1753,8 +1809,14 @@ static int wm8753_probe(struct platform_device *pdev) codec->hw_write = (hw_write_t)i2c_master_send; ret = wm8753_add_i2c_device(pdev, setup); } -#else - /* Add other interfaces here */ +#endif +#if defined(CONFIG_SPI_MASTER) + if (setup->spi) { + codec->hw_write = (hw_write_t)wm8753_spi_write; + ret = spi_register_driver(&wm8753_spi_driver); + if (ret != 0) + printk(KERN_ERR "can't add spi driver"); + } #endif if (ret != 0) { @@ -1798,6 +1860,9 @@ static int wm8753_remove(struct platform_device *pdev) i2c_unregister_device(codec->control_data); i2c_del_driver(&wm8753_i2c_driver); #endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8753_spi_driver); +#endif kfree(codec->private_data); kfree(codec); diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h index 7defde069f1..f55704ce931 100644 --- a/sound/soc/codecs/wm8753.h +++ b/sound/soc/codecs/wm8753.h @@ -2,8 +2,7 @@ * wm8753.h -- audio driver for WM8753 * * Copyright 2003 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -79,6 +78,7 @@ #define WM8753_ADCTL2 0x3f struct wm8753_setup_data { + int spi; int i2c_bus; unsigned short i2c_address; }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 0b8c6d38b48..3b326c9b558 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -18,7 +18,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/version.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index a3f54ec4226..ce40d787760 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -653,14 +653,14 @@ static const struct snd_kcontrol_new wm8903_snd_controls[] = { /* Input PGAs - No TLV since the scale depends on PGA mode */ SOC_SINGLE("Left Input PGA Switch", WM8903_ANALOGUE_LEFT_INPUT_0, - 7, 1, 0), + 7, 1, 1), SOC_SINGLE("Left Input PGA Volume", WM8903_ANALOGUE_LEFT_INPUT_0, 0, 31, 0), SOC_SINGLE("Left Input PGA Common Mode Switch", WM8903_ANALOGUE_LEFT_INPUT_1, 6, 1, 0), SOC_SINGLE("Right Input PGA Switch", WM8903_ANALOGUE_RIGHT_INPUT_0, - 7, 1, 0), + 7, 1, 1), SOC_SINGLE("Right Input PGA Volume", WM8903_ANALOGUE_RIGHT_INPUT_0, 0, 31, 0), SOC_SINGLE("Right Input PGA Common Mode Switch", WM8903_ANALOGUE_RIGHT_INPUT_1, diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 974a4cd0f3f..f41a578ddd4 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -29,7 +29,6 @@ #include "wm8971.h" -#define AUDIO_NAME "wm8971" #define WM8971_VERSION "0.9" #define WM8971_REG_COUNT 43 diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 63410d7b5ef..572d22b0880 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -30,7 +30,6 @@ #include "wm8990.h" -#define AUDIO_NAME "wm8990" #define WM8990_VERSION "0.2" /* codec private data */ diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 2f1c91b1d55..ffb471e420e 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -2,8 +2,7 @@ * wm9712.c -- ALSA Soc WM9712 codec support * * Copyright 2006 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 441d0580db1..aba402b3c99 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -2,8 +2,7 @@ * wm9713.c -- ALSA Soc WM9713 codec support * * Copyright 2006 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index aea27e70043..8b7766b998d 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -13,3 +13,11 @@ config SND_OMAP_SOC_N810 select SND_SOC_TLV320AIC3X help Say Y if you want to add support for SoC audio on Nokia N810. + +config SND_OMAP_SOC_OSK5912 + tristate "SoC Audio support for omap osk5912" + depends on SND_OMAP_SOC && MACH_OMAP_OSK + select SND_OMAP_SOC_MCBSP + select SND_SOC_TLV320AIC23 + help + Say Y if you want to add support for SoC audio on osk5912. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index d8d8d58075e..e09d1f297f6 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -7,5 +7,7 @@ obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o # OMAP Machine Support snd-soc-n810-objs := n810.o +snd-soc-osk5912-objs := osk5912.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o +obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index d166b6b2a60..fae3ad36e0b 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -247,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec) int i, err; /* Not connected */ - snd_soc_dapm_disable_pin(codec, "MONO_LOUT"); - snd_soc_dapm_disable_pin(codec, "HPLCOM"); - snd_soc_dapm_disable_pin(codec, "HPRCOM"); + snd_soc_dapm_nc_pin(codec, "MONO_LOUT"); + snd_soc_dapm_nc_pin(codec, "HPLCOM"); + snd_soc_dapm_nc_pin(codec, "HPRCOM"); /* Add N810 specific controls */ for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) { diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 35310e16d7f..0a063a98a66 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -59,12 +59,7 @@ static struct omap_mcbsp_data mcbsp_data[NUM_LINKS]; * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs */ -static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2] = { -{ - { .name = "I2S PCM Stereo out", }, - { .name = "I2S PCM Stereo in", }, -}, -}; +static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2]; #if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) static const int omap1_dma_reqs[][2] = { @@ -84,11 +79,22 @@ static const unsigned long omap1_mcbsp_port[][2] = { static const int omap1_dma_reqs[][2] = {}; static const unsigned long omap1_mcbsp_port[][2] = {}; #endif -#if defined(CONFIG_ARCH_OMAP2420) -static const int omap2420_dma_reqs[][2] = { + +#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) +static const int omap24xx_dma_reqs[][2] = { { OMAP24XX_DMA_MCBSP1_TX, OMAP24XX_DMA_MCBSP1_RX }, { OMAP24XX_DMA_MCBSP2_TX, OMAP24XX_DMA_MCBSP2_RX }, +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) + { OMAP24XX_DMA_MCBSP3_TX, OMAP24XX_DMA_MCBSP3_RX }, + { OMAP24XX_DMA_MCBSP4_TX, OMAP24XX_DMA_MCBSP4_RX }, + { OMAP24XX_DMA_MCBSP5_TX, OMAP24XX_DMA_MCBSP5_RX }, +#endif }; +#else +static const int omap24xx_dma_reqs[][2] = {}; +#endif + +#if defined(CONFIG_ARCH_OMAP2420) static const unsigned long omap2420_mcbsp_port[][2] = { { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1, OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1 }, @@ -96,10 +102,43 @@ static const unsigned long omap2420_mcbsp_port[][2] = { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1 }, }; #else -static const int omap2420_dma_reqs[][2] = {}; static const unsigned long omap2420_mcbsp_port[][2] = {}; #endif +#if defined(CONFIG_ARCH_OMAP2430) +static const unsigned long omap2430_mcbsp_port[][2] = { + { OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, + OMAP24XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, + OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, + OMAP2430_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, + OMAP2430_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, + OMAP2430_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, +}; +#else +static const unsigned long omap2430_mcbsp_port[][2] = {}; +#endif + +#if defined(CONFIG_ARCH_OMAP34XX) +static const unsigned long omap34xx_mcbsp_port[][2] = { + { OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP1_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP3_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP4_BASE + OMAP_MCBSP_REG_DRR }, + { OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DXR, + OMAP34XX_MCBSP5_BASE + OMAP_MCBSP_REG_DRR }, +}; +#else +static const unsigned long omap34xx_mcbsp_port[][2] = {}; +#endif + static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -167,14 +206,19 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, dma = omap1_dma_reqs[bus_id][substream->stream]; port = omap1_mcbsp_port[bus_id][substream->stream]; } else if (cpu_is_omap2420()) { - dma = omap2420_dma_reqs[bus_id][substream->stream]; + dma = omap24xx_dma_reqs[bus_id][substream->stream]; port = omap2420_mcbsp_port[bus_id][substream->stream]; + } else if (cpu_is_omap2430()) { + dma = omap24xx_dma_reqs[bus_id][substream->stream]; + port = omap2430_mcbsp_port[bus_id][substream->stream]; + } else if (cpu_is_omap343x()) { + dma = omap24xx_dma_reqs[bus_id][substream->stream]; + port = omap34xx_mcbsp_port[bus_id][substream->stream]; } else { - /* - * TODO: Add support for 2430 and 3430 - */ return -ENODEV; } + omap_mcbsp_dai_dma_params[id][substream->stream].name = + substream->stream ? "Audio Capture" : "Audio Playback"; omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma; omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port; cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream]; @@ -245,6 +289,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, regs->rcr2 |= RDATDLY(1); regs->xcr2 |= XDATDLY(1); break; + case SND_SOC_DAIFMT_DSP_A: + /* 0-bit data delay */ + regs->rcr2 |= RDATDLY(0); + regs->xcr2 |= XDATDLY(0); + break; default: /* Unsupported data format */ return -EINVAL; @@ -310,7 +359,7 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, int clk_id) { int sel_bit; - u16 reg; + u16 reg, reg_devconf1 = OMAP243X_CONTROL_DEVCONF1; if (cpu_class_is_omap1()) { /* OMAP1's can use only external source clock */ @@ -320,6 +369,12 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, return 0; } + if (cpu_is_omap2420() && mcbsp_data->bus_id > 1) + return -EINVAL; + + if (cpu_is_omap343x()) + reg_devconf1 = OMAP343X_CONTROL_DEVCONF1; + switch (mcbsp_data->bus_id) { case 0: reg = OMAP2_CONTROL_DEVCONF0; @@ -329,20 +384,26 @@ static int omap_mcbsp_dai_set_clks_src(struct omap_mcbsp_data *mcbsp_data, reg = OMAP2_CONTROL_DEVCONF0; sel_bit = 6; break; - /* TODO: Support for ports 3 - 5 in OMAP2430 and OMAP34xx */ + case 2: + reg = reg_devconf1; + sel_bit = 0; + break; + case 3: + reg = reg_devconf1; + sel_bit = 2; + break; + case 4: + reg = reg_devconf1; + sel_bit = 4; + break; default: return -EINVAL; } - if (cpu_class_is_omap2()) { - if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) { - omap_ctrl_writel(omap_ctrl_readl(reg) & - ~(1 << sel_bit), reg); - } else { - omap_ctrl_writel(omap_ctrl_readl(reg) | - (1 << sel_bit), reg); - } - } + if (clk_id == OMAP_MCBSP_SYSCLK_CLKS_FCLK) + omap_ctrl_writel(omap_ctrl_readl(reg) & ~(1 << sel_bit), reg); + else + omap_ctrl_writel(omap_ctrl_readl(reg) | (1 << sel_bit), reg); return 0; } @@ -376,37 +437,49 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, return err; } -struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS] = { -{ - .name = "omap-mcbsp-dai", - .id = 0, - .type = SND_SOC_DAI_I2S, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = OMAP_MCBSP_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = OMAP_MCBSP_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = { - .startup = omap_mcbsp_dai_startup, - .shutdown = omap_mcbsp_dai_shutdown, - .trigger = omap_mcbsp_dai_trigger, - .hw_params = omap_mcbsp_dai_hw_params, - }, - .dai_ops = { - .set_fmt = omap_mcbsp_dai_set_dai_fmt, - .set_clkdiv = omap_mcbsp_dai_set_clkdiv, - .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, - }, - .private_data = &mcbsp_data[0].bus_id, -}, +#define OMAP_MCBSP_DAI_BUILDER(link_id) \ +{ \ + .name = "omap-mcbsp-dai-(link_id)", \ + .id = (link_id), \ + .type = SND_SOC_DAI_I2S, \ + .playback = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = OMAP_MCBSP_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .capture = { \ + .channels_min = 2, \ + .channels_max = 2, \ + .rates = OMAP_MCBSP_RATES, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE, \ + }, \ + .ops = { \ + .startup = omap_mcbsp_dai_startup, \ + .shutdown = omap_mcbsp_dai_shutdown, \ + .trigger = omap_mcbsp_dai_trigger, \ + .hw_params = omap_mcbsp_dai_hw_params, \ + }, \ + .dai_ops = { \ + .set_fmt = omap_mcbsp_dai_set_dai_fmt, \ + .set_clkdiv = omap_mcbsp_dai_set_clkdiv, \ + .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \ + }, \ + .private_data = &mcbsp_data[(link_id)].bus_id, \ +} + +struct snd_soc_dai omap_mcbsp_dai[] = { + OMAP_MCBSP_DAI_BUILDER(0), + OMAP_MCBSP_DAI_BUILDER(1), +#if NUM_LINKS >= 3 + OMAP_MCBSP_DAI_BUILDER(2), +#endif +#if NUM_LINKS == 5 + OMAP_MCBSP_DAI_BUILDER(3), + OMAP_MCBSP_DAI_BUILDER(4), +#endif }; + EXPORT_SYMBOL_GPL(omap_mcbsp_dai); MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>"); diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index ed8afb55067..df7ad13ba73 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -38,11 +38,17 @@ enum omap_mcbsp_div { OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ }; -/* - * REVISIT: Preparation for the ASoC v2. Let the number of available links to - * be same than number of McBSP ports found in OMAP(s) we are compiling for. - */ -#define NUM_LINKS 1 +#if defined(CONFIG_ARCH_OMAP2420) +#define NUM_LINKS 2 +#endif +#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) +#undef NUM_LINKS +#define NUM_LINKS 3 +#endif +#if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP34XX) +#undef NUM_LINKS +#define NUM_LINKS 5 +#endif extern struct snd_soc_dai omap_mcbsp_dai[NUM_LINKS]; diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 690bfeaec4a..e9084fdd208 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, prtd->dma_data = dma_data; err = omap_request_dma(dma_data->dma_req, dma_data->name, omap_pcm_dma_irq, substream, &prtd->dma_ch); - if (!cpu_is_omap1510()) { + if (!err & !cpu_is_omap1510()) { /* * Link channel with itself so DMA doesn't need any * reprogramming while looping the buffer @@ -147,12 +147,14 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream) dma_params.src_or_dst_synch = OMAP_DMA_DST_SYNC; dma_params.src_start = runtime->dma_addr; dma_params.dst_start = dma_data->port_addr; + dma_params.dst_port = OMAP_DMA_PORT_MPUI; } else { dma_params.src_amode = OMAP_DMA_AMODE_CONSTANT; dma_params.dst_amode = OMAP_DMA_AMODE_POST_INC; dma_params.src_or_dst_synch = OMAP_DMA_SRC_SYNC; dma_params.src_start = dma_data->port_addr; dma_params.dst_start = runtime->dma_addr; + dma_params.src_port = OMAP_DMA_PORT_MPUI; } /* * Set DMA transfer frame size equal to ALSA period size and frame diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c new file mode 100644 index 00000000000..0fe73379689 --- /dev/null +++ b/sound/soc/omap/osk5912.c @@ -0,0 +1,232 @@ +/* + * osk5912.c -- SoC audio for OSK 5912 + * + * Copyright (C) 2008 Mistral Solutions + * + * Contact: Arun KS <arunks@mistralsolutions.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <linux/gpio.h> +#include <mach/mcbsp.h> + +#include "omap-mcbsp.h" +#include "omap-pcm.h" +#include "../codecs/tlv320aic23.h" + +#define CODEC_CLOCK 12000000 + +static struct clk *tlv320aic23_mclk; + +static int osk_startup(struct snd_pcm_substream *substream) +{ + return clk_enable(tlv320aic23_mclk); +} + +static void osk_shutdown(struct snd_pcm_substream *substream) +{ + clk_disable(tlv320aic23_mclk); +} + +static int osk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai; + int err; + + /* Set codec DAI configuration */ + err = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM); + if (err < 0) { + printk(KERN_ERR "can't set codec DAI configuration\n"); + return err; + } + + /* Set cpu DAI configuration */ + err = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_NB_IF | + SND_SOC_DAIFMT_CBM_CFM); + if (err < 0) { + printk(KERN_ERR "can't set cpu DAI configuration\n"); + return err; + } + + /* Set the codec system clock for DAC and ADC */ + err = + snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); + + if (err < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return err; + } + + return err; +} + +static struct snd_soc_ops osk_ops = { + .startup = osk_startup, + .hw_params = osk_hw_params, + .shutdown = osk_shutdown, +}; + +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + {"LLINEIN", NULL, "Line In"}, + {"RLINEIN", NULL, "Line In"}, + + {"MICIN", NULL, "Mic Jack"}, +}; + +static int osk_tlv320aic23_init(struct snd_soc_codec *codec) +{ + + /* Add osk5912 specific widgets */ + snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets, + ARRAY_SIZE(tlv320aic23_dapm_widgets)); + + /* Set up osk5912 specific audio path audio_map */ + snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); + + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Line In"); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); + + snd_soc_dapm_sync(codec); + + return 0; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link osk_dai = { + .name = "TLV320AIC23", + .stream_name = "AIC23", + .cpu_dai = &omap_mcbsp_dai[0], + .codec_dai = &tlv320aic23_dai, + .init = osk_tlv320aic23_init, + .ops = &osk_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_machine snd_soc_machine_osk = { + .name = "OSK5912", + .dai_link = &osk_dai, + .num_links = 1, +}; + +/* Audio subsystem */ +static struct snd_soc_device osk_snd_devdata = { + .machine = &snd_soc_machine_osk, + .platform = &omap_soc_platform, + .codec_dev = &soc_codec_dev_tlv320aic23, +}; + +static struct platform_device *osk_snd_device; + +static int __init osk_soc_init(void) +{ + int err; + u32 curRate; + struct device *dev; + + if (!(machine_is_omap_osk())) + return -ENODEV; + + osk_snd_device = platform_device_alloc("soc-audio", -1); + if (!osk_snd_device) + return -ENOMEM; + + platform_set_drvdata(osk_snd_device, &osk_snd_devdata); + osk_snd_devdata.dev = &osk_snd_device->dev; + *(unsigned int *)osk_dai.cpu_dai->private_data = 0; /* McBSP1 */ + err = platform_device_add(osk_snd_device); + if (err) + goto err1; + + dev = &osk_snd_device->dev; + + tlv320aic23_mclk = clk_get(dev, "mclk"); + if (IS_ERR(tlv320aic23_mclk)) { + printk(KERN_ERR "Could not get mclk clock\n"); + return -ENODEV; + } + + if (clk_get_usecount(tlv320aic23_mclk) > 0) { + /* MCLK is already in use */ + printk(KERN_WARNING + "MCLK in use at %d Hz. We change it to %d Hz\n", + (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); + } + + /* + * Configure 12 MHz output on MCLK. + */ + curRate = (uint) clk_get_rate(tlv320aic23_mclk); + if (curRate != CODEC_CLOCK) { + if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) { + printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n"); + err = -ECANCELED; + goto err1; + } + } + + printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n", + (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK, + clk_get_usecount(tlv320aic23_mclk)); + + return 0; +err1: + clk_put(tlv320aic23_mclk); + platform_device_del(osk_snd_device); + platform_device_put(osk_snd_device); + + return err; + +} + +static void __exit osk_soc_exit(void) +{ + platform_device_unregister(osk_snd_device); +} + +module_init(osk_soc_init); +module_exit(osk_soc_exit); + +MODULE_AUTHOR("Arun KS <arunks@mistralsolutions.com>"); +MODULE_DESCRIPTION("ALSA SoC OSK 5912"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index 1a8373de7f3..2718eaf7895 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -281,8 +281,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_disable_pin(codec, "LLINEIN"); - snd_soc_dapm_disable_pin(codec, "RLINEIN"); + snd_soc_dapm_nc_pin(codec, "LLINEIN"); + snd_soc_dapm_nc_pin(codec, "RLINEIN"); /* Add corgi specific controls */ for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index d9c3f7b28be..e6ff6929ab4 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -9,7 +9,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index f84f7d8db09..4d9930c5278 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -242,8 +242,8 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_disable_pin(codec, "LLINEIN"); - snd_soc_dapm_disable_pin(codec, "RLINEIN"); + snd_soc_dapm_nc_pin(codec, "LLINEIN"); + snd_soc_dapm_nc_pin(codec, "RLINEIN"); snd_soc_dapm_enable_pin(codec, "MICIN"); /* Add poodle specific controls */ diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c index 2fb58298513..e758034db5c 100644 --- a/sound/soc/pxa/pxa2xx-i2s.c +++ b/sound/soc/pxa/pxa2xx-i2s.c @@ -3,7 +3,7 @@ * * Copyright 2005 Wolfson Microelectronics PLC. * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * lrg@slimlogic.co.uk * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -405,6 +405,6 @@ module_init(pxa2xx_i2s_init); module_exit(pxa2xx_i2s_exit); /* Module information */ -MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("pxa2xx I2S SoC Interface"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 9a70b00fc30..d307b6757e9 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -281,13 +281,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) int i, err; /* NC codec pins */ - snd_soc_dapm_disable_pin(codec, "RINPUT1"); - snd_soc_dapm_disable_pin(codec, "LINPUT2"); - snd_soc_dapm_disable_pin(codec, "RINPUT2"); - snd_soc_dapm_disable_pin(codec, "LINPUT3"); - snd_soc_dapm_disable_pin(codec, "RINPUT3"); - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "MONO1"); + snd_soc_dapm_nc_pin(codec, "RINPUT1"); + snd_soc_dapm_nc_pin(codec, "LINPUT2"); + snd_soc_dapm_nc_pin(codec, "RINPUT2"); + snd_soc_dapm_nc_pin(codec, "LINPUT3"); + snd_soc_dapm_nc_pin(codec, "RINPUT3"); + snd_soc_dapm_nc_pin(codec, "OUT3"); + snd_soc_dapm_nc_pin(codec, "MONO1"); /* Add spitz specific controls */ for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 2baaa750f12..afefe41b8c4 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -4,7 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * Authors: Liam Girdwood <lrg@slimlogic.co.uk> * Richard Purdie <richard@openedhand.com> * * This program is free software; you can redistribute it and/or modify it @@ -190,8 +190,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "MONOOUT"); + snd_soc_dapm_nc_pin(codec, "OUT3"); + snd_soc_dapm_nc_pin(codec, "MONOOUT"); /* add tosa specific controls */ for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 73a50e93a9a..87ddfefcc2f 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -511,21 +511,20 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) DBG("Entered %s\n", __func__); /* set up NC codec pins */ - snd_soc_dapm_disable_pin(codec, "LOUT2"); - snd_soc_dapm_disable_pin(codec, "ROUT2"); - snd_soc_dapm_disable_pin(codec, "OUT3"); - snd_soc_dapm_disable_pin(codec, "OUT4"); - snd_soc_dapm_disable_pin(codec, "LINE1"); - snd_soc_dapm_disable_pin(codec, "LINE2"); - - - /* set endpoints to default mode */ - set_scenario_endpoints(codec, NEO_AUDIO_OFF); + snd_soc_dapm_nc_pin(codec, "LOUT2"); + snd_soc_dapm_nc_pin(codec, "ROUT2"); + snd_soc_dapm_nc_pin(codec, "OUT3"); + snd_soc_dapm_nc_pin(codec, "OUT4"); + snd_soc_dapm_nc_pin(codec, "LINE1"); + snd_soc_dapm_nc_pin(codec, "LINE2"); /* Add neo1973 specific widgets */ snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets, ARRAY_SIZE(wm8753_dapm_widgets)); + /* set endpoints to default mode */ + set_scenario_endpoints(codec, NEO_AUDIO_OFF); + /* add neo1973 specific controls */ for (i = 0; i < ARRAY_SIZE(wm8753_neo1973_controls); i++) { err = snd_ctl_add(codec->card, @@ -603,6 +602,8 @@ static int lm4857_i2c_probe(struct i2c_client *client, { DBG("Entered %s\n", __func__); + i2c = client; + lm4857_write_regs(); return 0; } @@ -611,6 +612,8 @@ static int lm4857_i2c_remove(struct i2c_client *client) { DBG("Entered %s\n", __func__); + i2c = NULL; + return 0; } @@ -650,7 +653,7 @@ static void lm4857_shutdown(struct i2c_client *dev) } static const struct i2c_device_id lm4857_i2c_id[] = { - { "neo1973_lm4857", 0 } + { "neo1973_lm4857", 0 }, { } }; @@ -668,48 +671,6 @@ static struct i2c_driver lm4857_i2c_driver = { }; static struct platform_device *neo1973_snd_device; -static struct i2c_client *lm4857_client; - -static int __init neo1973_add_lm4857_device(struct platform_device *pdev, - int i2c_bus, - unsigned short i2c_address) -{ - struct i2c_board_info info; - struct i2c_adapter *adapter; - struct i2c_client *client; - int ret; - - ret = i2c_add_driver(&lm4857_i2c_driver); - if (ret != 0) { - dev_err(&pdev->dev, "can't add lm4857 driver\n"); - return ret; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = i2c_address; - strlcpy(info.type, "neo1973_lm4857", I2C_NAME_SIZE); - - adapter = i2c_get_adapter(i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "can't get i2c adapter %d\n", i2c_bus); - goto err_driver; - } - - client = i2c_new_device(adapter, &info); - i2c_put_adapter(adapter); - if (!client) { - dev_err(&pdev->dev, "can't add lm4857 device at 0x%x\n", - (unsigned int)info.addr); - goto err_driver; - } - - lm4857_client = client; - return 0; - -err_driver: - i2c_del_driver(&lm4857_i2c_driver); - return -ENODEV; -} static int __init neo1973_init(void) { @@ -736,8 +697,8 @@ static int __init neo1973_init(void) return ret; } - ret = neo1973_add_lm4857_device(neo1973_snd_device, - neo1973_wm8753_setup, 0x7C); + ret = i2c_add_driver(&lm4857_i2c_driver); + if (ret != 0) platform_device_unregister(neo1973_snd_device); @@ -748,7 +709,6 @@ static void __exit neo1973_exit(void) { DBG("Entered %s\n", __func__); - i2c_unregister_device(lm4857_client); i2c_del_driver(&lm4857_i2c_driver); platform_device_unregister(neo1973_snd_device); } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ad381138fc2..462e635dfc7 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4,8 +4,7 @@ * Copyright 2005 Wolfson Microelectronics PLC. * Copyright 2005 Openedhand Ltd. * - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * with code, comments and ideas from :- * Richard Purdie <richard@openedhand.com> * @@ -1886,7 +1885,7 @@ module_init(snd_soc_init); module_exit(snd_soc_exit); /* Module information */ -MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("ALSA SoC Core"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:soc-audio"); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 9ca9c08610f..efbd0b37810 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2,8 +2,7 @@ * soc-dapm.c -- ALSA SoC Dynamic Audio Power Management * * Copyright 2005 Wolfson Microelectronics PLC. - * Author: Liam Girdwood - * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Author: Liam Girdwood <lrg@slimlogic.co.uk> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -1484,6 +1483,26 @@ int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin) EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); /** + * snd_soc_dapm_nc_pin - permanently disable pin. + * @codec: SoC codec + * @pin: pin name + * + * Marks the specified pin as being not connected, disabling it along + * any parent or child widgets. At present this is identical to + * snd_soc_dapm_disable_pin() but in future it will be extended to do + * additional things such as disabling controls which only affect + * paths through the pin. + * + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin) +{ + return snd_soc_dapm_set_pin(codec, pin, 0); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); + +/** * snd_soc_dapm_get_pin_status - get audio pin status * @codec: audio codec * @pin: audio signal pin endpoint (or start point) @@ -1521,6 +1540,6 @@ void snd_soc_dapm_free(struct snd_soc_device *socdev) EXPORT_SYMBOL_GPL(snd_soc_dapm_free); /* Module information */ -MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); MODULE_LICENSE("GPL"); diff --git a/sound/sparc/amd7930.c b/sound/sparc/amd7930.c index 49acee0c484..f87933e4881 100644 --- a/sound/sparc/amd7930.c +++ b/sound/sparc/amd7930.c @@ -1,6 +1,6 @@ /* * Driver for AMD7930 sound chips found on Sparcs. - * Copyright (C) 2002 David S. Miller <davem@redhat.com> + * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net> * * Based entirely upon drivers/sbus/audio/amd7930.c which is: * Copyright (C) 1996,1997 Thomas K. Dyas (tdyas@eden.rutgers.edu) @@ -35,6 +35,8 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/moduleparam.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -44,7 +46,6 @@ #include <asm/io.h> #include <asm/irq.h> -#include <asm/sbus.h> #include <asm/prom.h> static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -335,8 +336,8 @@ struct snd_amd7930 { int pgain; int mgain; + struct of_device *op; unsigned int irq; - unsigned int regs_size; struct snd_amd7930 *next; }; @@ -905,13 +906,16 @@ static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd) static int snd_amd7930_free(struct snd_amd7930 *amd) { + struct of_device *op = amd->op; + amd7930_idle(amd); if (amd->irq) free_irq(amd->irq, amd); if (amd->regs) - sbus_iounmap(amd->regs, amd->regs_size); + of_iounmap(&op->resource[0], amd->regs, + resource_size(&op->resource[0])); kfree(amd); @@ -930,13 +934,12 @@ static struct snd_device_ops snd_amd7930_dev_ops = { }; static int __devinit snd_amd7930_create(struct snd_card *card, - struct resource *rp, - unsigned int reg_size, + struct of_device *op, int irq, int dev, struct snd_amd7930 **ramd) { - unsigned long flags; struct snd_amd7930 *amd; + unsigned long flags; int err; *ramd = NULL; @@ -946,9 +949,10 @@ static int __devinit snd_amd7930_create(struct snd_card *card, spin_lock_init(&amd->lock); amd->card = card; - amd->regs_size = reg_size; + amd->op = op; - amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930"); + amd->regs = of_ioremap(&op->resource[0], 0, + resource_size(&op->resource[0]), "amd7930"); if (!amd->regs) { snd_printk("amd7930-%d: Unable to map chip registers.\n", dev); return -EIO; @@ -997,12 +1001,15 @@ static int __devinit snd_amd7930_create(struct snd_card *card, return 0; } -static int __devinit amd7930_attach_common(struct resource *rp, int irq) +static int __devinit amd7930_sbus_probe(struct of_device *op, const struct of_device_id *match) { + struct resource *rp = &op->resource[0]; static int dev_num; struct snd_card *card; struct snd_amd7930 *amd; - int err; + int err, irq; + + irq = op->irqs[0]; if (dev_num >= SNDRV_CARDS) return -ENODEV; @@ -1023,8 +1030,7 @@ static int __devinit amd7930_attach_common(struct resource *rp, int irq) (unsigned long long)rp->start, irq); - if ((err = snd_amd7930_create(card, rp, - (rp->end - rp->start) + 1, + if ((err = snd_amd7930_create(card, op, irq, dev_num, &amd)) < 0) goto out_err; @@ -1049,43 +1055,7 @@ out_err: return err; } -static int __devinit amd7930_obio_attach(struct device_node *dp) -{ - const struct linux_prom_registers *regs; - const struct linux_prom_irqs *irqp; - struct resource res, *rp; - int len; - - irqp = of_get_property(dp, "intr", &len); - if (!irqp) { - snd_printk("%s: Firmware node lacks IRQ property.\n", - dp->full_name); - return -ENODEV; - } - - regs = of_get_property(dp, "reg", &len); - if (!regs) { - snd_printk("%s: Firmware node lacks register property.\n", - dp->full_name); - return -ENODEV; - } - - rp = &res; - rp->start = regs->phys_addr; - rp->end = rp->start + regs->reg_size - 1; - rp->flags = IORESOURCE_IO | (regs->which_io & 0xff); - - return amd7930_attach_common(rp, irqp->pri); -} - -static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - - return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]); -} - -static struct of_device_id amd7930_match[] = { +static const struct of_device_id amd7930_match[] = { { .name = "audio", }, @@ -1100,20 +1070,7 @@ static struct of_platform_driver amd7930_sbus_driver = { static int __init amd7930_init(void) { - struct device_node *dp; - - /* Try to find the sun4c "audio" node first. */ - dp = of_find_node_by_path("/"); - dp = dp->child; - while (dp) { - if (!strcmp(dp->name, "audio")) - amd7930_obio_attach(dp); - - dp = dp->sibling; - } - - /* Probe each SBUS for amd7930 chips. */ - return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type); + return of_register_driver(&amd7930_sbus_driver, &of_bus_type); } static void __exit amd7930_exit(void) diff --git a/sound/sparc/cs4231.c b/sound/sparc/cs4231.c index 791d2fb821d..d44bf98e965 100644 --- a/sound/sparc/cs4231.c +++ b/sound/sparc/cs4231.c @@ -1,6 +1,6 @@ /* * Driver for CS4231 sound chips found on Sparcs. - * Copyright (C) 2002 David S. Miller <davem@redhat.com> + * Copyright (C) 2002, 2008 David S. Miller <davem@davemloft.net> * * Based entirely upon drivers/sbus/audio/cs4231.c which is: * Copyright (C) 1996, 1997, 1998 Derrick J Brashear (shadow@andrew.cmu.edu) @@ -17,7 +17,8 @@ #include <linux/moduleparam.h> #include <linux/irq.h> #include <linux/io.h> - +#include <linux/of.h> +#include <linux/of_device.h> #include <sound/core.h> #include <sound/pcm.h> @@ -29,13 +30,12 @@ #ifdef CONFIG_SBUS #define SBUS_SUPPORT -#include <asm/sbus.h> #endif #if defined(CONFIG_PCI) && defined(CONFIG_SPARC64) #define EBUS_SUPPORT #include <linux/pci.h> -#include <asm/ebus.h> +#include <asm/ebus_dma.h> #endif static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ @@ -70,8 +70,6 @@ struct cs4231_dma_control { int (*request)(struct cs4231_dma_control *dma_cont, dma_addr_t bus_addr, size_t len); unsigned int (*address)(struct cs4231_dma_control *dma_cont); - void (*preallocate)(struct snd_cs4231 *chip, - struct snd_pcm *pcm); #ifdef EBUS_SUPPORT struct ebus_dma_info ebus_info; #endif @@ -114,21 +112,12 @@ struct snd_cs4231 { struct mutex mce_mutex; /* mutex for mce register */ struct mutex open_mutex; /* mutex for ALSA open/close */ - union { -#ifdef SBUS_SUPPORT - struct sbus_dev *sdev; -#endif -#ifdef EBUS_SUPPORT - struct pci_dev *pdev; -#endif - } dev_u; + struct of_device *op; unsigned int irq[2]; unsigned int regs_size; struct snd_cs4231 *next; }; -static struct snd_cs4231 *cs4231_list; - /* Eventually we can use sound/isa/cs423x/cs4231_lib.c directly, but for * now.... -DaveM */ @@ -267,27 +256,19 @@ static unsigned char snd_cs4231_original_image[32] = static u8 __cs4231_readb(struct snd_cs4231 *cp, void __iomem *reg_addr) { -#ifdef EBUS_SUPPORT if (cp->flags & CS4231_FLAG_EBUS) return readb(reg_addr); else -#endif -#ifdef SBUS_SUPPORT return sbus_readb(reg_addr); -#endif } static void __cs4231_writeb(struct snd_cs4231 *cp, u8 val, void __iomem *reg_addr) { -#ifdef EBUS_SUPPORT if (cp->flags & CS4231_FLAG_EBUS) return writeb(val, reg_addr); else -#endif -#ifdef SBUS_SUPPORT return sbus_writeb(val, reg_addr); -#endif } /* @@ -1258,7 +1239,9 @@ static int __init snd_cs4231_pcm(struct snd_card *card) pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; strcpy(pcm->name, "CS4231"); - chip->p_dma.preallocate(chip, pcm); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + &chip->op->dev, + 64 * 1024, 128 * 1024); chip->pcm = pcm; @@ -1627,8 +1610,7 @@ static int __init cs4231_attach_finish(struct snd_card *card) if (err < 0) goto out_err; - chip->next = cs4231_list; - cs4231_list = chip; + dev_set_drvdata(&chip->op->dev, chip); dev++; return 0; @@ -1783,24 +1765,19 @@ static unsigned int sbus_dma_addr(struct cs4231_dma_control *dma_cont) return sbus_readl(base->regs + base->dir + APCVA); } -static void sbus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_SBUS, - snd_dma_sbus_data(chip->dev_u.sdev), - 64 * 1024, 128 * 1024); -} - /* * Init and exit routines */ static int snd_cs4231_sbus_free(struct snd_cs4231 *chip) { + struct of_device *op = chip->op; + if (chip->irq[0]) free_irq(chip->irq[0], chip); if (chip->port) - sbus_iounmap(chip->port, chip->regs_size); + of_iounmap(&op->resource[0], chip->port, chip->regs_size); return 0; } @@ -1817,7 +1794,7 @@ static struct snd_device_ops snd_cs4231_sbus_dev_ops = { }; static int __init snd_cs4231_sbus_create(struct snd_card *card, - struct sbus_dev *sdev, + struct of_device *op, int dev) { struct snd_cs4231 *chip = card->private_data; @@ -1828,13 +1805,13 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card, spin_lock_init(&chip->p_dma.sbus_info.lock); mutex_init(&chip->mce_mutex); mutex_init(&chip->open_mutex); - chip->dev_u.sdev = sdev; - chip->regs_size = sdev->reg_addrs[0].reg_size; + chip->op = op; + chip->regs_size = resource_size(&op->resource[0]); memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); - chip->port = sbus_ioremap(&sdev->resource[0], 0, - chip->regs_size, "cs4231"); + chip->port = of_ioremap(&op->resource[0], 0, + chip->regs_size, "cs4231"); if (!chip->port) { snd_printdd("cs4231-%d: Unable to map chip registers.\n", dev); return -EIO; @@ -1849,22 +1826,20 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card, chip->p_dma.enable = sbus_dma_enable; chip->p_dma.request = sbus_dma_request; chip->p_dma.address = sbus_dma_addr; - chip->p_dma.preallocate = sbus_dma_preallocate; chip->c_dma.prepare = sbus_dma_prepare; chip->c_dma.enable = sbus_dma_enable; chip->c_dma.request = sbus_dma_request; chip->c_dma.address = sbus_dma_addr; - chip->c_dma.preallocate = sbus_dma_preallocate; - if (request_irq(sdev->irqs[0], snd_cs4231_sbus_interrupt, + if (request_irq(op->irqs[0], snd_cs4231_sbus_interrupt, IRQF_SHARED, "cs4231", chip)) { snd_printdd("cs4231-%d: Unable to grab SBUS IRQ %d\n", - dev, sdev->irqs[0]); + dev, op->irqs[0]); snd_cs4231_sbus_free(chip); return -EBUSY; } - chip->irq[0] = sdev->irqs[0]; + chip->irq[0] = op->irqs[0]; if (snd_cs4231_probe(chip) < 0) { snd_cs4231_sbus_free(chip); @@ -1881,9 +1856,9 @@ static int __init snd_cs4231_sbus_create(struct snd_card *card, return 0; } -static int __init cs4231_sbus_attach(struct sbus_dev *sdev) +static int __devinit cs4231_sbus_probe(struct of_device *op, const struct of_device_id *match) { - struct resource *rp = &sdev->resource[0]; + struct resource *rp = &op->resource[0]; struct snd_card *card; int err; @@ -1895,9 +1870,9 @@ static int __init cs4231_sbus_attach(struct sbus_dev *sdev) card->shortname, rp->flags & 0xffL, (unsigned long long)rp->start, - sdev->irqs[0]); + op->irqs[0]); - err = snd_cs4231_sbus_create(card, sdev, dev); + err = snd_cs4231_sbus_create(card, op, dev); if (err < 0) { snd_card_free(card); return err; @@ -1950,30 +1925,25 @@ static unsigned int _ebus_dma_addr(struct cs4231_dma_control *dma_cont) return ebus_dma_addr(&dma_cont->ebus_info); } -static void _ebus_dma_preallocate(struct snd_cs4231 *chip, struct snd_pcm *pcm) -{ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->dev_u.pdev), - 64*1024, 128*1024); -} - /* * Init and exit routines */ static int snd_cs4231_ebus_free(struct snd_cs4231 *chip) { + struct of_device *op = chip->op; + if (chip->c_dma.ebus_info.regs) { ebus_dma_unregister(&chip->c_dma.ebus_info); - iounmap(chip->c_dma.ebus_info.regs); + of_iounmap(&op->resource[2], chip->c_dma.ebus_info.regs, 0x10); } if (chip->p_dma.ebus_info.regs) { ebus_dma_unregister(&chip->p_dma.ebus_info); - iounmap(chip->p_dma.ebus_info.regs); + of_iounmap(&op->resource[1], chip->p_dma.ebus_info.regs, 0x10); } if (chip->port) - iounmap(chip->port); + of_iounmap(&op->resource[0], chip->port, 0x10); return 0; } @@ -1990,7 +1960,7 @@ static struct snd_device_ops snd_cs4231_ebus_dev_ops = { }; static int __init snd_cs4231_ebus_create(struct snd_card *card, - struct linux_ebus_device *edev, + struct of_device *op, int dev) { struct snd_cs4231 *chip = card->private_data; @@ -2002,35 +1972,35 @@ static int __init snd_cs4231_ebus_create(struct snd_card *card, mutex_init(&chip->mce_mutex); mutex_init(&chip->open_mutex); chip->flags |= CS4231_FLAG_EBUS; - chip->dev_u.pdev = edev->bus->self; + chip->op = op; memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); strcpy(chip->c_dma.ebus_info.name, "cs4231(capture)"); chip->c_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->c_dma.ebus_info.callback = snd_cs4231_ebus_capture_callback; chip->c_dma.ebus_info.client_cookie = chip; - chip->c_dma.ebus_info.irq = edev->irqs[0]; + chip->c_dma.ebus_info.irq = op->irqs[0]; strcpy(chip->p_dma.ebus_info.name, "cs4231(play)"); chip->p_dma.ebus_info.flags = EBUS_DMA_FLAG_USE_EBDMA_HANDLER; chip->p_dma.ebus_info.callback = snd_cs4231_ebus_play_callback; chip->p_dma.ebus_info.client_cookie = chip; - chip->p_dma.ebus_info.irq = edev->irqs[1]; + chip->p_dma.ebus_info.irq = op->irqs[1]; chip->p_dma.prepare = _ebus_dma_prepare; chip->p_dma.enable = _ebus_dma_enable; chip->p_dma.request = _ebus_dma_request; chip->p_dma.address = _ebus_dma_addr; - chip->p_dma.preallocate = _ebus_dma_preallocate; chip->c_dma.prepare = _ebus_dma_prepare; chip->c_dma.enable = _ebus_dma_enable; chip->c_dma.request = _ebus_dma_request; chip->c_dma.address = _ebus_dma_addr; - chip->c_dma.preallocate = _ebus_dma_preallocate; - chip->port = ioremap(edev->resource[0].start, 0x10); - chip->p_dma.ebus_info.regs = ioremap(edev->resource[1].start, 0x10); - chip->c_dma.ebus_info.regs = ioremap(edev->resource[2].start, 0x10); + chip->port = of_ioremap(&op->resource[0], 0, 0x10, "cs4231"); + chip->p_dma.ebus_info.regs = + of_ioremap(&op->resource[1], 0, 0x10, "cs4231_pdma"); + chip->c_dma.ebus_info.regs = + of_ioremap(&op->resource[2], 0, 0x10, "cs4231_cdma"); if (!chip->port || !chip->p_dma.ebus_info.regs || !chip->c_dma.ebus_info.regs) { snd_cs4231_ebus_free(chip); @@ -2078,7 +2048,7 @@ static int __init snd_cs4231_ebus_create(struct snd_card *card, return 0; } -static int __init cs4231_ebus_attach(struct linux_ebus_device *edev) +static int __devinit cs4231_ebus_probe(struct of_device *op, const struct of_device_id *match) { struct snd_card *card; int err; @@ -2089,10 +2059,10 @@ static int __init cs4231_ebus_attach(struct linux_ebus_device *edev) sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname, - edev->resource[0].start, - edev->irqs[0]); + op->resource[0].start, + op->irqs[0]); - err = snd_cs4231_ebus_create(card, edev, dev); + err = snd_cs4231_ebus_create(card, op, dev); if (err < 0) { snd_card_free(card); return err; @@ -2102,68 +2072,57 @@ static int __init cs4231_ebus_attach(struct linux_ebus_device *edev) } #endif -static int __init cs4231_init(void) +static int __devinit cs4231_probe(struct of_device *op, const struct of_device_id *match) { -#ifdef SBUS_SUPPORT - struct sbus_bus *sbus; - struct sbus_dev *sdev; -#endif #ifdef EBUS_SUPPORT - struct linux_ebus *ebus; - struct linux_ebus_device *edev; + if (!strcmp(op->node->parent->name, "ebus")) + return cs4231_ebus_probe(op, match); #endif - int found; - - found = 0; - #ifdef SBUS_SUPPORT - for_all_sbusdev(sdev, sbus) { - if (!strcmp(sdev->prom_name, "SUNW,CS4231")) { - if (cs4231_sbus_attach(sdev) == 0) - found++; - } - } + if (!strcmp(op->node->parent->name, "sbus") || + !strcmp(op->node->parent->name, "sbi")) + return cs4231_sbus_probe(op, match); #endif -#ifdef EBUS_SUPPORT - for_each_ebus(ebus) { - for_each_ebusdev(edev, ebus) { - int match = 0; - - if (!strcmp(edev->prom_node->name, "SUNW,CS4231")) { - match = 1; - } else if (!strcmp(edev->prom_node->name, "audio")) { - const char *compat; - - compat = of_get_property(edev->prom_node, - "compatible", NULL); - if (compat && !strcmp(compat, "SUNW,CS4231")) - match = 1; - } + return -ENODEV; +} - if (match && - cs4231_ebus_attach(edev) == 0) - found++; - } - } -#endif +static int __devexit cs4231_remove(struct of_device *op) +{ + struct snd_cs4231 *chip = dev_get_drvdata(&op->dev); + snd_card_free(chip->card); - return (found > 0) ? 0 : -EIO; + return 0; } -static void __exit cs4231_exit(void) -{ - struct snd_cs4231 *p = cs4231_list; +static const struct of_device_id cs4231_match[] = { + { + .name = "SUNW,CS4231", + }, + { + .name = "audio", + .compatible = "SUNW,CS4231", + }, + {}, +}; - while (p != NULL) { - struct snd_cs4231 *next = p->next; +MODULE_DEVICE_TABLE(of, cs4231_match); - snd_card_free(p->card); +static struct of_platform_driver cs4231_driver = { + .name = "audio", + .match_table = cs4231_match, + .probe = cs4231_probe, + .remove = __devexit_p(cs4231_remove), +}; - p = next; - } +static int __init cs4231_init(void) +{ + return of_register_driver(&cs4231_driver, &of_bus_type); +} - cs4231_list = NULL; +static void __exit cs4231_exit(void) +{ + of_unregister_driver(&cs4231_driver); } module_init(cs4231_init); diff --git a/sound/sparc/dbri.c b/sound/sparc/dbri.c index c534a2a849f..c257ad8bdfb 100644 --- a/sound/sparc/dbri.c +++ b/sound/sparc/dbri.c @@ -57,6 +57,7 @@ #include <linux/delay.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/dma-mapping.h> #include <sound/core.h> #include <sound/pcm.h> @@ -66,7 +67,7 @@ #include <sound/initval.h> #include <linux/of.h> -#include <asm/sbus.h> +#include <linux/of_device.h> #include <asm/atomic.h> MODULE_AUTHOR("Rudolf Koenig, Brent Baccala and Martin Habets"); @@ -297,7 +298,7 @@ struct dbri_streaminfo { /* This structure holds the information for both chips (DBRI & CS4215) */ struct snd_dbri { int regs_size, irq; /* Needed for unload */ - struct sbus_dev *sdev; /* SBUS device info */ + struct of_device *op; /* OF device info */ spinlock_t lock; struct dbri_dma *dma; /* Pointer to our DMA block */ @@ -2093,14 +2094,15 @@ static int snd_dbri_hw_params(struct snd_pcm_substream *substream, */ if (info->dvma_buffer == 0) { if (DBRI_STREAMNO(substream) == DBRI_PLAY) - direction = SBUS_DMA_TODEVICE; + direction = DMA_TO_DEVICE; else - direction = SBUS_DMA_FROMDEVICE; + direction = DMA_FROM_DEVICE; - info->dvma_buffer = sbus_map_single(dbri->sdev, - runtime->dma_area, - params_buffer_bytes(hw_params), - direction); + info->dvma_buffer = + dma_map_single(&dbri->op->dev, + runtime->dma_area, + params_buffer_bytes(hw_params), + direction); } direction = params_buffer_bytes(hw_params); @@ -2121,12 +2123,12 @@ static int snd_dbri_hw_free(struct snd_pcm_substream *substream) */ if (info->dvma_buffer) { if (DBRI_STREAMNO(substream) == DBRI_PLAY) - direction = SBUS_DMA_TODEVICE; + direction = DMA_TO_DEVICE; else - direction = SBUS_DMA_FROMDEVICE; + direction = DMA_FROM_DEVICE; - sbus_unmap_single(dbri->sdev, info->dvma_buffer, - substream->runtime->buffer_size, direction); + dma_unmap_single(&dbri->op->dev, info->dvma_buffer, + substream->runtime->buffer_size, direction); info->dvma_buffer = 0; } if (info->pipe != -1) { @@ -2519,31 +2521,32 @@ static void __devinit snd_dbri_proc(struct snd_card *card) static void snd_dbri_free(struct snd_dbri *dbri); static int __devinit snd_dbri_create(struct snd_card *card, - struct sbus_dev *sdev, - int irq, int dev) + struct of_device *op, + int irq, int dev) { struct snd_dbri *dbri = card->private_data; int err; spin_lock_init(&dbri->lock); - dbri->sdev = sdev; + dbri->op = op; dbri->irq = irq; - dbri->dma = sbus_alloc_consistent(sdev, sizeof(struct dbri_dma), - &dbri->dma_dvma); + dbri->dma = dma_alloc_coherent(&op->dev, + sizeof(struct dbri_dma), + &dbri->dma_dvma, GFP_ATOMIC); memset((void *)dbri->dma, 0, sizeof(struct dbri_dma)); dprintk(D_GEN, "DMA Cmd Block 0x%p (0x%08x)\n", dbri->dma, dbri->dma_dvma); /* Map the registers into memory. */ - dbri->regs_size = sdev->reg_addrs[0].reg_size; - dbri->regs = sbus_ioremap(&sdev->resource[0], 0, - dbri->regs_size, "DBRI Registers"); + dbri->regs_size = resource_size(&op->resource[0]); + dbri->regs = of_ioremap(&op->resource[0], 0, + dbri->regs_size, "DBRI Registers"); if (!dbri->regs) { printk(KERN_ERR "DBRI: could not allocate registers\n"); - sbus_free_consistent(sdev, sizeof(struct dbri_dma), - (void *)dbri->dma, dbri->dma_dvma); + dma_free_coherent(&op->dev, sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); return -EIO; } @@ -2551,9 +2554,9 @@ static int __devinit snd_dbri_create(struct snd_card *card, "DBRI audio", dbri); if (err) { printk(KERN_ERR "DBRI: Can't get irq %d\n", dbri->irq); - sbus_iounmap(dbri->regs, dbri->regs_size); - sbus_free_consistent(sdev, sizeof(struct dbri_dma), - (void *)dbri->dma, dbri->dma_dvma); + of_iounmap(&op->resource[0], dbri->regs, dbri->regs_size); + dma_free_coherent(&op->dev, sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); return err; } @@ -2577,27 +2580,23 @@ static void snd_dbri_free(struct snd_dbri *dbri) free_irq(dbri->irq, dbri); if (dbri->regs) - sbus_iounmap(dbri->regs, dbri->regs_size); + of_iounmap(&dbri->op->resource[0], dbri->regs, dbri->regs_size); if (dbri->dma) - sbus_free_consistent(dbri->sdev, sizeof(struct dbri_dma), - (void *)dbri->dma, dbri->dma_dvma); + dma_free_coherent(&dbri->op->dev, + sizeof(struct dbri_dma), + (void *)dbri->dma, dbri->dma_dvma); } -static int __devinit dbri_probe(struct of_device *of_dev, - const struct of_device_id *match) +static int __devinit dbri_probe(struct of_device *op, const struct of_device_id *match) { - struct sbus_dev *sdev = to_sbus_device(&of_dev->dev); struct snd_dbri *dbri; - int irq; struct resource *rp; struct snd_card *card; static int dev = 0; + int irq; int err; - dprintk(D_GEN, "DBRI: Found %s in SBUS slot %d\n", - sdev->prom_name, sdev->slot); - if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { @@ -2605,7 +2604,7 @@ static int __devinit dbri_probe(struct of_device *of_dev, return -ENOENT; } - irq = sdev->irqs[0]; + irq = op->irqs[0]; if (irq <= 0) { printk(KERN_ERR "DBRI-%d: No IRQ.\n", dev); return -ENODEV; @@ -2618,12 +2617,12 @@ static int __devinit dbri_probe(struct of_device *of_dev, strcpy(card->driver, "DBRI"); strcpy(card->shortname, "Sun DBRI"); - rp = &sdev->resource[0]; + rp = &op->resource[0]; sprintf(card->longname, "%s at 0x%02lx:0x%016Lx, irq %d", card->shortname, rp->flags & 0xffL, (unsigned long long)rp->start, irq); - err = snd_dbri_create(card, sdev, irq, dev); + err = snd_dbri_create(card, op, irq, dev); if (err < 0) { snd_card_free(card); return err; @@ -2640,7 +2639,7 @@ static int __devinit dbri_probe(struct of_device *of_dev, /* /proc file handling */ snd_dbri_proc(card); - dev_set_drvdata(&of_dev->dev, card); + dev_set_drvdata(&op->dev, card); err = snd_card_register(card); if (err < 0) @@ -2648,7 +2647,7 @@ static int __devinit dbri_probe(struct of_device *of_dev, printk(KERN_INFO "audio%d at %p (irq %d) is DBRI(%c)+CS4215(%d)\n", dev, dbri->regs, - dbri->irq, sdev->prom_name[9], dbri->mm.version); + dbri->irq, op->node->name[9], dbri->mm.version); dev++; return 0; @@ -2659,19 +2658,19 @@ _err: return err; } -static int __devexit dbri_remove(struct of_device *dev) +static int __devexit dbri_remove(struct of_device *op) { - struct snd_card *card = dev_get_drvdata(&dev->dev); + struct snd_card *card = dev_get_drvdata(&op->dev); snd_dbri_free(card->private_data); snd_card_free(card); - dev_set_drvdata(&dev->dev, NULL); + dev_set_drvdata(&op->dev, NULL); return 0; } -static struct of_device_id dbri_match[] = { +static const struct of_device_id dbri_match[] = { { .name = "SUNW,DBRIe", }, @@ -2693,7 +2692,7 @@ static struct of_platform_driver dbri_sbus_driver = { /* Probe for the dbri chip and then attach the driver. */ static int __init dbri_init(void) { - return of_register_driver(&dbri_sbus_driver, &sbus_bus_type); + return of_register_driver(&dbri_sbus_driver, &of_bus_type); } static void __exit dbri_exit(void) |