diff -uNr --exclude=CVS linux-2.4.9-ac10/Documentation/Configure.help linuxppc64_2_4/Documentation/Configure.help --- linux-2.4.9-ac10/Documentation/Configure.help Wed Oct 3 12:11:06 2001 +++ linuxppc64_2_4/Documentation/Configure.help Fri Sep 28 15:44:12 2001 @@ -179,6 +179,14 @@ from Motorola. The Linux PowerPC port has a home page at . +PowerPC64 processor +CONFIG_PPC64 + The PowerPC architecture was designed for both 32 bit and 64 bit + processor implementations. 64 bit PowerPC processors are in many + ways a superset of their 32 bit PowerPC cousins. Each 64 bit PowerPC + processor also has a 32 bit mode to allow for 32 bit compatibility. + The home of the PowerPC 64 Linux project is at + Motorola 68K processors CONFIG_M68K The Motorola 68K microprocessors are now obsolete, having been @@ -14337,6 +14345,13 @@ hard drives and ADFS-formatted floppy disks. This is experimental codes, so if you're unsure, say N. +JFS filesystem support +CONFIG_JFS_FS + This is a port of IBM's Journaled Filesystem . More information is + available in the file Documentation/filesystems/jfs.txt. + + If you do not intend to use the JFS filesystem, say N. + /dev/pts file system for Unix98 PTYs CONFIG_DEVPTS_FS You should say Y here if you said Y to "Unix98 PTY support" above. @@ -19960,6 +19975,46 @@ Select APUS if configuring for a PowerUP Amiga. More information is available at: . +# Choice: i or p +Platform support +CONFIG_PPC_ISERIES + Linux runs on certain models of the IBM AS/400, now known as the + IBM iSeries. Generally if you can run LPAR (Logical Partitioning) + on your iSeries you can run Linux in a partition on your machine. + + Linux also runs on most models of IBM pSeries hardware. (pSeries + used to be known as the RS/6000) + + If you have an iSeries and want to run Linux in a partition, + select the iSeries option to build your kernel. + + If you have a pSeries and want to run Linux, select pSeries + as the option to build your kernel. + + See for exact model information to see what + can run the 64 bit PowerPC kernel. + + iSeries Linux information from IBM can be found at: + + + pSeries Linux information from IBM can be found at: + + + Project information can be found at: + + + +Platform support +CONFIG_PPC_PSERIES + Linux runs on most models of IBM pSeries hardware. (pSeries used + to be known as the RS/6000) + + See for exact model information for the + 64 bit PowerPC kernel. + + pSeries Linux information from IBM can be found at: + + Synergy-Gemini CONFIG_GEMINI Select Gemini if configuring for a Synergy Microsystems' Gemini @@ -20218,6 +20273,13 @@ , but the EST8260 cannot be found on it and has probably been discontinued or rebadged. +Support for Large Memory +CONFIG_MSCHUNKS + MsChunks stands for Main Store Chunks and specifically allows the + 64 bit PowerPC Linux kernel to optimize for machines with sparse + discontiguous memory. iSeries kernels need to have this on. + It is recommended that for pSeries hardware that you answer Y. + ADB raw keycode support CONFIG_MAC_ADBKEYCODES This provides support for sending raw ADB keycodes to console @@ -22955,6 +23017,11 @@ CONFIG_XMON Include in-kernel hooks for the xmon kernel monitor/debugger supported by the PPC port. + +Include realtime debugging +CONFIG_PPCDBG + Include in-kernel PowerPC 64 information hooks that may be turned on/off + in real time. Include kgdb kernel debugger CONFIG_KWDB diff -uNr --exclude=CVS linux-2.4.9-ac10/Documentation/cachetlb.txt linuxppc64_2_4/Documentation/cachetlb.txt --- linux-2.4.9-ac10/Documentation/cachetlb.txt Sun Mar 25 20:14:20 2001 +++ linuxppc64_2_4/Documentation/cachetlb.txt Fri Sep 7 06:23:33 2001 @@ -260,8 +260,9 @@ Here is the new interface: - void copy_user_page(void *to, void *from, unsigned long address) - void clear_user_page(void *to, unsigned long address) + void copy_user_page(struct page *to, struct page *from, + unsigned long address) + void clear_user_page(struct page *to, unsigned long address) These two routines store data in user anonymous or COW pages. It allows a port to efficiently avoid D-cache alias @@ -279,6 +280,11 @@ If D-cache aliasing is not an issue, these two routines may simply call memcpy/memset directly and do nothing more. + + There are default versions of these procedures supplied in + include/linux/highmem.h. If a port does not want to use the + default versions it should declare them and define the symbol + __HAVE_ARCH_USER_PAGE in include/asm/page.h. void flush_dcache_page(struct page *page) diff -uNr --exclude=CVS linux-2.4.9-ac10/Documentation/filesystems/00-INDEX linuxppc64_2_4/Documentation/filesystems/00-INDEX --- linux-2.4.9-ac10/Documentation/filesystems/00-INDEX Wed Jun 20 13:10:27 2001 +++ linuxppc64_2_4/Documentation/filesystems/00-INDEX Thu Sep 13 14:29:38 2001 @@ -22,6 +22,8 @@ - info and mount options for the OS/2 HPFS. isofs.txt - info and mount options for the ISO 9660 (CDROM) filesystem. +jfs.txt + - info and mount options for the JFS filesystem. ncpfs.txt - info on Novell Netware(tm) filesystem using NCP protocol. ntfs.txt diff -uNr --exclude=CVS linux-2.4.9-ac10/Documentation/filesystems/changelog.jfs linuxppc64_2_4/Documentation/filesystems/changelog.jfs --- linux-2.4.9-ac10/Documentation/filesystems/changelog.jfs Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/Documentation/filesystems/changelog.jfs Thu Sep 13 14:31:38 2001 @@ -0,0 +1,594 @@ +IBM's Journaled File System (JFS) for Linux version 1.0.4 +Team members +Steve Best sbest@us.ibm.com +Dave Kleikamp shaggy@us.ibm.com +Barry Arndt barndt@us.ibm.com + + +Release August 31, 2001 (version 1.0.4) + +This is our forty-second release of IBM's Enterprise JFS technology port to Linux. +Beta 1 was release 0.1.0 on 12/8/2000, Beta 2 was release 0.2.0 on 3/7/2001, +Beta 3 was release 0.3.0 on 4/30/2001, and release 1.0.0 on 6/28/2001. + +The first drop on February 2, 2000 (jfs-0.0.1.tar.gz) had the following +functionality (enough to allow other developers to evaluate and potentially +contribute to this project): + + - MKFS functional and able to format an existing or newly created partition for JFS. + - mount/unmount functional + - able to view the self(.) and parent (..) directories entries. + +The second drop on February 28, 2000 (jfs-0.0.2.tar.gz) had fixes for MKFS and the +following utilities logredo, xchkdmp, xpeek are functional. The file system was able +to mkdir, create files and directories were working (and rm, rmdir). + +The third drop on March 24, 2000 (jfs-0.0.3.tar.gz) provided read and write capability +for the file system. Also, support for hard and soft links are functional. + +The fourth drop on March 29,2000 (jfs-0.0.4.tar.gz) provided rename capability for +the file system and fixes for write problems. + +The fifth drop on April 6, 2000 (jfs-0.0.5.tar.gz) provided mknod capability for the +file system. + +The sixth drop on April 21, 2000 (jfs-0.0.6.tar.gz) provided fixes for the file system +and utilities. + +The seventh drop on May 11, 2000 (jfs-0.0.7.tar.gz) provided changes to the file system +to be able to work on the development kernel 2.3.99-pre6. Drop 7 contains fixes for the +file system to be built within the kernel. + +The eighth drop on June 20, 2000 (jfs-0.0.8.tar.gz) provided fixes for the file system +and changes to the file system to be able to work on the development kernel 2.4.0-test1-ac21. +In the utility area: fsck and logdump are functional. + +The ninth drop on July 13, 2000 (jfs-0.0.9.tar.gz) has the meta-data of the file system +using the page cache. In the utility area: fixes for fsck.jfs and xpeek are included. + +The tenth drop on August 11, 2000 provided fixes for the file system +and changes to the file system to be able to work on the development kernel 2.4.0-test5. +In the utility area: fixes for fsck.jfs are included. The utility portion of extendfs has +been ported (still need to port the file system portion for extendfs). Man pages have +been written for the utilities. The compiler warnings have been removed from all of the +utilities. + +The eleventh drop on September 1, 2000 provided fixes for the file system +and changes to the file system to be able to work on the development kernel 2.4.0-test7. +The utility portion of defrag has been ported (still need to port the file system portion +for defrag). + +The twelfth drop on September 15, 2000 (jfs-0.0.12.tar.gz) provided the Transaction +Manager Log I/O routines. + +The thirteenth drop on September 29, 2000 (jfs-0.0.13.tar.gz) provided case-sensitive +support. In the utility area: fsck.jfs has the support for block/character special files. +Note: The JFS partition must be formatted/reformatted using mkfs.jfs created by drop 13 +for case-sensitive to work. + +The fourteenth drop on October 4, 2000 (jfs-0.0.14.tar.gz) included a fix to the file +system for not mounting a JFS partition if the size of the partition is > 4G on 2.2.x +series of the kernel. + +The fifteenth drop on October 9, 2000 (jfs-0.0.15.tar.gz) included the Transaction +Manager routines (Initialize, Begin, Locks, End). The file system has fixes and +changes to the file system to be able to work on the development kernel 2.4.0-test9. +The utility xchklog has been ported. + +The sixteenth drop on October 27, 2000 (jfs-0.0.16.tar.gz) included the Transaction +Manager routines (Commit, Buffer Management, Abort). The file system and utilities +have fixes included. + +The seventeenth drop on November 10, 2000 (jfs-0.0.17.tar.gz) has the transaction +sub operations back into the file system operations. The file system is now journaling +appropriate file system operations. The file system is temporary doing synchronous logging +(which slows down operations that are logged) to make sure that the transaction processing +is right. Asynchronous logging will be added shortly. The file system has fixes and changes +to the file system to be able to work on the development kernel 2.4.0-test10. + +The eighteenth drop on November 15, 2000 (jfs-0.0.18.tar.gz) included fixes to the file +system. + +The nineteenth drop on December 4, 2000 (jfs-0.0.19.tar.gz) is now doing asynchronous +logging. The file system has fixes and changes to the file system to be able to work +on the development kernel 2.4.0-test11. + +The twentieth drop on December 8, 2000 (jfs-0.1.0.tar.gz beta) included fixes to the file +system. + +The twenty-first drop on December 14, 2000 (jfs-0.1.1.tar.gz) included a fsck fix +to handle sparse files correctly. The file system has fixes and changes to the file +system to be able to work on the development kernel 2.4.0-test12 and 2.2.18 kernel. + +The twenty-second drop on January 5, 2001 (jfs-0.1.2.tar.gz) included fixes to the +file system. The file system has changes to able to work on the 2.4.0 kernel. + +The twenty-third drop on January 12, 2001 (jfs-0.1.3.tar.gz) included fixes to the +file system. Fsck now supports fifo correctly. + +The twenty-fourth drop on January 26, 2001 (jfs-0.1.4.tar.gz) included fixes to the +file system. The new feature in the file system is "Lazy Commit" which increases +performance while going asynchronous logging. The main makefile for the utilities +now has an install option. + +The twenty-fifth drop on February 7, 2001 (jfs-0.1.5.tar.gz) included fixes to the +file system. + +The twenty-sixth drop on February 26, 2001 (jfs-0.1.6.tar.gz) included fixes to the +file system. The log manager no longer uses the page cache for log pages, this +eliminates dead-locks that were occurring in the log manager. The file system has +general work done to remove SMP dead-lock problems. Fsck now supports default values +passed by fstab correctly. + +The twenty-seventh drop on March 7, 2001 (beta 2) (jfs-0.2.0.tar.gz) included fixes to the +file system. This drop contains jfsprogs.spec that can be used to create RPM for the +JFS utilities. The file system has general work done to remove SMP and UP hang related problems. +The file system performance has been increased by changes to extent inode cache. + +The twenty-eighth drop on March 21, 2001 (jfs-0.2.1.tar.gz) included fixes to the +file system. The file system has been changed to use standard types. + +The twenty-ninth drop on April 2, 2001 (jfs-0.2.2.tar.gz) included fixes to the +file system. The utilities have been changed to use standard types. + +The thirtieth drop on April 30, 2001 (jfs-0.3.0.tar.gz) included fixes to the +file system. The rest of the utilities have been changed to use standard types. +Both the file system and the utilities have been changed to use endian macros, +so that JFS will now store the meta-data as little endian when running on all +architectures, with this change you must re-format all of the JFS partitions +using the new mkfs.jfs included in this drop, if you are running on a big-endian system. + +The thirty-first drop on May 9, 2001 (jfs-0.3.1.tar.gz) included fixes to the file +system and the utilities. + +The thirty-second drop on May 18, 2001 (jfs-0.3.2-patch.tar.gz) included fixes to the file +system and the utilities. + +The thirty-third drop on May 25, 2001 (jfs-0.3.3-patch.tar.gz) included fixes to the file +system and the utilities. + +The thirty-fourth drop on June 8, 2001 (jfs-0.3.4-patch.tar.gz) included fixes to the +file system and the utilities. + +The thirty-fifth drop on June 15, 2001 (jfs-0.3.5-patch.tar.gz) includes fixes to the +file system and utilities. + +The thirty-sixth drop on June 22, 2001 (jfs-0.3.6-patch.tar.gz) includes fixes to the +file system and utilities. The change made to the file system to fix the rm -rf problem +is a disk layout change and with this change you must re-format all of the JFS partitions +using the new mkfs.jfs included in this drop to have the rm -rf problem fixed. + + +The thirty-seventh drop on June 25, 2001 (jfs-0.3.7-patch.tar.gz) includes fixes to the +file system and utilities. + +The thirty-eighth drop on June 28, 2001 (jfs-1.0.0-patch.tar.gz) includes fixes to the +file system and utilities. + +The thirty-ninth drop on July 10, 2001 (jfs-1.0.1-patch.tar.gz) includes fixes to the +file system and utilities. + +The fortieth drop on August 3, 2001 (jfs-1.0.2-patch.tar.gz) includes fixes to the +file system and utilities. + +The forty-first drop on August 20, 2001 (jfs-1.0.3-patch.tar.gz) includes fixes to the +file system and utilities. + + +The forty-second drop on August 31, 2001 (jfs-2.2-1.0.4-patch.tar.gz or +jfs-2.4-1.0.4-patch.tar.gz) includes fixes to the file system and utilities. + +Drop 42 has the temporary restriction that the block size must be 4K. MKFS.jfs defaults +the block size to 4K. + + +JFS today: + + +- Utilities: + Function in drop 1 + - MKFS.JFS builds on 2.2.12 + - MKFS successfully formats a new partition for JFS + + Function and Fixes in drop 2 + - MKFS supports all parameters now + - MKFS has fixes from release pre-alpha 0.0.1 + mkfs.jfs results in segmentation fault if no arguments are specified + mkfs.jfs hangs on invocation + + - XPEEK, utility to PEEK and POKE JFS on-disk data/structures + + Function and Fixes in drop 5 + - MKFS.jfs has a fix to support -l option correctly. + + Function and Fixes in drop 6 + - libfs has compiler warning fixes and a bug fix + + Function in drop 8 + - fsck.jfs successfully checks and repairs a jfs partition + - logdump, utility that dumps the contents of the journal log. + + Function and Fixes in drop 9 + - fixes for fsck.jfs and xpeek have been done. + + Function and Fixes in drop 10 + - fixes for fsck.jfs + - man pages for utilities + - extendfs utility part has been ported (still need to port FS portion) + - compiler warnings have been removed. + + Function and Fixes in drop 11 + - defrag utility part has been ported (still need to port FS portion) + + Function and Fixes in drop 13 + - fsck.jfs supports block special files and character special files (Jitterbug problem #28) + + Function and Fixes in drop 15 + - ported xchklog utility, extracts a log from fsck. + + Function and Fixes in drop 16 + - fixes for fsck.jfs to handle case-sensitive support correctly (Jitterbug problem #36) + - cleanup changes for the utilities + + Function and Fixes in drop 18 + - cleanup changes for the utilities + + Function and Fixes in drop 21 + - fix in fsck to handle sparse files correctly + + Function and Fixes in drop 23 + - fix in fsck to handle fifo correctly + + Function and Fixes in drop 24 + - man pages updates for the utilities + - install option for utilities + + Function and Fixes in drop 26 + - man page updates for fsck + - fsck now supports default options passed by fstab correctly + + Function and Fixes in drop 27 + - new jfsprogs.spec file + - fix in fsck for hard links + - fix for unicode conversion problem + + Function and Fixes in drop 29 + - cleanup changes for the utilities + - Code cleanup to use standard types + + Function and Fixes in drop 30 + - added endian macros support + - Code cleanup to use standard types (part 2) + - mkfs now clears out the 1st 4k bytes of the partition + + Function and Fixes in drop 31 + - completed endian macros support needed for xpeek + - added socket support for fsck + - minor bug fixes + + Function and Fixes in drop 32 + - Remove the warning message from fsck when partition is mounted read-only + + Function and Fixes in drop 33 + - Fix fsck to handle mount read-only correctly + - Fix top level utilities makefile to be able to easily overide version of gcc compiler + - Man pages are now available in html format + + Function and Fixes in drop 34 + - fsck fix to handle pre-existing lost+found sub dir + + Function and Fixes in drop 35 + - updated fsck error handling + - updated mkfs config options and the man page for fsck + + Function and Fixes in drop 36 + - Fixed jitterbug # 10 rm -rf fails on a big directory + + Function and Fixes in drop 38 + - Fixed small logredo problem + + Function and Fixes in drop 39 (1.0.1) + - Updated jfsprogs.spec file make it work on more distros + - Add force option to mkfs so the confirmation message isn't displayed + - Fixed fsck to handle index table on the root directory. + - Fixed error message displayed when partition was just created and haven't rebooted (jitterbug 130) + - Correctly place the built utilities in /sbin vs. /usr/sbin when using the make install + option. + + Note: If you have used the make install option to copy over the utilities from a + previous release then you should remove the following JFS utilities (fsck.jfs, logdump, + logredo, mkfs.jfs, xchkdmp, xchklog, and xpeek) that could be in /usr/sbin. This release + of make install places the utilities in the correct location which is /sbin. + + Function and Fixes in drop 40 (1.0.2) + - Fixed mkfs to display the correct error message if device name is not valid + or missing + - gzip the man pages and place /usr/share/man/man8 + - Fixed mkfs to properly setup buf_ai (caused Bus error with mkfs on SPARC Linux) + - Fixed fsck to display path correctly + + Function and Fixes in drop 41 (1.0.3) + - Fixed compiler warnings on 64 bit systems + - Created jfsutils package + + Function and Fixes in drop 42 (1.0.4) + - Fixed typecast problem causing intermittent fsck failures on 64 bit hardware (jitterbug 159) + - Fixed pointer calculation problem causing intermittent fsck failures on 64 bit hardware + - Fixed compiler warnings on s/390 and IA64 + - Fixed structure size mismatch between file system and utilities causing fsck problems when + large numbers of inodes are used + - Fixed seg fault in fsck when logging path lengths greater than 512 characters + - Fixed fsck printf format errors + + +- JFS: + Function in drop 1 + - builds on 2.2.12 + - successfully MOUNTs & UMOUNTs + - limited READ capability (i.e. LS is operational) + - other system commands (i.e. DF, CHOWN, CHMOD, etc. working limited) + + Function and Fixes in drop 2 + - Write capabilities are operational + - MKDIR + - CREATE file + - RMDIR + - RM + - Problems fixed from release pre-alpha 0.0.1 + using 2.2.14 jfs_imap.c:3004: `event' undeclared (first use in this function) + + Function and Fixes in drop 3 + - WRITE a file + - READ a file + - Support for hard and soft links + + Function and Fixes in drop 4 + - MV + - Executables can now be started from a jfs partition + - Problems while writing files have been fixed + + Function and Fixes in drop 5 + - added support for special files (mknod) + - alpha changes have been included + + Function and Fixes in drop 6 + - Jitterbug problem 9 du output is incorrect on jfs + - Jitterbug problem 11 unresolved symbol jfs_rwlock_lock on SMP build + + Function and Fixes in drop 7 + - moved JFS upto the 2.3.x development kernel (2.3.99-pre6) + - Jitterbug problem 14 can't build JFS within the kernel + + Function and Fixes in drop 8 + - moved JFS upto the 2.4.0 development kernel (2.4.0-test1-ac21) + - Jitterbug problem 17 undefined: jfs_rdwrlock_lock + - PowerPC build problem + + Function and Fixes in drop 9 + - moved JFS upto the 2.4.0 development kernel (2.4.0-test3) + - moved meta-data from buffer cache to page cache + - fixes for the file system are included + + Function and Fixes in drop 10 + - moved JFS upto the 2.4.0 development kernel (2.4.0-test5) + - fixes for the file system are included + + Function and Fixes in drop 11 + - moved JFS upto the 2.4.0 development kernel (2.4.0-test7) + - fixes for the file system are included + - start of journaling code has been included (jfs_txnmgr.h) + - WIP of log manager (jfs_logmgr.c) + + Function and Fixes in drop 12 + - Transaction Manager Log I/O -> Write log records is functional + + Function and Fixes in drop 13 + - case-sensitive support has been added + + Function and Fixes in drop 14 + - JFS mount error 22 is fixed (Jitterbug problem #30) + + Function and Fixes in drop 15 + - Following Transaction Manager routines are functional + Initialize -> Initialize transaction manager + Begin -> Start a transaction + Locks -> Acquire/release locks + End -> End a transaction + - moved JFS upto the 2.4.0 development kernel (2.4.0-test9) + - Fixed 2.2.x series block write problem. + + Function and Fixes in drop 16 + - Following Transaction Manager routines are functional + Commit -> Commit the transaction + Buffer Management -> Update inode, directory, extent + Abort -> Stop the commit from occurring + - File System build problem on 2.2.x series of the kernel (Jitterbug #35) + - Fixed case-sensitive bug in the filesystem (Jitterbug #36) + + + Function and Fixes in drop 17 + - Added transaction sub operations back into the file system operations. + - The file system is now journaling appropriate file system operations. + - moved JFS upto the 2.4.0 development kernel (2.4.0-test10) + + Function and Fixes in drop 18 + - symlink fix + - inode corruption problem + + Function and Fixes in drop 19 + - Added asynchronous logging support back into the file system. + - moved JFS upto the 2.4.0 development kernel (2.4.0-test11) + - Fixes to transaction related hang problems. + + Function and Fixes in drop 20 + - Fix to remove memory on module cleanup + + Function and Fixes in drop 21 + - Fix so fsck doesn't report the message + Secondary file/directory allocation structure(2) is not a correct redundant copy of primary structure. + - Fix for setup of finish_aync_io + - moved JFS upto the 2.4.0 development kernel (2.4.0-test12) + - moved JFS upto the 2.2.18 kernel + + Function and Fixes in drop 22 + + - moved JFS upto the 2.4.0 kernel + - Fix to do transaction processing for mknod correctly + - Fix for spinlock.h needed on 2.2.18 ppc + + Function and Fixes in drop 23 + - Fix for undefined BUG() in 2.2.x series + - Fix for 2.2 kernels, struct pipe_inode_info overlays the file system dependent portion of the inode structure. + + Function and Fixes in drop 24 + - rmmod jfs fix + - Implemented "Lazy Commit" - asynchronous logging enhancement to increase performance + - Removed ino_t from on-disk structures (fixes a mount problem) + + Function and Fixes in drop 25 + - Fix for deadlocks by putting IWRITE_LOCK/UNLOCK within jfs_delete_inode + - Fix to handle removing a link to an inode that isn't the last link. + - Fixes to general transaction processing SMP related hangs. + + Function and Fixes in drop 26 + - Fix for file writes on 2.2.x series of the kernel + - Change to log pages, so they no longer use the page cache. + - Fixes to general transaction processing SMP related dead-lock problems + + Function and Fixes in drop 27 + - Report error on read_metapage failure in jfs_readlink + - Report correct error in jfs_lookup if VFS fails to locate the inode + - Fix for a buffer overrun problem in jfs_readlink when compiled for linux-2.2 + - Fix to support fsync call correctly (jitterbug #57) + - Fixes to general SMP related dead-lock problems + - Fix to define BUG() if 2.2 and !i386 + - Fixes to general UP related hangs + - Changes to handling the inode extent cache increase the performance of the file system + + Function and Fixes in drop 28 + - Added ifdef IS_KIOBUFIO so JFS will be if kiobufs is in the kernel. Kiobufs has changed + the # of parameters for generic_make_request() + - Make jfsFYI a module parameter + insmod jfs.o jfsFYI=1 will turn on JFS FYI messages without having to recompile this + option is only supported on the 2.4.x kernel level. + - Defines cleanup_module() in terms of exit_jfs_fs() + - Fix to jfs_read_super, if get_empty_inode returns NULL cleanup correctly + - Code cleanup to use standard types + + + Function and Fixes in drop 29 + - Fix for assert(iagp->wmap[extno] & mask); (line #2875) in jfs_imap while running dbench + - Fixed hang on scsi + - added /proc/fs/jfs/jfsFYI (2.4.* kernels only) + echo 1 > /proc/fs/jfs/jfsFYI ; Turns on very verbose output to syslog + echo 0 > /proc/fs/jfs/jfsFYI ; Turns it back off + + Notes: + - IMPORTANT: If building jfs.o as a module on 2.2.* kernels, you will have to rebuild and + install the kernel to add wake_up_process to ksyms.c. Not needed on 2.4.* kernels. + + Function and Fixes in drop 30 + - Added endian macros support + - Fixed dbench hang + - jfs fails SPEC SFS validation on both NFSv2/NFSv3, fix to not add null-terminator to the symlink + value and accounting for it in the size. + - Fixed a bug where a casting of a 32 bit block # wasn't correct when a 64 bit value is needed. + xtLookup messages where display in the /var/log/messages (i.e of a message is below) + xtLookup: lstart (0x80000) >= size (0x80000) + + Notes: release 0.3.0 requires a re-format of the JFS partition, if the system architecture is + big-endian (i.e. s/390, PowerPC, etc.) + + Function and Fixes in drop 31 + - Removed max hard links check (showed up during cp -a /usr /jfs/usr) + - Fixed inode writing hang could have showed up running (dbench, iozone, etc), + the change was to prevent a deadlock during inode writing. + + Function and Fixes in drop 32 + - Fix for assert(mp->count) jfs_metapage.c 675! report as hardlink problem in drop 31 (dtDeleteUp + was discarding the wrong metapage_t. + - Fix seg fault problem while creating hard links. + - Fixed dbench hang do to transaction locks not being freed. + - Added support to correctly handle read-only and remounting the file system. + + Function and Fixes in drop 33 + + - Fixed statfs call to return the maximum number of inodes that JFS could allocate. (problem + reported as rpm exits with a (x) inodes needed message without installing the package). + - Fix to handle a case where a inode wasn't getting written to disk. + - Increase the performance of unlinking files. + - Fix to null terminate symlinks. + - General SMP fixes. + + Function and Fixes in drop 34 + + - Fixed to remove a hang waiting on inode (jitterbug #73) + - Fixed dbench hang on SMP 8-way + - Fixed a log sync problem, improved performance with this fix + + Function and Fixes in drop 35 + - Increase the performance of unlinking files, most unlinks are done asynchronously now + - Fixed "XT_GETPAGE: xtree page corrupt" during creating files on nfs mounted partition + + Function and Fixes in drop 36 + - Fixed jitterbug # 10 rm -rf fails on a big directory + + Notes: release 0.3.6 requires a re-format of the JFS partition, for the rm -rf problem + to be fixed. + + Function and Fixes in drop 37 + - Fixed find_entry called with index = 0 or 1 (jitterbug #126) + - Fixed the rm -rf case where if files weren't created sequential + then the rm -rf wasn't working correctly + + + Notes: If you used release 0.3.6 please re-format all JFS partitions, for the rm -rf problem + to be fixed. + + Function and Fixes in drop 38 + - Fixed some general log problems + + Function and Fixes in drop 39 (1.0.1) + - Fixed hang during copying files on 2.2.x series + - Fixed TxLock compile problem + - Fixed to correctly update the number of blocks for directories (this was causing the FS + to show fsck error after compiling mozilla). + - Fixed to prevent old data from being written to disk from the page cache. + + Function and Fixes in drop40 (1.0.2) + - Fixed multiple truncate hang + - Fixed hang on unlink a file and sync happening at the same time + - Improved handling of kmalloc error conditions + - Fixed hang in blk_get_queue and SMP deadlock: bh_end_io call generic_make_request + (jitterbug 145 and 146) + - stbl was not set correctly set in dtDelete + - changed trap to printk in dbAllocAG to avoid system hang + + Function and Fixes in drop41 (1.0.3) + - Patch to move from previous release to latest release needs to update the version number in super.c + - Jitterbug problems (134,140,152) removing files have been fixed + - Set rc=ENOSPC if ialloc fails in jfs_create and jfs_mkdir + - Fixed jfs_txnmgr.c 775! assert + - Fixed jfs_txnmgr.c 884! assert(mp->nohomeok==0) + - Fix hang - prevent tblocks from being exhausted + - Fix oops trying to mount reiserfs + - Fail more gracefully in jfs_imap.c + - Print more information when char2uni fails + - Fix timing problem between Block map and metapage cache - jitterbug 139 + - Code Cleanup (removed many ifdef's, obsolete code, ran code through indent) Mostly 2.4 tree + - Split source tree (Now have a separate source tree for 2.2, 2.4, and jfsutils) + + Function and Fixes in drop42 (1.0.4) + - Fixed compiler warnings in the FS when building on 64 bits systems + - Fixed deadlock where jfsCommit hung in hold_metapage + - Fixed problems with remount + - Reserve metapages for jfsCommit thread + - Get rid of buggy invalidate_metapage & use discard_metapage + - Don't hand metapages to jfsIOthread (too many context switches) (jitterbug 125, bugzilla 238) + - Fix error message in jfs_strtoUCS + + +Please send bugs, comments, cards and letters to linuxjfs@us.ibm.com. + +The JFS mailing list can be subscribed to by using the link labeled "Mail list Subscribe" +at our web page http://oss.software.ibm.com/jfs/. diff -uNr --exclude=CVS linux-2.4.9-ac10/Documentation/filesystems/jfs.txt linuxppc64_2_4/Documentation/filesystems/jfs.txt --- linux-2.4.9-ac10/Documentation/filesystems/jfs.txt Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/Documentation/filesystems/jfs.txt Thu Sep 13 14:29:38 2001 @@ -0,0 +1,153 @@ +IBM's Journaled File System (JFS) for Linux version 1.0.4 +Team members +Steve Best sbest@us.ibm.com +Dave Kleikamp shaggy@us.ibm.com +Barry Arndt barndt@us.ibm.com + + +Release August 31, 2001 (version 1.0.4) + +This is our forty-second release of IBM's Enterprise JFS technology port to Linux. +Beta 1 was release 0.1.0 on 12/8/2000, Beta 2 was release 0.2.0 on 3/7/2001, +Beta 3 was release 0.3.0 on 4/30/2001, and release 1.0.0 on 6/28/2001. + +The changelog.jfs file contains detailed information of changes done in each source +code drop. + +JFS has a source tree that can be built on 2.2.14 - 2.2.19 and 2.4.0 - 2.4.9 +kernel.org source trees. + +There is a anonymous cvs access available for the JFS tree. The steps below are +what is needed to pull the JFS cvs tree from the oss.software.ibm.com server. + +id anoncvs +password anoncvs + +To checkout 2.2.x series of the JFS files do the following: +CVSROOT should be set to :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jfs +cvs checkout linux-2.2.12 + +To checkout 2.4.x series of the JFS files do the following: +CVSROOT should be set to :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jfs +cvs checkout linux24 + +To checkout the JFS utilities do the following: +CVSROOT should be set to :pserver:anoncvs@oss.software.ibm.com:/usr/cvs/jfs +cvs checkout jfsutils + +The jfs-2.4-1.0.4-patch.tar.gz file contains a readme and patch files for different +levels of the 2.4 kernel. Please see the README in the jfs-2.4-1.0.4-patch.tar.gz +file for help on applying the two patch files. + +Similarly jfs-2.2-1.0.4-patch.tar.gz contains a readme and patch files for +different levels of the 2.2 kernel. + +The following files in the kernel source tree have been changed so JFS can be built. +The jfs-2.4-1.0.4.tar.gz source tar ball now contains each of the files below with +the extension of the kernel level it is associated with. As an example, there are now +three Config.in files named Config.in-2.4.0, Config.in-2.4.5, and Config.in-2.4.7. + +Similarly, the jfs-2.2-1.0.4.tar.gz source tar ball contains the files +Config.in-2.2.14, Config.in-2.2.16, and Config.in-2.2.18. + +If you use the tar ball to build JFS you must rename each of the kernel files to the +file names listed below. The standard kernel from www.kernel.org is the source of the +kernel files that are included in the jfs tar files. + +In sub dir fs Config.in, Makefile, filesystem.c +In sub dir fs/nls Config.in +In sub dir arch/i386 defconfig +In sub dir Documentation Configure.help +In sub dir Documentation/filesystems 00-INDEX +In sub dir include/linux fs.h +In sub dir linux MAINTAINERS +In sub dir linux/kernel/ksyms.c + +Please backup the above files before the JFS patch file is added to the kernel source +tree. There are three new header files in the sub dir include/linux named jfs_fs.h, +jfs_fs_i.h,and jfs_fs_sb.h. All other JFS files are located in the include/linux/jfs +or fs/jfs sub dirs. + +Our development team has used the Linux kernel levels 2.2.14-2.2.19 and +2.4.0 - 2.4.9 kernels with gcc version egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) +for our port so far. A goal of the JFS team is to have JFS run on all architectures +that Linux supports, there is no architecture specific code in JFS. JFS has been run +on the following architectures (x86, PowerPC, Alpha, s/390, ARM) so far. + +To make JFS build, during the "make config" step of building the kernel answer y to +the Prompt for development and/or incomplete code/drivers in the Code maturity level +options section. In the Filesystems section use the m for the answer to +JFS filesystem support (experimental) (CONFIG_JFS_FS) [Y/m/n?] + +Note: If you are use JFS as module on the 2.2.x series of the kernel you must rebuild +the kernel and run that rebuilt kernel for JFS to run. The reason for this change is +that JFS needs to export symbols. + +Build in /usr/src/linux with the command: + + +make modules +make modules_install + +If you rebuild jfs.o after having mounted and unmounted a partition, "modprobe -r jfs" +will unload the old module. + +For the file system debugging messages are being written to /var/log/messages. + +There are two ways to the build the JFS utilities, the first using the jfsprogs.spec +file and the second way is to run make and then make install in the +/jfsutils sub dir. + +To use the jfsprogs.spec located in sub dir jfsutils/SPECS you need to update +the version of JFS in the spec file, also the corresponding jfs-x.x.x-patch.tar.gz file +must be in the SOURCES directory of your system. Now it's time to begin the build. First, +change into the directory holding jfsprogs.spec file: + +cd /jfsutils/SPECS + +Next, start the build with a rpm -b command: + +rpm -ba jfsprogs.spec + +The a following the -b option directs RPM to perform all phases of the build process. + + +The second way to build the utilities for JFS (mkfs, xpeek, logredo, xchkdmp, fsck, logdump, xchklog). + +Build in /jfsutils with the command: + +make +make install + +One of results of this build is a program called mkfs.jfs. +To format a JFS partition use the following command. + + mkfs -t jfs device-name + +will result in the specified device being formatted. + + + +JFS TODO list: + +Plans for our near term development items + + - Fix for 2.2.* SMP kernel hangs + - get defrag capabilities operational in the FS + - get extendfs capabilities operational in the FS + - test EXTENDFS utility, for growing JFS partitions + - test defrag utility, calls file system to defrag the file system. + - add support for block sizes (512,1024,2048) + - add support for logfile on dedicated partition + + +Longer term work items + + - get access control list functionality operational + - get extended attributes functionality operational + - add quota support + +Please send bugs, comments, cards and letters to linuxjfs@us.ibm.com. + +The JFS mailing list can be subscribed to by using the link labeled "Mail list Subscribe" +at our web page http://oss.software.ibm.com/jfs/. diff -uNr --exclude=CVS linux-2.4.9-ac10/MAINTAINERS linuxppc64_2_4/MAINTAINERS --- linux-2.4.9-ac10/MAINTAINERS Wed Oct 3 12:11:06 2001 +++ linuxppc64_2_4/MAINTAINERS Fri Sep 28 15:44:12 2001 @@ -794,6 +794,13 @@ W: http://sources.redhat.com/jffs2/ S: Maintained +JFS FILESYSTEM +P: Dave Kleikamp +M: shaggy@austin.ibm.com +L: jfs-discussion@oss.software.ibm.com +W: http://oss.software.ibm.com/developerworks/opensource/jfs/ +S: Supported + JOYSTICK DRIVER P: Vojtech Pavlik M: vojtech@suse.cz @@ -869,14 +876,10 @@ S: Maintained LINUX FOR 64BIT POWERPC -P: Tom Gall -M: tom_gall@vnet.ibm.com P: David Engebretsen M: engebret@us.ibm.com -P: Dwayne McConnell -M: dwayne@austin.ibm.com W: http://linuxppc64.org -L: linuxppc64-dev@lists.linuxppc.porg +L: linuxppc64-dev@lists.linuxppc.org S: Supported LOGICAL DISK MANAGER SUPPORT (LDM, Windows 2000/XP Dynamic Disks) diff -uNr --exclude=CVS linux-2.4.9-ac10/Makefile linuxppc64_2_4/Makefile --- linux-2.4.9-ac10/Makefile Wed Oct 3 12:11:06 2001 +++ linuxppc64_2_4/Makefile Fri Sep 28 15:44:12 2001 @@ -10,11 +10,13 @@ # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, -# then ARCH is assigned, getting whatever value it gets normally, and +# then ARCH is assigned, getting whatever value it gets normally, and # SUBARCH is subsequently ignored. SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) -ARCH := $(SUBARCH) +#ARCH := $(SUBARCH) + +ARCH := ppc64 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ @@ -27,7 +29,7 @@ HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -CROSS_COMPILE = +CROSS_COMPILE = /usr/local/ppc64-current3.0/bin/powerpc64-linux- # # Include the make variables (CC, etc...) @@ -97,7 +99,7 @@ CPPFLAGS := -D__KERNEL__ -I$(HPATH) CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \ - -fomit-frame-pointer -fno-strict-aliasing -fno-common + -fomit-frame-pointer -fno-strict-aliasing -fno-common AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS) # @@ -160,6 +162,7 @@ DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o DRIVERS-$(CONFIG_FUSION_BOOT) += drivers/message/fusion/fusion.o DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o +DRIVERS-$(CONFIG_PPC_ISERIES) += drivers/iseries/iseries.o ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),) DRIVERS-y += drivers/cdrom/driver.o diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/i386/defconfig linuxppc64_2_4/arch/i386/defconfig --- linux-2.4.9-ac10/arch/i386/defconfig Wed Oct 3 12:11:06 2001 +++ linuxppc64_2_4/arch/i386/defconfig Fri Sep 28 15:44:14 2001 @@ -598,6 +598,7 @@ # CONFIG_RAMFS is not set CONFIG_ISO9660_FS=y # CONFIG_JOLIET is not set +# CONFIG_JFS_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc/8xx_io/micropatch.c linuxppc64_2_4/arch/ppc/8xx_io/micropatch.c --- linux-2.4.9-ac10/arch/ppc/8xx_io/micropatch.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc/8xx_io/micropatch.c Fri Sep 28 00:00:52 2001 @@ -0,0 +1,777 @@ + +/* Microcode patches for the CPM as supplied by Motorola. + * This is the one for IIC/SPI. There is a newer one that + * also relocates SMC2, but this would require additional changes + * to uart.c, so I am holding off on that for a moment. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "commproc.h" + +/* Define this to get SMC patches as well. You need to modify the uart + * driver as well...... +#define USE_SMC_PATCH 1 + */ + +#ifdef CONFIG_USB_MPC8xx +#define USE_USB_SOF_PATCH +#endif + +#ifdef USE_IIC_PATCH +#define PATCH_DEFINED + /* IIC/SPI */ +uint patch_2000[] = { + 0x7FFFEFD9, + 0x3FFD0000, + 0x7FFB49F7, + 0x7FF90000, + 0x5FEFADF7, + 0x5F89ADF7, + 0x5FEFAFF7, + 0x5F89AFF7, + 0x3A9CFBC8, + 0xE7C0EDF0, + 0x77C1E1BB, + 0xF4DC7F1D, + 0xABAD932F, + 0x4E08FDCF, + 0x6E0FAFF8, + 0x7CCF76CF, + 0xFD1FF9CF, + 0xABF88DC6, + 0xAB5679F7, + 0xB0937383, + 0xDFCE79F7, + 0xB091E6BB, + 0xE5BBE74F, + 0xB3FA6F0F, + 0x6FFB76CE, + 0xEE0DF9CF, + 0x2BFBEFEF, + 0xCFEEF9CF, + 0x76CEAD24, + 0x90B2DF9A, + 0x7FDDD0BF, + 0x4BF847FD, + 0x7CCF76CE, + 0xCFEF7E1F, + 0x7F1D7DFD, + 0xF0B6EF71, + 0x7FC177C1, + 0xFBC86079, + 0xE722FBC8, + 0x5FFFDFFF, + 0x5FB2FFFB, + 0xFBC8F3C8, + 0x94A67F01, + 0x7F1D5F39, + 0xAFE85F5E, + 0xFFDFDF96, + 0xCB9FAF7D, + 0x5FC1AFED, + 0x8C1C5FC1, + 0xAFDD5FC3, + 0xDF9A7EFD, + 0xB0B25FB2, + 0xFFFEABAD, + 0x5FB2FFFE, + 0x5FCE600B, + 0xE6BB600B, + 0x5FCEDFC6, + 0x27FBEFDF, + 0x5FC8CFDE, + 0x3A9CE7C0, + 0xEDF0F3C8, + 0x7F0154CD, + 0x7F1D2D3D, + 0x363A7570, + 0x7E0AF1CE, + 0x37EF2E68, + 0x7FEE10EC, + 0xADF8EFDE, + 0xCFEAE52F, + 0x7D0FE12B, + 0xF1CE5F65, + 0x7E0A4DF8, + 0xCFEA5F72, + 0x7D0BEFEE, + 0xCFEA5F74, + 0xE522EFDE, + 0x5F74CFDA, + 0x0B627385, + 0xDF627E0A, + 0x30D8145B, + 0xBFFFF3C8, + 0x5FFFDFFF, + 0xA7F85F5E, + 0xBFFE7F7D, + 0x10D31450, + 0x5F36BFFF, + 0xAF785F5E, + 0xBFFDA7F8, + 0x5F36BFFE, + 0x77FD30C0, + 0x4E08FDCF, + 0xE5FF6E0F, + 0xAFF87E1F, + 0x7E0FFD1F, + 0xF1CF5F1B, + 0xABF80D5E, + 0x5F5EFFEF, + 0x79F730A2, + 0xAFDD5F34, + 0x47F85F34, + 0xAFED7FDD, + 0x50B24978, + 0x47FD7F1D, + 0x7DFD70AD, + 0xEF717EC1, + 0x6BA47F01, + 0x2D267EFD, + 0x30DE5F5E, + 0xFFFD5F5E, + 0xFFEF5F5E, + 0xFFDF0CA0, + 0xAFED0A9E, + 0xAFDD0C3A, + 0x5F3AAFBD, + 0x7FBDB082, + 0x5F8247F8 +}; + +uint patch_2f00[] = { + 0x3E303430, + 0x34343737, + 0xABF7BF9B, + 0x994B4FBD, + 0xBD599493, + 0x349FFF37, + 0xFB9B177D, + 0xD9936956, + 0xBBFDD697, + 0xBDD2FD11, + 0x31DB9BB3, + 0x63139637, + 0x93733693, + 0x193137F7, + 0x331737AF, + 0x7BB9B999, + 0xBB197957, + 0x7FDFD3D5, + 0x73B773F7, + 0x37933B99, + 0x1D115316, + 0x99315315, + 0x31694BF4, + 0xFBDBD359, + 0x31497353, + 0x76956D69, + 0x7B9D9693, + 0x13131979, + 0x79376935 +}; +#endif + +#ifdef USE_SMC_PATCH +#define PATCH_DEFINED +/* SMC2/IIC/SPI Patch */ +/* This is the area from 0x2000 to 0x23ff. +*/ +uint patch_2000[] = { + 0x3fff0000, + 0x3ffd0000, + 0x3ffb0000, + 0x3ff90000, + 0x5f13eff8, + 0x5eb5eff8, + 0x5f88adf7, + 0x5fefadf7, + 0x3a9cfbc8, + 0x77cae1bb, + 0xf4de7fad, + 0xabae9330, + 0x4e08fdcf, + 0x6e0faff8, + 0x7ccf76cf, + 0xfdaff9cf, + 0xabf88dc8, + 0xab5879f7, + 0xb0925d8d, + 0xdfd079f7, + 0xb090e6bb, + 0xe5bbe74f, + 0x9e046f0f, + 0x6ffb76ce, + 0xee0cf9cf, + 0x2bfbefef, + 0xcfeef9cf, + 0x76cead23, + 0x90b3df99, + 0x7fddd0c1, + 0x4bf847fd, + 0x7ccf76ce, + 0xcfef77ca, + 0x7eaf7fad, + 0x7dfdf0b7, + 0xef7a7fca, + 0x77cafbc8, + 0x6079e722, + 0xfbc85fff, + 0xdfff5fb3, + 0xfffbfbc8, + 0xf3c894a5, + 0xe7c9edf9, + 0x7f9a7fad, + 0x5f36afe8, + 0x5f5bffdf, + 0xdf95cb9e, + 0xaf7d5fc3, + 0xafed8c1b, + 0x5fc3afdd, + 0x5fc5df99, + 0x7efdb0b3, + 0x5fb3fffe, + 0xabae5fb3, + 0xfffe5fd0, + 0x600be6bb, + 0x600b5fd0, + 0xdfc827fb, + 0xefdf5fca, + 0xcfde3a9c, + 0xe7c9edf9, + 0xf3c87f9e, + 0x54ca7fed, + 0x2d3a3637, + 0x756f7e9a, + 0xf1ce37ef, + 0x2e677fee, + 0x10ebadf8, + 0xefdecfea, + 0xe52f7d9f, + 0xe12bf1ce, + 0x5f647e9a, + 0x4df8cfea, + 0x5f717d9b, + 0xefeecfea, + 0x5f73e522, + 0xefde5f73, + 0xcfda0b61, + 0x5d8fdf61, + 0xe7c9edf9, + 0x7e9a30d5, + 0x1458bfff, + 0xf3c85fff, + 0xdfffa7f8, + 0x5f5bbffe, + 0x7f7d10d0, + 0x144d5f33, + 0xbfffaf78, + 0x5f5bbffd, + 0xa7f85f33, + 0xbffe77fd, + 0x30bd4e08, + 0xfdcfe5ff, + 0x6e0faff8, + 0x7eef7e9f, + 0xfdeff1cf, + 0x5f17abf8, + 0x0d5b5f5b, + 0xffef79f7, + 0x309eafdd, + 0x5f3147f8, + 0x5f31afed, + 0x7fdd50af, + 0x497847fd, + 0x7f9e7fed, + 0x7dfd70a9, + 0xef7e7ece, + 0x6ba07f9e, + 0x2d227efd, + 0x30db5f5b, + 0xfffd5f5b, + 0xffef5f5b, + 0xffdf0c9c, + 0xafed0a9a, + 0xafdd0c37, + 0x5f37afbd, + 0x7fbdb081, + 0x5f8147f8, + 0x3a11e710, + 0xedf0ccdd, + 0xf3186d0a, + 0x7f0e5f06, + 0x7fedbb38, + 0x3afe7468, + 0x7fedf4fc, + 0x8ffbb951, + 0xb85f77fd, + 0xb0df5ddd, + 0xdefe7fed, + 0x90e1e74d, + 0x6f0dcbf7, + 0xe7decfed, + 0xcb74cfed, + 0xcfeddf6d, + 0x91714f74, + 0x5dd2deef, + 0x9e04e7df, + 0xefbb6ffb, + 0xe7ef7f0e, + 0x9e097fed, + 0xebdbeffa, + 0xeb54affb, + 0x7fea90d7, + 0x7e0cf0c3, + 0xbffff318, + 0x5fffdfff, + 0xac59efea, + 0x7fce1ee5, + 0xe2ff5ee1, + 0xaffbe2ff, + 0x5ee3affb, + 0xf9cc7d0f, + 0xaef8770f, + 0x7d0fb0c6, + 0xeffbbfff, + 0xcfef5ede, + 0x7d0fbfff, + 0x5ede4cf8, + 0x7fddd0bf, + 0x49f847fd, + 0x7efdf0bb, + 0x7fedfffd, + 0x7dfdf0b7, + 0xef7e7e1e, + 0x5ede7f0e, + 0x3a11e710, + 0xedf0ccab, + 0xfb18ad2e, + 0x1ea9bbb8, + 0x74283b7e, + 0x73c2e4bb, + 0x2ada4fb8, + 0xdc21e4bb, + 0xb2a1ffbf, + 0x5e2c43f8, + 0xfc87e1bb, + 0xe74ffd91, + 0x6f0f4fe8, + 0xc7ba32e2, + 0xf396efeb, + 0x600b4f78, + 0xe5bb760b, + 0x53acaef8, + 0x4ef88b0e, + 0xcfef9e09, + 0xabf8751f, + 0xefef5bac, + 0x741f4fe8, + 0x751e760d, + 0x7fdbf081, + 0x741cafce, + 0xefcc7fce, + 0x751e70ac, + 0x741ce7bb, + 0x3372cfed, + 0xafdbefeb, + 0xe5bb760b, + 0x53f2aef8, + 0xafe8e7eb, + 0x4bf8771e, + 0x7e247fed, + 0x4fcbe2cc, + 0x7fbc30a9, + 0x7b0f7a0f, + 0x34d577fd, + 0x308b5db7, + 0xde553e5f, + 0xaf78741f, + 0x741f30f0, + 0xcfef5e2c, + 0x741f3eac, + 0xafb8771e, + 0x5e677fed, + 0x0bd3e2cc, + 0x741ccfec, + 0xe5ca53cd, + 0x6fcb4f74, + 0x5dadde4b, + 0x2ab63d38, + 0x4bb3de30, + 0x751f741c, + 0x6c42effa, + 0xefea7fce, + 0x6ffc30be, + 0xefec3fca, + 0x30b3de2e, + 0xadf85d9e, + 0xaf7daefd, + 0x5d9ede2e, + 0x5d9eafdd, + 0x761f10ac, + 0x1da07efd, + 0x30adfffe, + 0x4908fb18, + 0x5fffdfff, + 0xafbb709b, + 0x4ef85e67, + 0xadf814ad, + 0x7a0f70ad, + 0xcfef50ad, + 0x7a0fde30, + 0x5da0afed, + 0x3c12780f, + 0xefef780f, + 0xefef790f, + 0xa7f85e0f, + 0xffef790f, + 0xefef790f, + 0x14adde2e, + 0x5d9eadfd, + 0x5e2dfffb, + 0xe79addfd, + 0xeff96079, + 0x607ae79a, + 0xddfceff9, + 0x60795dff, + 0x607acfef, + 0xefefefdf, + 0xefbfef7f, + 0xeeffedff, + 0xebffe7ff, + 0xafefafdf, + 0xafbfaf7f, + 0xaeffadff, + 0xabffa7ff, + 0x6fef6fdf, + 0x6fbf6f7f, + 0x6eff6dff, + 0x6bff67ff, + 0x2fef2fdf, + 0x2fbf2f7f, + 0x2eff2dff, + 0x2bff27ff, + 0x4e08fd1f, + 0xe5ff6e0f, + 0xaff87eef, + 0x7e0ffdef, + 0xf11f6079, + 0xabf8f542, + 0x7e0af11c, + 0x37cfae3a, + 0x7fec90be, + 0xadf8efdc, + 0xcfeae52f, + 0x7d0fe12b, + 0xf11c6079, + 0x7e0a4df8, + 0xcfea5dc4, + 0x7d0befec, + 0xcfea5dc6, + 0xe522efdc, + 0x5dc6cfda, + 0x4e08fd1f, + 0x6e0faff8, + 0x7c1f761f, + 0xfdeff91f, + 0x6079abf8, + 0x761cee24, + 0xf91f2bfb, + 0xefefcfec, + 0xf91f6079, + 0x761c27fb, + 0xefdf5da7, + 0xcfdc7fdd, + 0xd09c4bf8, + 0x47fd7c1f, + 0x761ccfcf, + 0x7eef7fed, + 0x7dfdf093, + 0xef7e7f1e, + 0x771efb18, + 0x6079e722, + 0xe6bbe5bb, + 0xae0ae5bb, + 0x600bae85, + 0xe2bbe2bb, + 0xe2bbe2bb, + 0xaf02e2bb, + 0xe2bb2ff9, + 0x6079e2bb +}; + + /* This is from 0x2f00 to 0x2fff + */ +uint patch_2f00[] = { + 0x30303030, + 0x3e3e3434, + 0xabbf9b99, + 0x4b4fbdbd, + 0x59949334, + 0x9fff37fb, + 0x9b177dd9, + 0x936956bb, + 0xfbdd697b, + 0xdd2fd113, + 0x1db9f7bb, + 0x36313963, + 0x79373369, + 0x3193137f, + 0x7331737a, + 0xf7bb9b99, + 0x9bb19795, + 0x77fdfd3d, + 0x573b773f, + 0x737933f7, + 0xb991d115, + 0x31699315, + 0x31531694, + 0xbf4fbdbd, + 0x35931497, + 0x35376956, + 0xbd697b9d, + 0x96931313, + 0x19797937, + 0x6935af78, + 0xb9b3baa3, + 0xb8788683, + 0x368f78f7, + 0x87778733, + 0x3ffffb3b, + 0x8e8f78b8, + 0x1d118e13, + 0xf3ff3f8b, + 0x6bd8e173, + 0xd1366856, + 0x68d1687b, + 0x3daf78b8, + 0x3a3a3f87, + 0x8f81378f, + 0xf876f887, + 0x77fd8778, + 0x737de8d6, + 0xbbf8bfff, + 0xd8df87f7, + 0xfd876f7b, + 0x8bfff8bd, + 0x8683387d, + 0xb873d87b, + 0x3b8fd7f8, + 0xf7338883, + 0xbb8ee1f8, + 0xef837377, + 0x3337b836, + 0x817d11f8, + 0x7378b878, + 0xd3368b7d, + 0xed731b7d, + 0x833731f3, + 0xf22f3f23 +}; + +uint patch_2e00[] = { + /* This is from 0x2e00 to 0x2e3c + */ + 0x27eeeeee, + 0xeeeeeeee, + 0xeeeeeeee, + 0xeeeeeeee, + 0xee4bf4fb, + 0xdbd259bb, + 0x1979577f, + 0xdfd2d573, + 0xb773f737, + 0x4b4fbdbd, + 0x25b9b177, + 0xd2d17376, + 0x956bbfdd, + 0x697bdd2f, + 0xff9f79ff, + 0xff9ff22f +}; +#endif + +#ifdef USE_USB_SOF_PATCH +#define PATCH_DEFINED +uint patch_2000[] = { + 0x7fff0000, + 0x7ffd0000, + 0x7ffb0000, + 0x49f7ba5b, + 0xba383ffb, + 0xf9b8b46d, + 0xe5ab4e07, + 0xaf77bffe, + 0x3f7bbf79, + 0xba5bba38, + 0xe7676076, + 0x60750000 +}; + +uint patch_2f00[] = { + 0x3030304c, + 0xcab9e441, + 0xa1aaf220 +}; +#endif + +/* Load the microcode patch. This is called early in the CPM initialization + * with the controller in the reset state. We enable the processor after + * we load the patch. + */ +void +cpm_load_patch(volatile immap_t *immr) +{ +#ifdef PATCH_DEFINED + volatile uint *dp; + volatile cpm8xx_t *commproc; + volatile iic_t *iip; + volatile spi_t *spp; + int i; + + commproc = (cpm8xx_t *)&immr->im_cpm; + + /* We work closely with commproc.c. We know it only allocates + * from data only space. + * For this particular patch, we only use the bottom 512 bytes + * and the upper 256 byte extension. We will use the space + * starting at 1K for the relocated parameters, as the general + * CPM allocation won't come from that area. + */ + commproc->cp_rccr = 0; + + /* Copy the patch into DPRAM. + */ + dp = (uint *)(commproc->cp_dpmem); + for (i=0; i<(sizeof(patch_2000)/4); i++) + *dp++ = patch_2000[i]; + + dp = (uint *)&(commproc->cp_dpmem[0x0f00]); + for (i=0; i<(sizeof(patch_2f00)/4); i++) + *dp++ = patch_2f00[i]; + +#ifdef USE_USB_SOF_PATCH +#if 0 /* usb patch should not relocate iic */ + iip = (iic_t *)&commproc->cp_dparam[PROFF_IIC]; +#define RPBASE 0x0030 + iip->iic_rpbase = RPBASE; + + /* Put SPI above the IIC, also 32-byte aligned. + */ + i = (RPBASE + sizeof(iic_t) + 31) & ~31; + spp = (spi_t *)&commproc->cp_dparam[PROFF_SPI]; + spp->spi_rpbase = i; +#endif + + /* Enable uCode fetches from DPRAM. */ + commproc->cp_rccr = 0x0009; + + printk("USB uCode patch installed\n"); +#endif /* USE_USB_SOF_PATCH */ + +#if defined(USE_SMC_PATCH) || defined(USE_IIC_PATCH) + + iip = (iic_t *)&commproc->cp_dparam[PROFF_IIC]; +#define RPBASE 0x0400 + iip->iic_rpbase = RPBASE; + + /* Put SPI above the IIC, also 32-byte aligned. + */ + i = (RPBASE + sizeof(iic_t) + 31) & ~31; + spp = (spi_t *)&commproc->cp_dparam[PROFF_SPI]; + spp->spi_rpbase = i; + +#ifdef USE_SMC_PATCH + dp = (uint *)&(commproc->cp_dpmem[0x0e00]); + for (i=0; i<(sizeof(patch_2e00)/4); i++) + *dp++ = patch_2e00[i]; + + /* Enable the traps to get to it. + */ + commproc->cp_cpmcr1 = 0x8080; + commproc->cp_cpmcr2 = 0x808a; + commproc->cp_cpmcr3 = 0x8028; + commproc->cp_cpmcr4 = 0x802a; + + /* Enable uCode fetches from DPRAM. + */ + commproc->cp_rccr = 3; +#endif + +#ifdef USE_IIC_PATCH + /* Enable the traps to get to it. + */ + commproc->cp_cpmcr1 = 0x802a; + commproc->cp_cpmcr2 = 0x8028; + commproc->cp_cpmcr3 = 0x802e; + commproc->cp_cpmcr4 = 0x802c; + + /* Enable uCode fetches from DPRAM. + */ + commproc->cp_rccr = 1; + + printk("I2C uCode patch installed\n"); +#endif + + /* Relocate the IIC and SPI parameter areas. These have to + * aligned on 32-byte boundaries. + */ + iip = (iic_t *)&commproc->cp_dparam[PROFF_IIC]; + iip->iic_rpbase = RPBASE; + + /* Put SPI above the IIC, also 32-byte aligned. + */ + i = (RPBASE + sizeof(iic_t) + 31) & ~31; + spp = (spi_t *)&commproc->cp_dparam[PROFF_SPI]; + spp->spi_rpbase = i; + +#endif /* USE_SMC_PATCH || USE_IIC_PATCH */ +#endif /* PATCH_DEFINED */ +} + +void +verify_patch(volatile immap_t *immr) +{ +#ifdef PATCH_DEFINED + volatile uint *dp; + volatile cpm8xx_t *commproc; + int i; + + commproc = (cpm8xx_t *)&immr->im_cpm; + + printk("cp_rccr %x\n", commproc->cp_rccr); + commproc->cp_rccr = 0; + + dp = (uint *)(commproc->cp_dpmem); + for (i=0; i<(sizeof(patch_2000)/4); i++) + if (*dp++ != patch_2000[i]) { + printk("patch_2000 bad at %d\n", i); + dp--; + printk("found 0x%X, wanted 0x%X\n", *dp, patch_2000[i]); + break; + } + + dp = (uint *)&(commproc->cp_dpmem[0x0f00]); + for (i=0; i<(sizeof(patch_2f00)/4); i++) + if (*dp++ != patch_2f00[i]) { + printk("patch_2f00 bad at %d\n", i); + dp--; + printk("found 0x%X, wanted 0x%X\n", *dp, patch_2f00[i]); + break; + } + + commproc->cp_rccr = 0x0009; +#endif /* PATCH_DEFINED */ +} + diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/Makefile linuxppc64_2_4/arch/ppc64/Makefile --- linux-2.4.9-ac10/arch/ppc64/Makefile Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/Makefile Wed Sep 19 18:11:47 2001 @@ -19,13 +19,19 @@ CHECKS = checks endif +# Re: -DPPC64_32B_ADDR_SPACE in CPPLAGS/CFLAGS below. +# For 64-bit apps, temporarily reduce the size of the address space +# available to user application. This allow us to use strace without +# having to compile a strace64 program. This shouldn't affect anyone +# other than Steve Munroe, Peter Bergner. I will back this hack out +# later... -Peter + ASFLAGS = LINKFLAGS = -T arch/ppc64/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic -CPPFLAGS := $(CPPFLAGS) -D__powerpc__ -include $(TOPDIR)/arch/ppc64/mymacros.h -CFLAGS := $(CFLAGS) -D__linux__ -D__powerpc__ -fsigned-char -Wa,-Saix \ +CPPFLAGS := $(CPPFLAGS) -D__powerpc__ -DPPC64_32B_ADDR_SPACE +CFLAGS := $(CFLAGS) -D__linux__ -D__powerpc__ -fsigned-char \ -msoft-float -pipe -Wno-uninitialized $(PRINTK) \ - -include $(TOPDIR)/arch/ppc64/mymacros.h -mminimal-toc \ - -fno-builtin + -mminimal-toc -fno-builtin -DPPC64_32B_ADDR_SPACE CPP = $(CC) -E $(CFLAGS) @@ -53,29 +59,21 @@ BOOT_TARGETS = zImage znetboot.initrd zImage.initrd ifdef CONFIG_PPC_PSERIES -$(BOOT_TARGETS): $(CHECKS) vmlinux +$(BOOT_TARGETS): vmlinux @$(MAKEBOOT) $@ -znetboot: $(CHECKS) vmlinux -ifdef CONFIG_ALL_PPC +znetboot: vmlinux ifdef CONFIG_SMP cp -f vmlinux /tftpboot/vmlinux.smp else - cp -f vmlinux /tftpboot/vmlinux.smp.64 -endif + cp -f vmlinux /tftpboot/vmlinux endif @$(MAKEBOOT) $@ endif -.PHONY: clean_config -clean_config: +%_config: arch/ppc64/configs/%_defconfig rm -f .config arch/ppc64/defconfig - -chrp_config: clean_config - cp -f arch/ppc64/configs/chrp_defconfig arch/ppc64/defconfig - -common_config: clean_config - cp -f arch/ppc64/configs/common_defconfig arch/ppc64/defconfig + cp -f arch/ppc64/configs/$(@:config=defconfig) arch/ppc64/defconfig archclean: rm -f arch/ppc64/kernel/{ppc_defs.h,checks,mk_defs.s,mk_defs_out.c,mk_defs_tpl} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/boot/Makefile linuxppc64_2_4/arch/ppc64/boot/Makefile --- linux-2.4.9-ac10/arch/ppc64/boot/Makefile Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/boot/Makefile Tue Sep 18 15:10:37 2001 @@ -29,7 +29,7 @@ $(BOOTCC) $(BOOTAFLAGS) -traditional -c -o $*.o $< CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS -LD_ARGS = -Ttext 0x00400000 +LD_ARGS = -Ttext 0x00400000 -e _start OBJS = crt0.o start.o main.o zlib.o image.o imagesize.o #LIBS = $(TOPDIR)/lib/lib.a @@ -41,11 +41,27 @@ TFTPIMAGE=/tftpboot/zImage.chrp endif + +ifeq ($(CONFIG_PPC_ISERIES),y) +all: vmlinux.sm +else all: $(TOPDIR)/zImage +endif + znetboot: zImage cp zImage $(TFTPIMAGE) + +ifeq ($(CONFIG_PPC_ISERIES),y) +addSystemMap: addSystemMap.c + $(HOSTCC) $(HOSTCFLAGS) -o addSystemMap addSystemMap.c + +vmlinux.sm: $(TOPDIR)/vmlinux addSystemMap + ./addSystemMap $(TOPDIR)/System.map $(TOPDIR)/vmlinux vmlinux.sm +endif + + znetboot.initrd: zImage.initrd cp zImage.initrd $(TFTPIMAGE) @@ -84,7 +100,7 @@ imagesize.c: vmlinux.gz clean: - rm -f piggyback note addnote $(OBJS) zImage vmlinux.gz no_initrd.o + rm -f piggyback note addnote $(OBJS) zImage zImage.initrd vmlinux.gz no_initrd.o imagesize.c addSystemMap vmlinux.sm fastdep: $(TOPDIR)/scripts/mkdep *.[Sch] > .depend diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/boot/addSystemMap.c linuxppc64_2_4/arch/ppc64/boot/addSystemMap.c --- linux-2.4.9-ac10/arch/ppc64/boot/addSystemMap.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/addSystemMap.c Mon Oct 1 08:19:37 2001 @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include + +void xlate( char * inb, char * trb, unsigned len ) +{ + unsigned i; + for ( i=0; i> 4; + char c2 = c & 0xf; + if ( c1 > 9 ) + c1 = c1 + 'A' - 10; + else + c1 = c1 + '0'; + if ( c2 > 9 ) + c2 = c2 + 'A' - 10; + else + c2 = c2 + '0'; + *trb++ = c1; + *trb++ = c2; + } + *trb = 0; +} + +#define ElfHeaderSize (64 * 1024) +#define ElfPages (ElfHeaderSize / 4096) + +void get4k( /*istream *inf*/FILE *file, char *buf ) +{ + unsigned j; + unsigned num = fread(buf, 1, 4096, file); + for ( j=num; j<4096; ++j ) + buf[j] = 0; +} + +void put4k( /*ostream *outf*/FILE *file, char *buf ) +{ + fwrite(buf, 1, 4096, file); +} + +int main(int argc, char **argv) +{ + char inbuf[4096]; + FILE *sysmap = NULL; + char* ptr_end = NULL; + FILE *inputVmlinux = NULL; + FILE *outputVmlinux = NULL; + unsigned i = 0; + unsigned long sysmapFileLen = 0; + unsigned long sysmapLen = 0; + unsigned long roundR = 0; + unsigned long kernelLen = 0; + unsigned long actualKernelLen = 0; + unsigned long round = 0; + unsigned long roundedKernelLen = 0; + unsigned long sysmapStartOffs = 0; + unsigned long sysmapPages = 0; + unsigned long roundedKernelPages = 0; + unsigned long padPages = 0; + if ( argc < 2 ) + { + printf("Name of System Map file missing.\n"); + exit(1); + } + + if ( argc < 3 ) + { + printf("Name of vmlinux file missing.\n"); + exit(1); + } + + if ( argc < 4 ) + { + printf("Name of vmlinux output file missing.\n"); + exit(1); + } + + sysmap = fopen(argv[1], "r"); + if ( ! sysmap ) + { + printf("System Map file \"%s\" failed to open.\n", argv[1]); + exit(1); + } + inputVmlinux = fopen(argv[2], "r"); + if ( ! inputVmlinux ) + { + printf("vmlinux file \"%s\" failed to open.\n", argv[2]); + exit(1); + } + outputVmlinux = fopen(argv[3], "w"); + if ( ! outputVmlinux ) + { + printf("output vmlinux file \"%s\" failed to open.\n", argv[3]); + exit(1); + } + + + + fseek(inputVmlinux, 0, SEEK_END); + kernelLen = ftell(inputVmlinux); + fseek(inputVmlinux, 0, SEEK_SET); + printf("kernel file size = %ld\n", kernelLen); + if ( kernelLen == 0 ) + { + printf("You must have a linux kernel specified as argv[2]\n"); + exit(1); + } + + + actualKernelLen = kernelLen - ElfHeaderSize; + + printf("actual kernel length (minus ELF header) = %ld\n", actualKernelLen); + + round = actualKernelLen % 4096; + roundedKernelLen = actualKernelLen; + if ( round ) + roundedKernelLen += (4096 - round); + + printf("Kernel length rounded up to a 4k multiple = %ld\n", roundedKernelLen); + roundedKernelPages = roundedKernelLen / 4096; + printf("Kernel pages to copy = %ld\n", roundedKernelPages); + + + + /* Sysmap file */ + fseek(sysmap, 0, SEEK_END); + sysmapFileLen = ftell(sysmap); + fseek(sysmap, 0, SEEK_SET); + printf("%s file size = %ld\n", argv[1], sysmapFileLen); + + sysmapLen = sysmapFileLen; + + roundR = 4096 - (sysmapLen % 4096); + if (roundR) + { + printf("Rounding System Map file up to a multiple of 4096, adding %ld\n", roundR); + sysmapLen += roundR; + } + printf("Rounded System Map size is %ld\n", sysmapLen); + + /* Process the Sysmap file to determine the true end of the kernel */ + sysmapPages = sysmapLen / 4096; + printf("System map pages to copy = %ld\n", sysmapPages); + for (i=0; i #include #include +#include #include -#define _ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1))) extern void *finddevice(const char *); extern int getprop(void *, const char *, void *, int); @@ -25,16 +24,15 @@ void *claim(unsigned int, unsigned int, unsigned int); void flush_cache(void *, int); void pause(void); +static struct bi_record *make_bi_recs(unsigned long); #define RAM_START 0x00000000 #define RAM_END (64<<20) #define BOOT_START ((unsigned long)_start) -#define BOOT_END ((unsigned long)(_end + 0xFFF) & ~0xFFF) - -#define RAM_FREE ((unsigned long)(_end+0x1000)&~0xFFF) +#define BOOT_END ((unsigned long)_end) -// Value picked to match that used by yaboot +/* Value picked to match that used by yaboot */ #define PROG_START 0x01400000 char *avail_ram; @@ -42,6 +40,8 @@ char *avail_high; unsigned int heap_use; unsigned int heap_max; +unsigned long initrd_start = 0; +unsigned long initrd_size = 0; extern char _end[]; extern char image_data[]; @@ -54,32 +54,37 @@ static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ +typedef void (*kernel_entry_t)( unsigned long, + unsigned long, + void *, + struct bi_record *); + void -chrpboot(int a1, int a2, void *prom) +chrpboot(unsigned long a1, unsigned long a2, void *prom) { - unsigned sa, len; - void *dst, *claim_addr; + unsigned len; + void *dst = (void *)-1; + unsigned long claim_addr; unsigned char *im; - unsigned initrd_start, initrd_size; extern char _start; + struct bi_record *bi_recs; + kernel_entry_t kernel_entry; printf("chrpboot starting: loaded at 0x%x\n\r", (unsigned)&_start); - printf(" initrd_len = 0x%x\n\r", (unsigned)initrd_len); if (initrd_len) { initrd_size = initrd_len; initrd_start = (RAM_END - initrd_size) & ~0xFFF; - a1 = initrd_start; - a2 = initrd_size; + a1 = a2 = 0; claim(initrd_start, RAM_END - initrd_start, 0); - printf("initial ramdisk moving 0x%x <- 0x%x (%x bytes)\n\r", - initrd_start, (unsigned)initrd_data, initrd_size); + printf("initial ramdisk moving 0x%lx <- 0x%lx (%lx bytes)\n\r", + initrd_start, (unsigned long)initrd_data, initrd_size); memcpy((void *)initrd_start, (void *)initrd_data, initrd_size); } im = image_data; len = image_len; - uncompressed_size = _ALIGN(uncompressed_size, (1<<12)); /* page align it */ + uncompressed_size = PAGE_ALIGN(uncompressed_size); for(claim_addr = PROG_START; claim_addr <= PROG_START * 8; @@ -108,48 +113,72 @@ } flush_cache(dst, len); - make_bi_recs((unsigned long)dst + len); - sa = (unsigned long)dst; - printf("start address = 0x%x\n\r", (unsigned) sa); + bi_recs = make_bi_recs((unsigned long)dst + len); + + kernel_entry = (kernel_entry_t)dst; + printf( "kernel:\n\r" + " entry addr = 0x%lx\n\r" + " a1 = 0x%lx,\n\r" + " a2 = 0x%lx,\n\r" + " prom = 0x%lx,\n\r" + " bi_recs = 0x%lx,\n\r", + (unsigned long)kernel_entry, a1, a2, + (unsigned long)prom, (unsigned long)bi_recs); - (*(void (*)(int, int, void *))sa)(a1, a2, prom); + kernel_entry( a1, a2, prom, bi_recs ); printf("returned?\n\r"); pause(); } -void make_bi_recs(unsigned long addr) +static struct bi_record * +make_bi_recs(unsigned long addr) { + struct bi_record *bi_recs; struct bi_record *rec; - - rec = (struct bi_record *)_ALIGN(addr + (1<<20) -1, (1<<20)); + bi_recs = rec = bi_rec_init(addr); + + rec = bi_rec_alloc(rec, 1); rec->tag = BI_FIRST; - rec->size = sizeof(struct bi_record); - rec = (struct bi_record *)((unsigned long)rec + rec->size); - + /* rec->data[0] = ...; # Written below before return */ + + rec = bi_rec_alloc_bytes(rec, strlen("chrpboot")+1); rec->tag = BI_BOOTLOADER_ID; sprintf( (char *)rec->data, "chrpboot"); - rec->size = sizeof(struct bi_record) + strlen("chrpboot") + 1; - rec = (struct bi_record *)((unsigned long)rec + rec->size); - + + rec = bi_rec_alloc(rec, 2); rec->tag = BI_MACHTYPE; - rec->data[0] = _MACH_chrp; + rec->data[0] = _MACH_pSeries; rec->data[1] = 1; - rec->size = sizeof(struct bi_record) + sizeof(unsigned long); - rec = (struct bi_record *)((unsigned long)rec + rec->size); + + if ( initrd_size > 0 ) { + rec = bi_rec_alloc(rec, 2); + rec->tag = BI_INITRD; + rec->data[0] = initrd_start; + rec->data[1] = initrd_size; + } + #if 0 - rec->tag = BI_SYSMAP; - rec->data[0] = (unsigned long)sysmap_data; - rec->data[1] = sysmap_len; - rec->size = sizeof(struct bi_record) + sizeof(unsigned long); - rec = (struct bi_record *)((unsigned long)rec + rec->size); + if ( sysmap_len > 0 ) { + rec = bi_rec_alloc(rec, 2); + rec->tag = BI_SYSMAP; + rec->data[0] = (unsigned long)sysmap_data; + rec->data[1] = sysmap_len; + } #endif + + rec = bi_rec_alloc(rec, 0); rec->tag = BI_LAST; - rec->size = sizeof(struct bi_record); - rec = (struct bi_record *)((unsigned long)rec + rec->size); + + /* Save the _end_ address of the bi_rec's in the first bi_rec + * data field for easy access by the kernel. + */ + bi_recs->data[0] = (bi_rec_field)rec + rec->size; + + return bi_recs; } struct memchunk { @@ -212,47 +241,48 @@ void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) { - z_stream s; - int r, i, flags; + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n\r"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n\r"); + exit(); + } - /* skip header */ - i = 10; - flags = src[3]; - if (src[2] != DEFLATED || (flags & RESERVED) != 0) { - printf("bad gzipped data\n\r"); - exit(); - } - if ((flags & EXTRA_FIELD) != 0) - i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) - i += 2; - if (i >= *lenp) { - printf("gunzip: ran out of data in header\n\r"); - exit(); - } - - s.zalloc = zalloc; - s.zfree = zfree; - r = inflateInit2(&s, -MAX_WBITS); - if (r != Z_OK) { - printf("inflateInit2 returned %d\n\r", r); - exit(); - } - s.next_in = src + i; - s.avail_in = *lenp - i; - s.next_out = dst; - s.avail_out = dstlen; - r = inflate(&s, Z_FINISH); - if (r != Z_OK && r != Z_STREAM_END) { - printf("inflate returned %d msg: %s\n\r", r, s.msg); - exit(); - } - *lenp = s.next_out - (unsigned char *) dst; - inflateEnd(&s); + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n\r", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d msg: %s\n\r", r, s.msg); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); } + diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/boot/start.c linuxppc64_2_4/arch/ppc64/boot/start.c --- linux-2.4.9-ac10/arch/ppc64/boot/start.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/boot/start.c Thu Sep 13 14:13:35 2001 @@ -30,200 +30,200 @@ void start(int a1, int a2, void *promptr) { - prom = (int (*)(void *)) promptr; - chosen_handle = finddevice("/chosen"); - if (chosen_handle == (void *) -1) - exit(); - if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) - exit(); - stderr = stdout; - if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) + prom = (int (*)(void *)) promptr; + chosen_handle = finddevice("/chosen"); + if (chosen_handle == (void *) -1) + exit(); + if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) + exit(); + stderr = stdout; + if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) exit(); - chrpboot(a1, a2, promptr); - for (;;) - exit(); + chrpboot(a1, a2, promptr); + for (;;) + exit(); } int write(void *handle, void *ptr, int nb) { - struct prom_args { - char *service; - int nargs; - int nret; - void *ihandle; - void *addr; - int len; - int actual; - } args; - - args.service = "write"; - args.nargs = 3; - args.nret = 1; - args.ihandle = handle; - args.addr = ptr; - args.len = nb; - args.actual = -1; - (*prom)(&args); - return args.actual; + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; } int read(void *handle, void *ptr, int nb) { - struct prom_args { - char *service; - int nargs; - int nret; - void *ihandle; - void *addr; - int len; - int actual; - } args; - - args.service = "read"; - args.nargs = 3; - args.nret = 1; - args.ihandle = handle; - args.addr = ptr; - args.len = nb; - args.actual = -1; - (*prom)(&args); - return args.actual; + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; } void exit() { - struct prom_args { - char *service; - } args; + struct prom_args { + char *service; + } args; - for (;;) { - args.service = "exit"; - (*prom)(&args); - } + for (;;) { + args.service = "exit"; + (*prom)(&args); + } } void pause(void) { - struct prom_args { - char *service; - } args; + struct prom_args { + char *service; + } args; - args.service = "enter"; - (*prom)(&args); + args.service = "enter"; + (*prom)(&args); } void * finddevice(const char *name) { - struct prom_args { - char *service; - int nargs; - int nret; - const char *devspec; - void *phandle; - } args; - - args.service = "finddevice"; - args.nargs = 1; - args.nret = 1; - args.devspec = name; - args.phandle = (void *) -1; - (*prom)(&args); - return args.phandle; + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + void *phandle; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.phandle = (void *) -1; + (*prom)(&args); + return args.phandle; } void * -claim(unsigned int virt, unsigned int size, unsigned int align) +claim(unsigned long virt, unsigned long size, unsigned long align) { - struct prom_args { - char *service; - int nargs; - int nret; - unsigned int virt; - unsigned int size; - unsigned int align; - void *ret; - } args; - - args.service = "claim"; - args.nargs = 3; - args.nret = 1; - args.virt = virt; - args.size = size; - args.align = align; - (*prom)(&args); - return args.ret; + struct prom_args { + char *service; + int nargs; + int nret; + unsigned int virt; + unsigned int size; + unsigned int align; + void *ret; + } args; + + args.service = "claim"; + args.nargs = 3; + args.nret = 1; + args.virt = virt; + args.size = size; + args.align = align; + (*prom)(&args); + return args.ret; } int getprop(void *phandle, const char *name, void *buf, int buflen) { - struct prom_args { - char *service; - int nargs; - int nret; - void *phandle; - const char *name; - void *buf; - int buflen; - int size; - } args; - - args.service = "getprop"; - args.nargs = 4; - args.nret = 1; - args.phandle = phandle; - args.name = name; - args.buf = buf; - args.buflen = buflen; - args.size = -1; - (*prom)(&args); - return args.size; + struct prom_args { + char *service; + int nargs; + int nret; + void *phandle; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.phandle = phandle; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*prom)(&args); + return args.size; } int putc(int c, void *f) { - char ch = c; + char ch = c; - if (c == '\n') - putc('\r', f); - return write(f, &ch, 1) == 1? c: -1; + if (c == '\n') + putc('\r', f); + return write(f, &ch, 1) == 1? c: -1; } int putchar(int c) { - return putc(c, stdout); + return putc(c, stdout); } int fputs(char *str, void *f) { - int n = strlen(str); + int n = strlen(str); - return write(f, str, n) == n? 0: -1; + return write(f, str, n) == n? 0: -1; } int readchar(void) { - char ch; + char ch; - for (;;) { - switch (read(stdin, &ch, 1)) { - case 1: - return ch; - case -1: - printk("read(stdin) returned -1\r\n"); - return -1; + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\r\n"); + return -1; + } } - } } static char line[256]; @@ -233,53 +233,53 @@ int getchar(void) { - int c; + int c; - if (lineleft == 0) { - lineptr = line; - for (;;) { - c = readchar(); - if (c == -1 || c == 4) - break; - if (c == '\r' || c == '\n') { - *lineptr++ = '\n'; - putchar('\n'); - break; - } - switch (c) { - case 0177: - case '\b': - if (lineptr > line) { - putchar('\b'); - putchar(' '); - putchar('\b'); - --lineptr; - } - break; - case 'U' & 0x1F: - while (lineptr > line) { - putchar('\b'); - putchar(' '); - putchar('\b'); - --lineptr; - } - break; - default: - if (lineptr >= &line[sizeof(line) - 1]) - putchar('\a'); - else { - putchar(c); - *lineptr++ = c; + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } } - } + lineleft = lineptr - line; + lineptr = line; } - lineleft = lineptr - line; - lineptr = line; - } - if (lineleft == 0) - return -1; - --lineleft; - return *lineptr++; + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/boot/zImage.lds linuxppc64_2_4/arch/ppc64/boot/zImage.lds --- linux-2.4.9-ac10/arch/ppc64/boot/zImage.lds Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/boot/zImage.lds Tue Sep 11 08:57:14 2001 @@ -33,6 +33,7 @@ *(.fixup) *(.got1) } + . = ALIGN(4096); _etext = .; PROVIDE (etext = .); .rodata : @@ -45,7 +46,7 @@ .ctors : { *(.ctors) } .dtors : { *(.dtors) } /* Read-write section, merged into data segment: */ - . = (. + 0x0FFF) & 0xFFFFF000; + . = ALIGN(4096); .data : { *(.data) @@ -56,11 +57,13 @@ *(.dynamic) CONSTRUCTORS } + . = ALIGN(4096); _edata = .; PROVIDE (edata = .); .fixup : { *(.fixup) } + . = ALIGN(4096); __bss_start = .; .bss : { @@ -69,6 +72,7 @@ *(.bss) *(COMMON) } + . = ALIGN(4096); _end = . ; PROVIDE (end = .); } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/config.in linuxppc64_2_4/arch/ppc64/config.in --- linux-2.4.9-ac10/arch/ppc64/config.in Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/config.in Thu Sep 27 14:00:26 2001 @@ -25,6 +25,9 @@ iSeries CONFIG_PPC_ISERIES" CONFIG_PPC_PSERIES bool 'Symmetric multi-processing support' CONFIG_SMP +if [ "$CONFIG_SMP" = "y" ]; then + bool ' Distribute interrupts on all CPUs by default' CONFIG_IRQ_ALL_CPUS +fi if [ "$CONFIG_PPC_ISERIES" = "y" ]; then define_bool CONFIG_MSCHUNKS y else @@ -151,6 +154,42 @@ comment 'Console drivers' source drivers/video/Config.in endmenu + + +if [ "$CONFIG_PPC_ISERIES" = "y" ]; then +mainmenu_option next_comment +comment 'iSeries device drivers' + dep_tristate 'iSeries Virtual Console Support' CONFIG_VIOCONS $CONFIG_PPC_ISERIES + dep_tristate 'iSeries Virtual I/O disk support' CONFIG_VIODASD $CONFIG_PPC_ISERIES + if [ "$CONFIG_VIODASD" = "y" -o "$CONFIG_VIODASD" = "m" ]; then + bool 'iSeries Virtual disk IDE emulation' CONFIG_VIODASD_IDE + fi + dep_tristate 'iSeries Virtual I/O CD support' CONFIG_VIOCD $CONFIG_PPC_ISERIES + if [ "$CONFIG_VIOCD" = "y" -o "$CONFIG_VIOCD" = "m" ]; then + bool 'iSeries Virtual CD Aztech emulation' CONFIG_VIOCD_AZTECH + fi + dep_tristate 'iSeries Virtual Tape Support' CONFIG_VIOTAPE $CONFIG_PPC_ISERIES + dep_tristate 'iSeries Virtual Ethernet driver support' CONFIG_VETH $CONFIG_PPC_ISERIES +endmenu +fi + +if [ "$CONFIG_VIOCONS" = "n" ]; then + if [ "$CONFIG_VIODASD" = "n" ]; then + if [ "$CONFIG_VIOTAPE" = "n" ]; then + if [ "$CONFIG_VIOCD" = "n" ]; then + define_bool CONFIG_VIOPATH n + else + define_bool CONFIG_VIOPATH y + fi + else + define_bool CONFIG_VIOPATH y + fi + else + define_bool CONFIG_VIOPATH y + fi +else + define_bool CONFIG_VIOPATH y +fi source drivers/char/Config.in source fs/Config.in diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig linuxppc64_2_4/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig --- linux-2.4.9-ac10/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/configs/iSeries_nodevfs_ideemul_defconfig Mon Oct 1 10:30:21 2001 @@ -0,0 +1,573 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC64=y +CONFIG_ALL_PPC=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_PPC_PSERIES is not set +CONFIG_PPC_ISERIES=y +CONFIG_SMP=y +CONFIG_MSCHUNKS=y + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# General setup +# +# CONFIG_ISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +# CONFIG_EISA is not set +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_ELF32=y +CONFIG_BINFMT_MISC=y +CONFIG_PCI_NAMES=y +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set +# CONFIG_PNPBIOS is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_VIODASD=y +CONFIG_VIODASD_IDE=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=64000 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +CONFIG_BLK_DEV_LVM=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +CONFIG_RTNETLINK=y +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +CONFIG_FILTER=y +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_RTNETLINK=y +CONFIG_NETLINK=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_NAT=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_TOS=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_ROUTE_LARGE_TABLES=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=y +CONFIG_NET_IPGRE=y +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +CONFIG_SYN_COOKIES=y +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_MACE is not set +# CONFIG_BMAC is not set +# CONFIG_GMAC is not set +# CONFIG_OAKNET is not set +# CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set +# CONFIG_SUNBMAC is not set +# CONFIG_SUNQE is not set +# CONFIG_SUNLANCE is not set +# CONFIG_SUNGEM is not set +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +CONFIG_VETH=y +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +CONFIG_CD_NO_IDESCSI=y +# CONFIG_AZTCD is not set +# CONFIG_GSCD is not set +# CONFIG_SBPCD is not set +# CONFIG_MCD is not set +# CONFIG_MCDX is not set +# CONFIG_OPTCD is not set +# CONFIG_CM206 is not set +# CONFIG_SJCD is not set +# CONFIG_ISP16_CDI is not set +# CONFIG_CDU31A is not set +# CONFIG_CDU535 is not set +CONFIG_VIOCD=y +# CONFIG_VIOCD_AZTECH is not set + +# +# Console drivers +# + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +CONFIG_SERIAL_NONSTANDARD=y +# CONFIG_COMPUTONE is not set +# CONFIG_ROCKETPORT is not set +# CONFIG_CYCLADES is not set +# CONFIG_DIGIEPCA is not set +# CONFIG_DIGI is not set +# CONFIG_ESPSERIAL is not set +# CONFIG_MOXA_INTELLIO is not set +# CONFIG_MOXA_SMARTIO is not set +# CONFIG_ISI is not set +# CONFIG_SYNCLINK is not set +# CONFIG_N_HDLC is not set +# CONFIG_RISCOM8 is not set +# CONFIG_SPECIALIX is not set +# CONFIG_SX is not set +# CONFIG_RIO is not set +# CONFIG_STALDRV is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=1024 +CONFIG_VIOCONS=y + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set +# CONFIG_INPUT_NS558 is not set +# CONFIG_INPUT_LIGHTNING is not set +# CONFIG_INPUT_PCIGAME is not set +# CONFIG_INPUT_CS461X is not set +# CONFIG_INPUT_EMU10K1 is not set +# CONFIG_INPUT_SERIO is not set +# CONFIG_INPUT_SERPORT is not set +# CONFIG_INPUT_ANALOG is not set +# CONFIG_INPUT_A3D is not set +# CONFIG_INPUT_ADI is not set +# CONFIG_INPUT_COBRA is not set +# CONFIG_INPUT_GF2K is not set +# CONFIG_INPUT_GRIP is not set +# CONFIG_INPUT_INTERACT is not set +# CONFIG_INPUT_TMDC is not set +# CONFIG_INPUT_SIDEWINDER is not set +# CONFIG_INPUT_IFORCE_USB is not set +# CONFIG_INPUT_IFORCE_232 is not set +# CONFIG_INPUT_WARRIOR is not set +# CONFIG_INPUT_MAGELLAN is not set +# CONFIG_INPUT_SPACEORB is not set +# CONFIG_INPUT_SPACEBALL is not set +# CONFIG_INPUT_STINGER is not set +# CONFIG_INPUT_DB9 is not set +# CONFIG_INPUT_GAMECON is not set +# CONFIG_INPUT_TURBOGRAFX is not set +# CONFIG_QIC02_TAPE is not set +CONFIG_VIOTAPE=y + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_SONYPI is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# File systems +# +CONFIG_QUOTA=y +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_CMS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_MINIX_FS is not set +# CONFIG_FREEVXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +CONFIG_ROMFS_FS=y +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +CONFIG_NFSD_V3=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_SMB_FS=y +# CONFIG_SMB_NLS_DEFAULT is not set +CONFIG_NCP_FS=y +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +CONFIG_SMB_NLS=y +CONFIG_NLS=y + +# +# Native Language Support +# +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# 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_1251 is not set +CONFIG_NLS_ISO8859_1=y +# 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=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set +# CONFIG_USB_UHCI is not set +# CONFIG_USB_UHCI_ALT is not set +# CONFIG_USB_OHCI is not set +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH is not set +# CONFIG_USB_STORAGE is not set +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +# CONFIG_USB_HID is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_DC2XX is not set +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_SCANNER is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_PWC is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_PLUSB is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_CATC is not set +# CONFIG_USB_CDCETHER is not set +# CONFIG_USB_USBNET is not set +# CONFIG_USB_USS720 is not set + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +# CONFIG_USB_SERIAL_PL2303 is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_OMNINET is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_ID75 is not set + +# +# Kernel hacking +# +CONFIG_MAGIC_SYSRQ=y +# CONFIG_KGDB is not set +# CONFIG_XMON is not set +# CONFIG_PPCDBG is not set diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/configs/pSeries_defconfig linuxppc64_2_4/arch/ppc64/configs/pSeries_defconfig --- linux-2.4.9-ac10/arch/ppc64/configs/pSeries_defconfig Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/configs/pSeries_defconfig Tue Sep 25 14:26:35 2001 @@ -20,12 +20,16 @@ # CONFIG_PPC_ISERIES is not set CONFIG_PPC_PSERIES=y CONFIG_SMP=y +CONFIG_IRQ_ALL_CPUS=y CONFIG_MSCHUNKS=y # # Loadable module support # CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + # # General setup diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/defconfig linuxppc64_2_4/arch/ppc64/defconfig --- linux-2.4.9-ac10/arch/ppc64/defconfig Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/defconfig Tue Sep 25 08:11:38 2001 @@ -20,12 +20,16 @@ # CONFIG_PPC_ISERIES is not set CONFIG_PPC_PSERIES=y CONFIG_SMP=y +CONFIG_IRQ_ALL_CPUS=y CONFIG_MSCHUNKS=y # # Loadable module support # CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KMOD=y + # # General setup diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/HvLpConfig.c linuxppc64_2_4/arch/ppc64/kernel/HvLpConfig.c --- linux-2.4.9-ac10/arch/ppc64/kernel/HvLpConfig.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/HvLpConfig.c Mon Sep 24 18:25:38 2001 @@ -26,38 +26,3 @@ return HvLpConfig_getLpIndex(); } -const HvLpIndexMap HvLpIndexMapDefault = (1 << (sizeof(HvLpIndexMap) * 8 - 1)); -const HvLpIndex HvHardcodedPrimaryLpIndex = 0; -const HvLpIndex HvMaxArchitectedLps = HVMAXARCHITECTEDLPS; -const HvLpVirtualLanIndex HvMaxArchitectedVirtualLans = 16; -const HvLpSharedPoolIndex HvMaxArchitectedSharedPools = 16; -const HvLpSharedPoolIndex HvMaxSupportedSharedPools = 1; -const HvLpIndex HvMaxRuntimeLpsPreCondor = 12; -const HvLpIndex HvMaxRuntimeLps = HVMAXARCHITECTEDLPS; -const HvLpIndex HvLpIndexInvalid = 0xff; -const u16 HvInvalidProcIndex = 0xffff; -const u32 HvVirtualFlashSize = 0x200; -const u32 HvMaxBusesPreCondor = 32; -const u32 HvMaxBusesCondor = 256; -const u32 HvMaxArchitectedBuses = 512; -const HvLpBus HvCspBusNumber = 1; -const u32 HvMaxSanHwSets = 16; -const HvLpCard HvMaxSystemIops = 200; -const HvLpCard HvMaxBusIops = 20; -const u16 HvMaxUnitsPerIop = 100; -const u64 HvPageSize = 4 * 1024; -const u64 HvChunkSize = HVCHUNKSIZE; -const u64 HvChunksPerMeg = HVCHUNKSPERMEG; -const u64 HvPagesPerChunk = HVPAGESPERCHUNK; -const u64 HvPagesPerMeg = HVPAGESPERMEG; -const u64 HvLpMinMegsPrimary = HVLPMINMEGSPRIMARY; -const u64 HvLpMinMegsSecondary = HVLPMINMEGSSECONDARY; -const u64 HvLpMinChunksPrimary = HVLPMINMEGSPRIMARY * HVCHUNKSPERMEG; -const u64 HvLpMinChunksSecondary = HVLPMINMEGSSECONDARY * HVCHUNKSPERMEG; -const u64 HvLpMinPagesPrimary = HVLPMINMEGSPRIMARY * HVPAGESPERMEG; -const u64 HvLpMinPagesSecondary = HVLPMINMEGSSECONDARY * HVPAGESPERMEG; -const u8 HvLpMinProcs = 1; -const u8 HvLpConfigMinInteract = 1; -const u16 HvLpMinSharedProcUnitsX100 = 10; -const u16 HvLpMaxSharedProcUnitsX100 = 100; -const HvLpSharedPoolIndex HvLpSharedPoolIndexInvalid = 0xff; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ItLpQueue.c linuxppc64_2_4/arch/ppc64/kernel/ItLpQueue.c --- linux-2.4.9-ac10/arch/ppc64/kernel/ItLpQueue.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ItLpQueue.c Tue Sep 18 14:27:22 2001 @@ -10,14 +10,43 @@ #include #include +#include #include +#include #include #include #include #include +static __inline__ int set_inUse( struct ItLpQueue * lpQueue ) +{ + int t; + u32 * inUseP = &(lpQueue->xInUseWord); + + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 \n\ + cmpi 0,%0,0 \n\ + li %0,0 \n\ + bne- 2f \n\ + addi %0,%0,1 \n\ + stwcx. %0,0,%2 \n\ + bne- 1b \n\ +2: eieio" + : "=&r" (t), "=m" (lpQueue->xInUseWord) + : "r" (inUseP), "m" (lpQueue->xInUseWord) + : "cc"); + + return t; +} + +static __inline__ void clear_inUse( struct ItLpQueue * lpQueue ) +{ + lpQueue->xInUseWord = 0; +} + /* Array of LpEvent handler functions */ extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned long ItLpQueueInProcess = 0; struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue ) { @@ -76,7 +105,16 @@ { unsigned numIntsProcessed = 0; struct HvLpEvent * nextLpEvent; + + /* If we have recursed, just return */ + if ( !set_inUse( lpQueue ) ) + return 0; + if (ItLpQueueInProcess == 0) + ItLpQueueInProcess = 1; + else + BUG(); + for (;;) { nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue ); if ( nextLpEvent ) { @@ -119,5 +157,12 @@ else /* If nothing left then we are done */ break; } + + ItLpQueueInProcess = 0; + mb(); + clear_inUse( lpQueue ); + + get_paca()->lpEvent_count += numIntsProcessed; + return numIntsProcessed; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/LparData.c linuxppc64_2_4/arch/ppc64/kernel/LparData.c --- linux-2.4.9-ac10/arch/ppc64/kernel/LparData.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/LparData.c Wed Sep 19 11:23:56 2001 @@ -71,7 +71,7 @@ 0, /* shared processors */ 0, /* HMT allowed */ 6, /* TEMP: This allows non-GA driver */ - 3, /* We are v5r1m0 */ + 4, /* We are v5r2m0 */ 3, /* Min supported PLIC = v5r1m0 */ 3, /* Min usuable PLIC = v5r1m0 */ { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4 "*/ diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/Makefile linuxppc64_2_4/arch/ppc64/kernel/Makefile --- linux-2.4.9-ac10/arch/ppc64/kernel/Makefile Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/Makefile Tue Oct 2 07:20:07 2001 @@ -25,15 +25,18 @@ LparData.o udbg.o binfmt_elf32.o sys_ppc32.o sys32.o \ ioctl32.o ptrace32.o signal32.o open_pic.o xics.o \ pmc.o mf_proc.o proc_pmc.o proc_pcifr.o iSeries_setup.o \ - ItLpQueue.o hvCall.o mf.o viopath.o HvLpEvent.o \ - iSeries_proc.o HvCall.o flight_recorder.o HvLpConfig.o + ItLpQueue.o hvCall.o mf.o HvLpEvent.o ras.o \ + iSeries_proc.o HvCall.o flight_recorder.o HvLpConfig.o \ + rtc.o -obj-$(CONFIG_PCI) += pci.o +obj-$(CONFIG_PCI) += pci.o pci_dn.o pci_dma.o ifeq ($(CONFIG_PPC_ISERIES),y) -obj-$(CONFIG_PCI) += iSeries_dma.o iSeries_rtc.o -else -obj-$(CONFIG_PCI) += pci_dma.o +obj-$(CONFIG_PCI) += iSeries_pci.o iSeries_pci_reset.o iSeries_IoMmTable.o iSeries_irq.o iSeries_VpdInfo.o XmPciLpEvent.o +endif +ifeq ($(CONFIG_PPC_PSERIES),y) +obj-$(CONFIG_PCI) += pSeries_pci.o +obj-y += rtasd.o endif obj-$(CONFIG_KGDB) += ppc-stub.o @@ -46,8 +49,8 @@ endif ifeq ($(CONFIG_ALL_PPC),y) - obj-y += prom.o lmb.o rtas.o rtas-eventscan.o rtas-proc.o chrp_setup.o \ - chrp_time.o pSeries_pci.o i8259.o + obj-y += prom.o lmb.o rtas.o rtas-proc.o chrp_setup.o \ + i8259.o endif include $(TOPDIR)/Rules.make diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/XmPciLpEvent.c linuxppc64_2_4/arch/ppc64/kernel/XmPciLpEvent.c --- linux-2.4.9-ac10/arch/ppc64/kernel/XmPciLpEvent.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/XmPciLpEvent.c Thu Sep 27 11:19:20 2001 @@ -0,0 +1,155 @@ +/* + * File XmPciLpEvent.h created by Wayne Holm on Mon Jan 15 2001. + * + * This module handles PCI interrupt events sent by the iSeries Hypervisor. +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +long Pci_Interrupt_Count = 0; +long Pci_Event_Count = 0; + +enum XmPciLpEvent_Subtype { + XmPciLpEvent_BusCreated = 0, // PHB has been created + XmPciLpEvent_BusFailed = 1, // PHB has failed + XmPciLpEvent_BusRecovered = 12, // PHB has been recovered + XmPciLpEvent_NodeFailed = 4, // Multi-adapter bridge has failed + XmPciLpEvent_NodeRecovered = 5, // Multi-adapter bridge has recovered + XmPciLpEvent_SlotInterrupt = 22 // Slot interrupt +}; + +struct XmPciLpEvent_BusInterrupt { + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; +}; + +struct XmPciLpEvent_NodeInterrupt { + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; + HvAgentId deviceId; +}; + +struct XmPciLpEvent { + struct HvLpEvent hvLpEvent; + + union { + u64 alignData; // Align on an 8-byte boundary + + struct { + u32 fisr; + HvBusNumber busNumber; + HvSubBusNumber subBusNumber; + HvAgentId deviceId; + } slotInterrupt; + + struct XmPciLpEvent_BusInterrupt busFailed; + struct XmPciLpEvent_BusInterrupt busRecovered; + struct XmPciLpEvent_BusInterrupt busCreated; + + struct XmPciLpEvent_NodeInterrupt nodeFailed; + struct XmPciLpEvent_NodeInterrupt nodeRecovered; + + } eventData; + +}; + +static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm); + +static void XmPciLpEvent_handler( struct HvLpEvent* eventParm, struct pt_regs* regsParm) { + //PPCDBG(PPCDBG_BUSWALK,"XmPciLpEvent_handler, type 0x%x\n",eventParm->xType ); + ++Pci_Event_Count; + + if (eventParm && eventParm->xType == HvLpEvent_Type_PciIo) { + switch( eventParm->xFlags.xFunction ) { + case HvLpEvent_Function_Int: + intReceived( (struct XmPciLpEvent*)eventParm, regsParm ); + break; + case HvLpEvent_Function_Ack: + printk(KERN_ERR "XmPciLpEvent.c: unexpected ack received\n"); + break; + default: + printk(KERN_ERR "XmPciLpEvent.c: unexpected event function %d\n",(int)eventParm->xFlags.xFunction); + break; + } + } + else if (event) { + printk(KERN_ERR "XmPciLpEvent.c: Unrecognized PCI event type 0x%x\n",(int)eventParm->xType); + } + else { + printk(KERN_ERR "XmPciLpEvent.c: NULL event received\n"); + } +} + +static void intReceived(struct XmPciLpEvent* eventParm, struct pt_regs* regsParm) { + int irq; + + ++Pci_Interrupt_Count; + //PPCDBG(PPCDBG_BUSWALK,"PCI: XmPciLpEvent.c: intReceived\n"); + + switch (eventParm->hvLpEvent.xSubtype) { + case XmPciLpEvent_SlotInterrupt: + irq = eventParm->hvLpEvent.xCorrelationToken; + /* Dispatch the interrupt handlers for this irq */ + ppc_irq_dispatch_handler(regsParm, irq); + HvCallPci_eoi(eventParm->eventData.slotInterrupt.busNumber, + eventParm->eventData.slotInterrupt.subBusNumber, + eventParm->eventData.slotInterrupt.deviceId); + break; + /* Ignore error recovery events for now */ + case XmPciLpEvent_BusCreated: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d created\n", eventParm->eventData.busCreated.busNumber); + break; + case XmPciLpEvent_BusFailed: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d failed\n", eventParm->eventData.busFailed.busNumber); + break; + case XmPciLpEvent_BusRecovered: + printk(KERN_INFO "XmPciLpEvent.c: system bus %d recovered\n", eventParm->eventData.busRecovered.busNumber); + break; + case XmPciLpEvent_NodeFailed: + printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d failed\n", eventParm->eventData.nodeFailed.busNumber, eventParm->eventData.nodeFailed.subBusNumber, eventParm->eventData.nodeFailed.deviceId); + break; + case XmPciLpEvent_NodeRecovered: + printk(KERN_INFO "XmPciLpEvent.c: multi-adapter bridge %d/%d/%d recovered\n", eventParm->eventData.nodeRecovered.busNumber, eventParm->eventData.nodeRecovered.subBusNumber, eventParm->eventData.nodeRecovered.deviceId); + break; + default: + printk(KERN_ERR "XmPciLpEvent.c: unrecognized event subtype 0x%x\n", + eventParm->hvLpEvent.xSubtype); + break; + }; +} + + +/* This should be called sometime prior to buswalk (init_IRQ would be good) */ +int XmPciLpEvent_init() { + int xRc; + PPCDBG(PPCDBG_BUSWALK,"XmPciLpEvent_init, Register Event type 0x%04X\n",HvLpEvent_Type_PciIo); + + xRc = HvLpEvent_registerHandler(HvLpEvent_Type_PciIo, &XmPciLpEvent_handler); + if (xRc == 0) { + xRc = HvLpEvent_openPath(HvLpEvent_Type_PciIo, 0); + if (xRc != 0) { + printk(KERN_ERR "XmPciLpEvent.c: open event path failed with rc 0x%x\n", xRc); + } + } + else { + printk(KERN_ERR "XmPciLpEvent.c: register handler failed with rc 0x%x\n", xRc); + } + return xRc; +} + diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/align.c linuxppc64_2_4/arch/ppc64/kernel/align.c --- linux-2.4.9-ac10/arch/ppc64/kernel/align.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/align.c Fri Sep 14 07:41:02 2001 @@ -252,11 +252,6 @@ } flags = aligninfo[instr].flags; - - /* For the 4xx-family processors, the 'dar' field of the - * pt_regs structure is overloaded and is really from the DEAR. - */ - addr = (unsigned char *)regs->dar; /* Verify the address of the operand */ diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/binfmt_elf32.c linuxppc64_2_4/arch/ppc64/kernel/binfmt_elf32.c --- linux-2.4.9-ac10/arch/ppc64/kernel/binfmt_elf32.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/binfmt_elf32.c Tue Sep 25 13:52:29 2001 @@ -70,6 +70,7 @@ #define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) #define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) +extern void start_thread32(struct pt_regs *, unsigned long, unsigned long); #undef start_thread #define start_thread start_thread32 diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/chrp_setup.c linuxppc64_2_4/arch/ppc64/kernel/chrp_setup.c --- linux-2.4.9-ac10/arch/ppc64/kernel/chrp_setup.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/chrp_setup.c Sat Sep 29 13:41:46 2001 @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -68,11 +69,6 @@ extern volatile unsigned char *chrp_int_ack_special; extern struct Naca *naca; -unsigned long chrp_get_rtc_time(void); -int chrp_set_rtc_time(unsigned long nowtime); -void chrp_calibrate_decr(void); -long chrp_time_init(void); - void chrp_setup_pci_ptrs(void); void chrp_progress(char *, unsigned short); void chrp_request_regions(void); @@ -83,15 +79,19 @@ char raw_mode); extern char pckbd_unexpected_up(unsigned char keycode); extern void pckbd_leds(unsigned char leds); -extern int pckbd_rate(struct kbd_repeat *rep); extern void pckbd_init_hw(void); extern unsigned char pckbd_sysrq_xlate[128]; extern void openpic_init_IRQ(void); +extern void init_ras_IRQ(void); extern void find_and_init_phbs(void); extern void pSeries_pcibios_fixup(void); extern void iSeries_pcibios_fixup(void); +extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); +extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); +void pSeries_calibrate_decr(void); + kdev_t boot_dev; unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address. @@ -100,8 +100,6 @@ extern int probingmem; extern unsigned long loops_per_jiffy; -extern void *comport1; - #ifdef CONFIG_BLK_DEV_RAM extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ @@ -124,53 +122,6 @@ return len; } -/* - * Fixes for the National Semiconductor PC78308VUL SuperI/O - * - * Some versions of Open Firmware incorrectly initialize the IRQ settings - * for keyboard and mouse - */ -static inline void __init sio_write(u8 val, u8 index) -{ - outb(index, 0x15c); - outb(val, 0x15d); -} - -static inline u8 __init sio_read(u8 index) -{ - outb(index, 0x15c); - return inb(0x15d); -} - -static void __init sio_fixup_irq(const char *name, u8 device, u8 level, - u8 type) -{ - u8 level0, type0, active; - struct device_node *root; - - root = find_path_device("/"); - if (root && - !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13 ) ) - { - /* select logical device */ - sio_write(device, 0x07); - active = sio_read(0x30); - level0 = sio_read(0x70); - type0 = sio_read(0x71); - printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, - !active ? "in" : ""); - if (level0 == level && type0 == type && active) - printk("OK\n"); - else { - printk("remapping to level %d, type %d, active\n", level, type); - sio_write(0x01, 0x30); - sio_write(level, 0x70); - sio_write(type, 0x71); - } - } - -} - void __init chrp_request_regions(void) { request_region(0x20,0x20,"pic1"); request_region(0xa0,0x20,"pic2"); @@ -230,17 +181,6 @@ #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; #endif - - /* Get the event scan rate for the rtas so we know how - * often it expects a heartbeat. -- Cort - */ - if (rtas.event_scan.rate) { - ppc_md.heartbeat = rtas_event_scan; - ppc_md.heartbeat_reset = (HZ/rtas.event_scan.rate*60)-1; - ppc_md.heartbeat_count = 1; - printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", - rtas.event_scan.rate, ppc_md.heartbeat_reset); - } } void __init @@ -254,10 +194,36 @@ ppc_md.progress(UTS_RELEASE, 0x7777); } + +/* Early initialization. Relocation is on but do not reference unbolted pages */ +void __init pSeries_init_early(void) +{ +#ifdef CONFIG_PPC_PSERIES /* This ifdef should go away */ + void *comport; + + htpe_init_pSeries(); + tce_init_pSeries(); + pSeries_pcibios_init_early(); + +#ifdef CONFIG_SMP + smp_init_pSeries(); +#endif + + /* Map the uart for udbg. */ + comport = (void *)ioremap(naca->serialPortAddr, 16); + udbg_init_uart(comport); + + ppc_md.udbg_putc = udbg_putc; + ppc_md.udbg_getc = udbg_getc; + ppc_md.udbg_getc_poll = udbg_getc_poll; +#endif +} + void __init chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { +#if 0 /* PPPBBB remove this later... -Peter */ #ifdef CONFIG_BLK_DEV_INITRD /* take care of initrd if we have one */ if ( r6 ) @@ -266,8 +232,7 @@ initrd_end = __va(r6 + r7); } #endif /* CONFIG_BLK_DEV_INITRD */ - - /* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */ +#endif ppc_md.ppc_machine = _machine; @@ -275,7 +240,7 @@ ppc_md.setup_residual = NULL; ppc_md.get_cpuinfo = chrp_get_cpuinfo; if(naca->interrupt_controller == IC_OPEN_PIC) { - ppc_md.init_IRQ = openpic_init_IRQ; + ppc_md.init_IRQ = openpic_init_IRQ; ppc_md.get_irq = openpic_get_irq; ppc_md.post_irq = NULL; } else { @@ -283,6 +248,7 @@ ppc_md.get_irq = xics_get_irq; ppc_md.post_irq = NULL; } + ppc_md.init_ras_IRQ = init_ras_IRQ; #ifndef CONFIG_PPC_ISERIES ppc_md.pcibios_fixup = pSeries_pcibios_fixup; @@ -298,26 +264,31 @@ ppc_md.power_off = rtas_power_off; ppc_md.halt = rtas_halt; - ppc_md.time_init = chrp_time_init; - ppc_md.set_rtc_time = chrp_set_rtc_time; - ppc_md.get_rtc_time = chrp_get_rtc_time; - ppc_md.calibrate_decr = chrp_calibrate_decr; + ppc_md.time_init = NULL; + ppc_md.get_boot_time = pSeries_get_rtc_time; + ppc_md.get_rtc_time = pSeries_get_rtc_time; + ppc_md.set_rtc_time = pSeries_set_rtc_time; + ppc_md.calibrate_decr = pSeries_calibrate_decr; + + ppc_md.progress = chrp_progress; #ifdef CONFIG_VT - ppc_md.kbd_setkeycode = pckbd_setkeycode; - ppc_md.kbd_getkeycode = pckbd_getkeycode; - ppc_md.kbd_translate = pckbd_translate; - ppc_md.kbd_unexpected_up = pckbd_unexpected_up; - ppc_md.kbd_leds = pckbd_leds; - ppc_md.kbd_init_hw = pckbd_init_hw; - ppc_md.kbd_rate = pckbd_rate; + { + struct device_node *dn = find_type_devices("keyboard"); + if (dn) { + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; #ifdef CONFIG_MAGIC_SYSRQ - ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; - SYSRQ_KEY = 0x63; /* Print Screen */ -#endif /* CONFIG_MAGIC_SYSRQ */ -#endif /* CONFIG_VT */ - - ppc_md.progress = chrp_progress; + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x63; /* Print Screen */ +#endif + } + } +#endif #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) ppc_ide_md.insw = chrp_ide_insw; @@ -332,6 +303,7 @@ ppc_ide_md.io_base = _IO_BASE; #endif + ppc_md.progress("Linux ppc64\n", 0x0); } @@ -343,12 +315,12 @@ char *os; static int max_width; - if ( (_machine != _MACH_chrp) || !rtas.base ) - return; - if (hex) udbg_printf(" %s\n", s); + if (!rtas.base || (_machine != _MACH_pSeries)) + return; + if (max_width == 0) { if ( (root = find_path_device("/rtas")) && (p = (unsigned int *)get_property(root, @@ -386,9 +358,26 @@ call_rtas( "display-character", 1, 1, NULL, ' ' ); } -void chrp_init_map_io_space(void) +void __init pSeries_calibrate_decr(void) { - /* naca->serialPortAddr is initialized earlier in prom.c */ - comport1 = (void *)ioremap(naca->serialPortAddr, PAGE_SIZE); -} + struct device_node *cpu; + int *fp; + unsigned long freq; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", + freq/1000000, freq%1000000 ); + tb_ticks_per_jiffy = freq / HZ; + tb_ticks_per_usec = freq / 1000000; +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/chrp_time.c linuxppc64_2_4/arch/ppc64/kernel/chrp_time.c --- linux-2.4.9-ac10/arch/ppc64/kernel/chrp_time.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/chrp_time.c Wed Dec 31 18:00:00 1969 @@ -1,203 +0,0 @@ -/* - * linux/arch/i386/kernel/time.c - * - * Copyright (C) 1991, 1992, 1995 Linus Torvalds - * - * Adapted for PowerPC (PreP) by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) - * copied and modified from intel version - * Modified for PPC64 by PPC64 Team, Copyright IBM Corp - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -/*#include "time.h" */ - -static int nvram_as1 = NVRAM_AS1; -static int nvram_as0 = NVRAM_AS0; -static int nvram_data = NVRAM_DATA; - -long __init chrp_time_init(void) -{ - struct device_node *rtcs; - int base; - - rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); - if (rtcs == NULL || rtcs->addrs == NULL) { - return 0; - } - base = rtcs->addrs[0].address; - nvram_as1 = 0; - nvram_as0 = base; - nvram_data = base + 1; - - return 0; -} - -int __chrp chrp_cmos_clock_read(int addr) -{ - int retval; - -#if 1 /* DRENG -- see chrp_time_init, chrp_cmos_clock_write - * I think this is broken in general on 64b -- - * nvram_as0 = -1 in 64b format - */ - if (nvram_as1 != 0) { - outb(addr>>8, nvram_as1); - } - outb(addr, nvram_as0); - retval = inb(nvram_data); - return retval; -#else - return(-1); -#endif -} - -void __chrp chrp_cmos_clock_write(unsigned long val, int addr) -{ -#if 1 /* DRENG -- see chrp_cmos_clock_read */ - if (nvram_as1 != 0) - outb(addr>>8, nvram_as1); - outb(addr, nvram_as0); - outb(val, nvram_data); - return; -#else - return; -#endif -} - -/* - * Set the hardware clock. -- Cort - */ -int __chrp chrp_set_rtc_time(unsigned long nowtime) -{ - unsigned char save_control, save_freq_select; - struct rtc_time tm; - - to_tm(nowtime, &tm); - - save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ - - chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); - - save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ - - chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - - tm.tm_year -= 1900; - if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { - BIN_TO_BCD(tm.tm_sec); - BIN_TO_BCD(tm.tm_min); - BIN_TO_BCD(tm.tm_hour); - BIN_TO_BCD(tm.tm_mon); - BIN_TO_BCD(tm.tm_mday); - BIN_TO_BCD(tm.tm_year); - } - chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); - chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); - chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); - chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); - chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); - chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); - - /* The following flags have to be released exactly in this order, - * otherwise the DS12887 (popular MC146818A clone with integrated - * battery and quartz) will not reset the oscillator and will not - * update precisely 500 ms later. You won't find this mentioned in - * the Dallas Semiconductor data sheets, but who believes data - * sheets anyway ... -- Markus Kuhn - */ - chrp_cmos_clock_write(save_control, RTC_CONTROL); - chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); - - if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) - time_state = TIME_OK; - return 0; -} - -unsigned long __chrp chrp_get_rtc_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - int i; - - /* The Linux interpretation of the CMOS clock register contents: - * When the Update-In-Progress (UIP) flag goes from 1 to 0, the - * RTC registers show the second which has precisely just started. - * Let's hope other operating systems interpret the RTC the same way. - */ - /* read RTC exactly on falling edge of update flag */ - for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ - if (chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) - break; - for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ - if (!(chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) - break; - do { /* Isn't this overkill ? UIP above should guarantee consistency */ - sec = chrp_cmos_clock_read(RTC_SECONDS); - min = chrp_cmos_clock_read(RTC_MINUTES); - hour = chrp_cmos_clock_read(RTC_HOURS); - day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); - mon = chrp_cmos_clock_read(RTC_MONTH); - year = chrp_cmos_clock_read(RTC_YEAR); - } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); - if (!(chrp_cmos_clock_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; - return mktime(year, mon, day, hour, min, sec); -} - -extern void setup_default_decr(void); - -void __init chrp_calibrate_decr(void) -{ - struct device_node *cpu; - int *fp; - unsigned long freq; - - /* - * The cpu node should have a timebase-frequency property - * to tell us the rate at which the decrementer counts. - */ - freq = 16666000; /* hardcoded default */ - cpu = find_type_devices("cpu"); - if (cpu != 0) { - fp = (int *) get_property(cpu, "timebase-frequency", NULL); - if (fp != 0) - freq = *fp; - } - printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", - freq/1000000, freq%1000000 ); - - tb_ticks_per_jiffy = freq / HZ; - tb_ticks_per_usec = freq / 1000000; - setup_default_decr(); -} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/entry.S linuxppc64_2_4/arch/ppc64/kernel/entry.S --- linux-2.4.9-ac10/arch/ppc64/kernel/entry.S Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/entry.S Tue Sep 18 14:27:22 2001 @@ -32,6 +32,10 @@ #include #include +#ifdef CONFIG_PPC_ISERIES +#define DO_SOFT_DISABLE +#endif + #undef SHOW_SYSCALLS #undef SHOW_SYSCALLS_TASK @@ -324,19 +328,19 @@ _GLOBAL(ret_from_except) #ifdef CONFIG_PPC_ISERIES - ld r5,_MSR(r1) - andi. r5,r5,MSR_EE + ld r5,SOFTE(r1) + cmpdi 0,r5,0 beq 4f irq_recheck: - mfmsr r5 - andi. r5,r5,MSR_EE - bne 4f /* * Check for pending interrupts (iSeries) */ - CHECKANYINT(r4,r5) + CHECKANYINT(r3,r4) beq+ 4f /* skip do_IRQ if no interrupts */ + mfspr r5,SPRG3 + li r3,0 + stb r3,PACAPROCENABLED(r5) /* ensure we are disabled */ addi r3,r1,STACK_FRAME_OVERHEAD bl .do_IRQ b irq_recheck /* loop back and handle more */ @@ -386,23 +390,31 @@ rldicl r0,r0,16,0 /* clear MSR_EE */ mtmsrd r0 /* Update machine state */ #ifdef CONFIG_PPC_ISERIES - ld r0,_MSR(r1) - andi. r3,r0,MSR_EE + ld r0,SOFTE(r1) + cmpi 0,r0,0 beq+ 1f CHECKANYINT(r4,r3) - bne- irq_recheck + beq+ 1f + mfmsr r0 + ori r0,r0,MSR_EE + mtmsr r0 + b irq_recheck 1: #endif stdcx. r0,0,r1 /* to clear the reservation */ + mfspr r4,SPRG3 /* current task's PACA */ +#ifdef DO_SOFT_DISABLE + ld r0,SOFTE(r1) + stb r0,PACAPROCENABLED(r4) +#endif /* if returning to user mode, save kernel SP */ ld r0,_MSR(r1) andi. r0,r0,MSR_PR beq+ 1f addi r0,r1,INT_FRAME_SIZE /* size of frame */ - mfspr r4,SPRG3 /* current task's PACA */ ld r2,PACACURRENT(r4) /* Get 'current' */ std r0,THREAD+KSP(r2) /* save kernel stack pointer */ std r1,PACAKSAVE(r4) /* save exception stack pointer */ @@ -419,58 +431,8 @@ ld r4,GPR4(r1) ld r1,GPR1(r1) - /* sync required to force memory operations on this processor */ - /* to complete before the current thread gives up control. */ - SYNC rfid -#ifdef CONFIG_PPC_ISERIES -/* - * Fake an interrupt from kernel mode. - * This is used when enable_irq loses an interrupt. - * We only fill in the stack frame minimally. - */ -_GLOBAL(fake_interrupt) - mflr r0 - std r0,16(r1) - stdu r1,-(INT_FRAME_SIZE)(r1) - std r0,_NIP(r1) - std r0,_LINK(r1) - mfmsr r3 - std r3,_MSR(r1) - li r0,0x0fac - std r0,TRAP(r1) - addi r3,r1,STACK_FRAME_OVERHEAD - li r4,1 - bl .do_IRQ - addi r1,r1,INT_FRAME_SIZE - ld r0,16(r1) - mtlr r0 - blr - -/* - * Fake a decrementer from kernel mode. - * This is used when the decrementer pops in - * the hypervisor. We only fill in the stack - * frame minimally - */ -_GLOBAL(fake_decrementer) - mflr r0 - std r0,16(r1) - stdu r1,-(INT_FRAME_SIZE)(r1) - std r0,_NIP(r1) - std r0,_LINK(r1) - mfmsr r3 - std r3,_MSR(r1) - li r0,0x0fac - std r0,TRAP(r1) - addi r3,r1,STACK_FRAME_OVERHEAD - bl .timer_interrupt - addi r1,r1,INT_FRAME_SIZE - ld r0,16(r1) - mtlr r0 - blr -#endif /* * On CHRP, the Run-Time Abstraction Services (RTAS) have to be * called with the MMU off. @@ -633,7 +595,7 @@ rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) andc r11,r11,r12 mtmsrd r11 - SYNC + isync REST_8GPRS(2, r1) // Restore the TOC & incoming param(s) REST_8GPRS(14, r1) // Restore the non-volatiles @@ -643,7 +605,7 @@ mfspr r1,SPRG2 ld r6,_MSR(r1) mtmsrd r6 - SYNC + isync REST_GPR(2, r1) // Restore the TOC REST_8GPRS(14, r1) // Restore the non-volatiles diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/flight_recorder.c linuxppc64_2_4/arch/ppc64/kernel/flight_recorder.c --- linux-2.4.9-ac10/arch/ppc64/kernel/flight_recorder.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/flight_recorder.c Thu Sep 27 11:19:20 2001 @@ -50,6 +50,7 @@ static char LogText[512]; static int LogTextIndex; +static int LogCount = 0; static spinlock_t Fr_Lock; /************************************************************************ @@ -82,7 +83,8 @@ LogTime.tm_hour,LogTime.tm_min, LogTime.tm_sec); } else { - LogTextIndex = 0; + ++LogCount; + LogTextIndex = sprintf(LogText,"%04d. ",LogCount); } } /************************************************************************/ @@ -164,7 +166,7 @@ memset(Fr,SizeOfFr,0); strcpy(Fr->Signature,Signature); Fr->Size = FrSize; - Fr->Flags = 2; + Fr->Flags = 0; Fr->StartPointer = (char*)&Fr->Buffer; Fr->EndPointer = (char*)Fr + Fr->Size; Fr->NextPointer = Fr->StartPointer; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/head.S linuxppc64_2_4/arch/ppc64/kernel/head.S --- linux-2.4.9-ac10/arch/ppc64/kernel/head.S Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/head.S Mon Oct 1 11:21:04 2001 @@ -35,6 +35,10 @@ #include #include +#ifdef CONFIG_PPC_ISERIES +#define DO_SOFT_DISABLE +#endif + /* * We layout physical memory as follows: * 0x0000 - 0x00ff : Secondary processor spin code @@ -47,6 +51,17 @@ */ /* + * SPRG Usage + * + * Register Definition + * + * SPRG0 reserved for hypervisor + * SPRG1 temp - used to save gpr + * SPRG2 temp - used to save gpr + * SPRG3 virt addr of Paca + */ + +/* * Entering into this code we make the following assumptions: * For pSeries: * 1. The MMU is off & open firmware is running in real mode. @@ -75,6 +90,15 @@ .llong msChunks-KERNELBASE .llong pidhash-KERNELBASE + /* Offset 0x38 - Pointer to start of embedded System.map */ + .globl embedded_sysmap_start +embedded_sysmap_start: + .llong 0 + /* Offset 0x40 - Pointer to end of embedded System.map */ + .globl embedded_sysmap_end +embedded_sysmap_end: + .llong 0 + /* Secondary processors spin on this value until it goes to 1. */ .globl __secondary_hold_spinloop __secondary_hold_spinloop: @@ -135,72 +159,114 @@ * This is the start of the interrupt handlers for Pseries * This code runs with relocation off. */ +#define EXCEPTION_PROLOG_PSERIES(label) \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */ \ + mtspr SPRG1,r21; /* save r21 */ \ + mfspr r20,SPRG3; /* get Paca virt addr */ \ + clrldi r20,r20,12; /* convert virt to real addr */ \ + ld r21,PACAEXCSP(r20); /* get exception stack ptr */ \ + addi r21,r21,-EXC_FRAME_SIZE; /* make exception frame */ \ + std r21,PACAEXCSP(r20); /* update exception stack ptr */ \ + clrldi r20,r21,12; /* convert virt to real addr */ \ + std r22,32(r20); /* Save r22 in exc. frame */ \ + std r23,40(r20); /* Save r23 in exc. frame */ \ + mfspr r22,SRR0; /* EA of interrupted instr */ \ + std r22,0(r20); /* Save SRR0 in exc. frame */ \ + mfspr r23,SRR1; /* machine state at interrupt */ \ + std r23,8(r20); /* Save SRR1 in exc. frame */ \ + mfspr r22,SPRG2; \ + std r22,16(r20); /* Save r20 in exc. frame */ \ + mfspr r23,SPRG1; \ + std r23,24(r20); /* Save r21 in exc. frame */ \ + mfspr r20,SPRG3; /* get Paca virt addr again */ \ + SET_REG_TO_LABEL(r22,label); /* Get addr to branch to */ \ + mfmsr r23; \ + rotldi r23,r23,4; \ + ori r23,r23,0x32B; /* Set IR, DR, RI, SF, ISF, HV*/ \ + rotldi r23,r23,60; /* for generic handlers */ \ + mtspr SRR0,r22; \ + mtspr SRR1,r23; \ + mfcr r23; /* save CR in r23 */ \ + rfid -#define EXCEPTION_PROLOG_PSERIES(label) \ - mtspr SPRG2,r20; /* use SPRG2 as scratch reg */ \ - mfspr r20,SPRG3; /* get Paca virt addr */ \ - clrldi r20,r20,12; /* convert virt to real addr */ \ - std r21,PACAR21(r20); /* Save GPR21 in Paca */ \ - mfspr r21,SRR0; /* EA of interrupted instr */ \ - std r21,LPPACA+LPPACASRR0(r20); /* Set SRR0 in ItLpPaca */ \ - mfspr r21,SRR1; /* machine state at interrupt */ \ - std r21,LPPACA+LPPACASRR1(r20); /* Set SRR1 in ItLpPaca */ \ - SET_REG_TO_LABEL(r21,label); /* Get addr to branch to */ \ - mtspr SRR0,r21; \ - li r20,0x5; /* Turn on 64b, 64bints */ \ - sldi r20,r20,61; \ - ori r20,r20,0x30; /* for generic handlers */ \ - mfmsr r21; /* Turn on IR & DR */ \ - or r21,r21,r20; /* for generic handlers */ \ - mtspr SRR1,r21; \ - rfid /* Jump to generic handlers */ /* * This is the start of the interrupt handlers for i_series * This code runs with relocation on. */ - #define EXCEPTION_PROLOG_ISERIES \ - mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\ - mfspr r20,SPRG3; /* get Paca */\ - std r21,PACAR21(r20) /* Save GPR21 in Paca */ - -#define EXCEPTION_PROLOG_COMMON \ - mfspr r20,SPRG3; /* get Paca virt addr */ \ - std r22,PACAR22(r20); /* Save GPR22 in Paca */\ - mfcr r22; \ - ld r21,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */ \ - andi. r21,r21,MSR_PR; /* Set CR for later branch */ \ - beq+ 1f; \ - ld r21,PACAKSAVE(r20); /* exception stack to use */\ - b 2f; \ -1: \ - subi r21,r1,INT_FRAME_SIZE;/* alloc exc. frame */ \ -2: \ - std r22,_CCR(r21); /* save CR in stackframe */\ - ld r22,PACAR21(r20); /* Get GPR21 from Paca */\ - std r22,GPR21(r21); /* Save GPR21 in stackframe */\ - ld r22,PACAR22(r20); /* Get GPR22 from Paca */\ - std r22,GPR22(r21); /* Save GPR22 in stackframe */\ - std r23,GPR23(r21); /* Save GPR23 in stackframe */\ - mfspr r22,SPRG2; /* Get GPR20 from SPRG2 */\ - std r22,GPR20(r21); /* Save GPR20 in stackframe */\ - mflr r22; \ - std r22,_LINK(r21); \ - mfctr r22; \ - std r22,_CTR(r21); \ - mfspr r22,XER; \ - std r22,_XER(r21); \ - ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\ - ld r23,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */\ - SAVE_8GPRS(0, r21); \ - SAVE_4GPRS(8, r21); \ - SAVE_2GPRS(12, r21); \ - ld r2,PACATOC(r20); \ - std r1,0(r21); \ - mr r1,r21 + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\ + mtspr SPRG1,r21; /* save r21 */\ + mfspr r20,SPRG3; /* get Paca */\ + ld r21,PACAEXCSP(r20); /* get exception stack ptr */\ + addi r21,r21,-EXC_FRAME_SIZE; /* make exception frame */\ + std r21,PACAEXCSP(r20); /* update exception stack ptr */\ + std r22,32(r21); /* save r22 on exception frame */\ + std r23,40(r21); /* Save r23 in exc. frame */\ + ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\ + std r22,0(r21); /* save SRR0 in exc. frame */\ + ld r23,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */\ + std r23,8(r21); /* save SRR1 in exc. frame */\ + mfspr r22,SPRG2; \ + std r22,16(r21); /* Save r20 in exc. frame */\ + mfspr r23,SPRG1; \ + std r23,24(r21); /* Save r21 in exc. frame */\ + mfcr r23; /* save CR in r23 */ + +/* + * The common exception prolog is used for all except a few exceptions + * such as a segment miss on a kernel address. We have to be prepared + * to take another exception from the point where we first touch the + * kernel stack onwards. + * + * On entry r20 points to the paca and r21 points to the exception + * frame on entry, r23 contains the saved CR, and relocation is on. + */ +#define EXCEPTION_PROLOG_COMMON \ + mfspr r22,DAR; /* Save DAR in exc. frame */ \ + std r22,48(r21); \ + mfspr r22,DSISR; /* Save DSISR in exc. frame */ \ + std r22,56(r21); \ + ld r22,8(r21); /* Get SRR1 from exc. frame */ \ + andi. r22,r22,MSR_PR; /* Set CR for later branch */ \ + mr r22,r1; /* Save r1 */ \ + subi r1,r1,INT_FRAME_SIZE; /* alloc frame on kernel stack */ \ + beq- 1f; \ + ld r1,PACAKSAVE(r20); /* kernel stack to use */ \ +1: std r22,GPR1(r1); /* save r1 in stackframe */ \ + std r22,0(r1); /* make stack chain pointer */ \ + std r23,_CCR(r1); /* save CR in stackframe */ \ + ld r22,16(r21); /* move r20 to stackframe */ \ + std r22,GPR20(r1); \ + ld r23,24(r21); /* move r21 to stackframe */ \ + std r23,GPR21(r1); \ + ld r22,32(r21); /* move r22 to stackframe */ \ + std r22,GPR22(r1); \ + ld r23,40(r21); /* move r23 to stackframe */ \ + std r23,GPR23(r1); \ + mflr r22; /* save LR in stackframe */ \ + std r22,_LINK(r1); \ + mfctr r23; /* save CTR in stackframe */ \ + std r23,_CTR(r1); \ + mfspr r22,XER; /* save XER in stackframe */ \ + std r22,_XER(r1); \ + ld r23,48(r21); /* move DAR to stackframe */ \ + std r23,_DAR(r1); \ + ld r22,56(r21); /* move DSISR to stackframe */ \ + std r22,_DSISR(r1); \ + lbz r22,PACAPROCENABLED(r20); \ + std r22,SOFTE(r1); \ + ld r22,0(r21); /* get SRR0 from exc. frame */ \ + ld r23,8(r21); /* get SRR1 from exc. frame */ \ + addi r21,r21,EXC_FRAME_SIZE;/* pop off exception frame */ \ + std r21,PACAEXCSP(r20); \ + SAVE_GPR(0, r1); /* save r0 in stackframe */ \ + SAVE_8GPRS(2, r1); /* save r2 - r13 in stackframe */ \ + SAVE_4GPRS(10, r1); \ + ld r2,PACATOC(r20) + /* * Note: code which follows this uses cr0.eq (set if from kernel), - * r21, r22 (SRR0), and r23 (SRR1). + * r1, r22 (SRR0), and r23 (SRR1). */ /* @@ -218,16 +284,25 @@ EXCEPTION_PROLOG_ISERIES; \ b label##_common +#define MASKABLE_EXCEPTION_ISERIES( label ) \ + .globl label##_Iseries; \ +label##_Iseries: \ + EXCEPTION_PROLOG_ISERIES; \ + lbz r22,PACAPROCENABLED(r20); \ + cmpi 0,r22,0; \ + beq- label##_Iseries_masked; \ + b label##_common + #define STD_EXCEPTION_COMMON( trap, label, hdlr ) \ .globl label##_common; \ label##_common: \ EXCEPTION_PROLOG_COMMON; \ addi r3,r1,STACK_FRAME_OVERHEAD; \ - SET_REG_TO_CONST(r20, MSR_KERNEL); \ + li r20,0; \ li r6,trap; \ - bl .transfer_to_handler; \ - .llong hdlr; \ - .llong .ret_from_except + bl .save_remaining_regs; \ + bl hdlr; \ + b .ret_from_except /* * Start of pSeries system interrupt routines @@ -253,6 +328,7 @@ STD_EXCEPTION_PSERIES( 0xd00, SingleStep ) STD_EXCEPTION_PSERIES( 0xe00, Trap_0e ) STD_EXCEPTION_PSERIES( 0xf00, PerformanceMonitor ) + STD_EXCEPTION_PSERIES( 0x1300, InstructionBreakpoint ) . = 0x4000 .globl __end_interupts @@ -318,11 +394,11 @@ STD_EXCEPTION_ISERIES( DataAccessSLB ) STD_EXCEPTION_ISERIES( InstructionAccess ) STD_EXCEPTION_ISERIES( InstructionAccessSLB ) - STD_EXCEPTION_ISERIES( HardwareInterrupt ) + MASKABLE_EXCEPTION_ISERIES( HardwareInterrupt ) STD_EXCEPTION_ISERIES( Alignment ) STD_EXCEPTION_ISERIES( ProgramCheck ) STD_EXCEPTION_ISERIES( FPUnavailable ) - STD_EXCEPTION_ISERIES( Decrementer ) + MASKABLE_EXCEPTION_ISERIES( Decrementer ) STD_EXCEPTION_ISERIES( Trap_0a ) STD_EXCEPTION_ISERIES( Trap_0b ) STD_EXCEPTION_ISERIES( SystemCall ) @@ -362,9 +438,7 @@ /* Let the Hypervisor know we are alive */ /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ lis r3,0x8002 - rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ - rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ - or r3,r3,r0 /* r3 = r3 | r0 */ + rldicr r3,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ #else /* CONFIG_SMP */ /* Yield the processor. This is required for non-SMP kernels which are running on multi-threaded machines. */ @@ -380,6 +454,30 @@ b 1b /* If SMP not configured, secondaries * loop forever */ + .globl HardwareInterrupt_Iseries_masked +HardwareInterrupt_Iseries_masked: + b maskable_exception_exit + + .globl Decrementer_Iseries_masked +Decrementer_Iseries_masked: + li r22,1 + stb r22,PACALPPACA+LPPACADECRINT(r20) + lwz r22,PACADEFAULTDECR(r20) + mtspr DEC,r22 +maskable_exception_exit: + mtcrf 0xff,r23 /* Restore regs and free exception frame */ + ld r22,0(r21) + ld r23,8(r21) + mtspr SRR0,r22 + mtspr SRR1,r23 + ld r22,32(r21) + ld r23,40(r21) + addi r21,r21,EXC_FRAME_SIZE + std r21,PACAEXCSP(r20) + mfspr r21,SPRG1 + mfspr r20,SPRG2 + rfid + /*** Common interrupt handlers ***/ STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException ) @@ -390,32 +488,54 @@ STD_EXCEPTION_COMMON( 0xd00, SingleStep, .SingleStepException ) STD_EXCEPTION_COMMON( 0xe00, Trap_0e, .UnknownException ) STD_EXCEPTION_COMMON( 0xf00, PerformanceMonitor, .PerformanceMonitorException ) + STD_EXCEPTION_COMMON(0x1300, InstructionBreakpoint, .InstructionBreakpointException ) -/* r20 is in SPRG2, - r21 is in the PACA -*/ +/* + * Return from an exception which is handled without calling + * save_remaining_regs. The caller is assumed to have done + * EXCEPTION_PROLOG_COMMON. + */ +fast_exception_return: + ld r3,_CCR(r1) + ld r4,_LINK(r1) + ld r5,_CTR(r1) + ld r6,_XER(r1) + mtcr r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_GPR(0, r1) + REST_8GPRS(2, r1) + REST_4GPRS(10, r1) + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_4GPRS(20, r1) + ld r1,GPR1(r1) + rfid + + +/* + * Here r20 points to the PACA, r21 to the exception frame, + * r23 contains the saved CR. + * r20 - r23, SRR0 and SRR1 are saved in the exception frame. + */ .globl DataAccess_common DataAccess_common: - mfcr r20 - mfspr r21,DAR - srdi r21,r21,60 - cmpi 0,r21,0xc - bne 3f + mfspr r22,DAR + srdi r22,r22,60 + cmpi 0,r22,0xc - /* Segment faulted on a bolted segment. Go off and map that segment. */ - b .do_stab_bolted + /* Segment fault on a bolted segment. Go off and map that segment. */ + beq .do_stab_bolted -3: mtcr r20 EXCEPTION_PROLOG_COMMON - mfspr r20,DSISR - std r20,_DSISR(r21) - andis. r0,r20,0xa450 /* weird error? */ + ld r3,_DSISR(r1) + andis. r0,r3,0xa450 /* weird error? */ bne 1f /* if not, try to put a PTE */ - mfspr r3,DAR /* into the hash table */ - std r3,_DAR(r21) - rlwinm r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + andis. r0,r3,0x0020 /* Is it a page table fault? */ + rlwinm r4,r3,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + ld r3,_DAR(r1) /* into the hash table */ - andis. r0,r20,0x0020 /* Is it a page table fault? */ beq 2f /* If so handle it */ li r4,0x300 /* Trap number */ bl .do_stab_SI @@ -423,60 +543,44 @@ 2: bl .do_hash_page_DSI /* Try to handle as hpte fault */ 1: - ld r4,_DAR(r21) - ld r5,_DSISR(r21) + ld r4,_DAR(r1) + ld r5,_DSISR(r1) addi r3,r1,STACK_FRAME_OVERHEAD - - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) /* Copy saved SOFTE bit */ +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0x300 - bl .transfer_to_handler - .llong .do_page_fault - .llong .ret_from_except + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except .globl DataAccessSLB_common DataAccessSLB_common: + mfspr r22,DAR + srdi r22,r22,60 + cmpi 0,r22,0xc + + /* Segment fault on a bolted segment. Go off and map that segment. */ + beq .do_slb_bolted + EXCEPTION_PROLOG_COMMON - mfspr r3,DAR + ld r3,_DAR(r1) li r4,0x380 /* Exception vector */ bl .ste_allocate or. r3,r3,r3 /* Check return code */ - bne 1f /* Branch on failure */ - - /* SLB Allocation success - Return. */ - ld r3,_CCR(r21) - ld r4,_LINK(r21) - ld r5,_CTR(r21) - ld r6,_XER(r21) - - mtcrf 0xff,r3 - mtlr r4 - mtctr r5 - mtspr XER,r6 - REST_8GPRS(0,r21) - REST_4GPRS(8,r21) - REST_2GPRS(12,r21) - mtspr SRR1,r23 - mtspr SRR0,r22 - ld r20,GPR20(r21) - ld r22,GPR22(r21) - ld r23,GPR23(r21) - ld r21,GPR21(r21) - RFID - -1: - mfspr r4,DAR - std r4,_DAR(r21) - mfspr r5,DSISR - std r5,_DSISR(r21) + beq fast_exception_return /* Return if we succeeded */ addi r3,r1,STACK_FRAME_OVERHEAD - - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0x380 - bl .transfer_to_handler - .llong .do_page_fault - .llong .ret_from_except + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except .globl InstructionAccess_common InstructionAccess_common: @@ -497,12 +601,15 @@ mr r4,r22 mr r5,r23 addi r3,r1,STACK_FRAME_OVERHEAD - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0x400 - bl .transfer_to_handler - .llong .do_page_fault - .llong .ret_from_except + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except .globl InstructionAccessSLB_common InstructionAccessSLB_common: @@ -511,89 +618,111 @@ li r4,0x480 /* Exception vector */ bl .ste_allocate or. r3,r3,r3 /* Check return code */ - bne 1f /* Branch on failure */ + beq fast_exception_return /* Return if we succeeded */ - /* SLB Allocation success - Return. */ - ld r3,_CCR(r21) - ld r4,_LINK(r21) - ld r5,_CTR(r21) - ld r6,_XER(r21) - - mtcrf 0xff,r3 - mtlr r4 - mtctr r5 - mtspr XER,r6 - REST_8GPRS(0,r21) - REST_4GPRS(8,r21) - REST_2GPRS(12,r21) - mtspr SRR1,r23 - mtspr SRR0,r22 - ld r20,GPR20(r21) - ld r22,GPR22(r21) - ld r23,GPR23(r21) - ld r21,GPR21(r21) - RFID - -1: - mfspr r4,DAR - std r4,_DAR(r21) - mfspr r5,DSISR - std r5,_DSISR(r21) addi r3,r1,STACK_FRAME_OVERHEAD - - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0x380 - bl .transfer_to_handler - .llong .do_page_fault - .llong .ret_from_except + bl .save_remaining_regs + bl .do_page_fault + b .ret_from_except .globl HardwareInterrupt_common HardwareInterrupt_common: EXCEPTION_PROLOG_COMMON HardwareInterrupt_entry: addi r3,r1,STACK_FRAME_OVERHEAD - SET_REG_TO_CONST(r20, MSR_KERNEL) + li r20,0 li r6,0x500 - bl .transfer_to_handler - .llong .do_IRQ - .llong .ret_from_except + bl .save_remaining_regs + /* Determine if need to run do_irq on a hardware interrupt stack */ + /* The first invocation of do_irq will occur on the kernel */ + /* stack in the current stack */ + /* All other invocations of do_irq will run on the hardware */ + /* interrupt stack associated with the PACA of the current */ + /* processor. */ + /* */ + /* The call to do_irq will preserve the value of r14 - r31 */ + /* */ + mfspr r20,SPRG3 /* get Paca */ + lbz r21,PACAHRDWINTCOUNT(r20) /* get hardware interrupt cnt */ + cmpi 0,r21,1 /* */ + addi r21,r21,1 /* incr hardware interrupt cnt*/ + stb r21,PACAHRDWINTCOUNT(r20) /* */ + bne 2f /* */ + + mr r14,r1 /* preserve current r1 */ + ld r1,PACAHRDWINTSTACK(r20) /* */ + std r14,0(r1) /* set the back chain */ + bl .do_IRQ + lbz r22,PACAHRDWINTCOUNT(r20) /* get hardware interrupt cnt */ + cmp 0,r22,r21 /* debug test */ + bne 3f + subi r21,r21,1 + stb r21,PACAHRDWINTCOUNT(r20) /* */ + mr r1,r14 /* */ + b .ret_from_except + +2: + bl .do_IRQ + + lbz r22,PACAHRDWINTCOUNT(r20) /* get hardware interrupt cnt */ + cmp 0,r22,r21 /* debug test */ + bne 3f /* */ + subi r21,r21,1 /* decr hardware interrupt cnt*/ + stb r21,PACAHRDWINTCOUNT(r20) /* */ + + b .ret_from_except + +3: + /* error - counts out of sync */ +#ifdef CONFIG_XMON + bl .xmon +#endif +4: b 4b + .globl Alignment_common Alignment_common: EXCEPTION_PROLOG_COMMON - mfspr r4,DAR - std r4,_DAR(r21) - mfspr r5,DSISR - std r5,_DSISR(r21) addi r3,r1,STACK_FRAME_OVERHEAD - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0x600 - bl .transfer_to_handler - .llong .AlignmentException - .llong .ret_from_except + bl .save_remaining_regs + bl .AlignmentException + b .ret_from_except .globl ProgramCheck_common ProgramCheck_common: EXCEPTION_PROLOG_COMMON addi r3,r1,STACK_FRAME_OVERHEAD - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0x700 - bl .transfer_to_handler - .llong .ProgramCheckException - .llong .ret_from_except + bl .save_remaining_regs + bl .ProgramCheckException + b .ret_from_except .globl FPUnavailable_common FPUnavailable_common: EXCEPTION_PROLOG_COMMON bne .load_up_fpu /* if from user, just load it up */ - SET_REG_TO_CONST(r20, MSR_KERNEL) + li r20,0 li r6,0x800 - bl .transfer_to_handler /* if from kernel, take a trap */ - .llong .KernelFP - .llong .ret_from_except + bl .save_remaining_regs /* if from kernel, take a trap */ + bl .KernelFP + b .ret_from_except .globl SystemCall_common SystemCall_common: @@ -605,13 +734,16 @@ beq+ HardwareInterrupt_entry 1: #endif - std r3,ORIG_GPR3(r21) - SET_REG_TO_CONST(r20, MSR_KERNEL) - rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + std r3,ORIG_GPR3(r1) +#ifdef DO_SOFT_DISABLE + ld r20,SOFTE(r1) +#else + rldicl r20,r23,49,63 /* copy EE bit from saved MSR */ +#endif li r6,0xC00 - bl .transfer_to_handler - .llong .DoSyscall - .llong .ret_from_except + bl .save_remaining_regs + bl .DoSyscall + b .ret_from_except _GLOBAL(do_hash_page_ISI) li r4,0 @@ -620,7 +752,21 @@ ori r4,r4,1 /* add _PAGE_PRESENT */ mflr r21 /* Save LR in r21 */ - /* r21 restored later from r1 */ + +#ifdef DO_SOFT_DISABLE + /* + * We hard enable here (but first soft disable) so that the hash_page + * code can spin on the hash_table_lock with problem on a shared + * processor. + */ + li r0,0 + stb r0,PACAPROCENABLED(r20) /* Soft Disabled */ + + mfmsr r0 + ori r0,r0,MSR_EE + mtmsrd r0 /* Hard Enable */ +#endif + /* * r3 contains the faulting address * r4 contains the required access permissions @@ -629,58 +775,61 @@ */ bl .hash_page /* build HPTE if possible */ - mtlr r21 /* restore LR */ - mr r21,r1 /* restore r21 */ - - or. r3,r3,r3 /* Check return code */ - bnelr /* Return to DSI or ISI on failure */ +#ifdef DO_SOFT_DISABLE /* - * The HPTE has been created/updated. Restore and retry the faulting inst + * Now go back to hard disabled. */ + mfmsr r0 + li r4,0 + ori r4,r4,MSR_EE + andc r0,r0,r4 + mtmsrd r0 /* Hard Disable */ + + ld r0,SOFTE(r1) + cmpdi 0,r0,0 /* See if we will soft enable in */ + /* save_remaining_regs */ + beq 5f + CHECKANYINT(r4,r5) + bne- HardwareInterrupt_entry /* Convert this DSI into an External */ + /* to process interrupts which occurred */ + /* during hash_page */ +5: + stb r0,PACAPROCENABLED(r20) /* Restore soft enable/disable status */ +#endif + or. r3,r3,r3 /* Check return code */ + beq fast_exception_return /* Return from exception on success */ - ld r3,_CCR(r21) - ld r4,_LINK(r21) - ld r5,_CTR(r21) - ld r6,_XER(r21) - mtcrf 0xff,r3 - mtlr r4 - mtctr r5 - mtspr XER,r6 - REST_8GPRS(0,r21) - REST_4GPRS(8,r21) - REST_2GPRS(12,r21) - mtspr SRR1,r23 - mtspr SRR0,r22 - ld r20,GPR20(r21) - ld r22,GPR22(r21) - ld r23,GPR23(r21) - ld r21,GPR21(r21) - rfid + mtlr r21 /* restore LR */ + blr /* Return to DSI or ISI on failure */ -/* orig r20 is in SPRG2, - orig r21 is in the PACA - r20 contains CCR - - r22 needs to be saved - r1 needs to be saved - CCR needs to be saved -*/ +/* + * r20 points to the PACA, r21 to the exception frame, + * r23 contains the saved CR. + * r20 - r23, SRR0 and SRR1 are saved in the exception frame. + * We assume we aren't going to take any exceptions during this procedure. + */ _GLOBAL(do_stab_bolted) - mfsprg r21,3 - std r22,PACAR22(r21) - std r1,PACAR1(r21) - stw r20,PACACCR(r21) - mfspr r21,DAR + std r23,48(r21) /* save CR in exc. frame */ + mfspr r21,DSISR + andis. r21,r21,0x0020 + bne+ 2f + li r3,0 +#ifdef CONFIG_XMON + bl .xmon +#endif +1: b 1b +2: /* (((ea >> 28) & 0x1fff) << 15) | (ea >> 60) */ + mfspr r21,DAR rldicl r20,r21,36,32 /* Permits a full 32b of ESID */ rldicr r20,r20,15,48 rldicl r21,r21,4,60 or r20,r20,r21 li r21,9 /* VSID_RANDOMIZER */ - rldicr r21,r21,32,31 + sldi r21,r21,32 oris r21,r21,58231 ori r21,r21,39831 @@ -699,20 +848,20 @@ /* Search the primary group for a free entry */ li r22,0 1: - ld r1,0(r21) /* Test valid bit of the current ste */ - rldicl r1,r1,57,63 - cmpwi r1,0 + ld r23,0(r21) /* Test valid bit of the current ste */ + rldicl r23,r23,57,63 + cmpwi r23,0 bne 2f - ld r1,8(r21) /* Get the current vsid part of the ste */ - rldimi r1,r20,12,0 /* Insert the new vsid value */ - std r1,8(r21) /* Put new entry back into the stab */ + ld r23,8(r21) /* Get the current vsid part of the ste */ + rldimi r23,r20,12,0 /* Insert the new vsid value */ + std r23,8(r21) /* Put new entry back into the stab */ eieio /* Order vsid update */ - ld r1,0(r21) /* Get the esid part of the ste */ + ld r23,0(r21) /* Get the esid part of the ste */ mfspr r20,DAR /* Get the new esid */ rldicl r20,r20,36,28 /* Permits a full 36b of ESID */ - rldimi r1,r20,28,0 /* Insert the new esid value */ - ori r1,r1,144 /* Turn on valid and kp */ - std r1,0(r21) /* Put new entry back into the stab */ + rldimi r23,r20,28,0 /* Insert the new esid value */ + ori r23,r23,144 /* Turn on valid and kp */ + std r23,0(r21) /* Put new entry back into the stab */ sync /* Order the update */ b 3f 2: @@ -732,27 +881,27 @@ /* r21 currently points to and ste one past the group of interest */ /* make it point to the randomly selected entry */ subi r21,r21,128 - ori r21,r21,r22 /* r21 is the entry to invalidate */ + or r21,r21,r22 /* r21 is the entry to invalidate */ isync /* mark the entry invalid */ - ld r1,0(r21) + ld r23,0(r21) li r22,-129 - and r1,r1,r22 - std r1,0(r21) + and r23,r23,r22 + std r23,0(r21) sync - ld r1,8(r21) - rldimi r1,r20,12,0 - std r1,8(r21) + ld r23,8(r21) + rldimi r23,r20,12,0 + std r23,8(r21) eieio - ld r1,0(r21) /* Get the esid part of the ste */ - mr r22,r1 - mfspr r20,DAR /* Get the new esid */ - rldicl r20,r20,36,32 /* Permits a full 32b of ESID */ - rldimi r1,r20,28,0 /* Insert the new esid value */ - ori r1,r1,144 /* Turn on valid and kp */ - std r1,0(r21) /* Put new entry back into the stab */ + ld r23,0(r21) /* Get the esid part of the ste */ + mr r22,r23 + mfspr r20,DAR /* Get the new esid */ + rldicl r20,r20,36,28 /* Permits a full 32b of ESID */ + rldimi r23,r20,28,0 /* Insert the new esid value */ + ori r23,r23,144 /* Turn on valid and kp */ + std r23,0(r21) /* Put new entry back into the stab */ rldicl r22,r22,36,28 rldicr r22,r22,28,35 @@ -761,24 +910,108 @@ 3: /* All done -- return from exception. */ - mfsprg r20,3 /* Load the PACA pointer */ + mfsprg r20,3 /* Load the PACA pointer */ + ld r21,PACAEXCSP(r20) /* Get the exception frame pointer */ + addi r22,r21,EXC_FRAME_SIZE + ld r23,48(r21) /* get saved CR */ + std r22,PACAEXCSP(r20) /* pop off the exception frame */ + mtcr r23 /* restore CR */ + ld r22,0(r21) /* Get SRR0 from exc. frame */ + ld r23,8(r21) /* Get SRR1 from exc. frame */ + mtspr SRR0,r22 + + mtspr SRR1,r23 + ld r20,16(r21) /* restore r20 from exc. frame */ + ld r22,32(r21) /* restore r22 and r23 */ + ld r23,40(r21) + ld r21,24(r21) /* restore r21 last */ + rfid +_TRACEBACK(do_stab_bolted) + +/* + * r20 points to the PACA, r21 to the exception frame, + * r23 contains the saved CR. + * r20 - r23, SRR0 and SRR1 are saved in the exception frame. + * We assume we aren't going to take any exceptions during this procedure. + */ +_GLOBAL(do_slb_bolted) + std r23,48(r21) /* save CR in exc. frame */ + + /* (((ea >> 28) & 0x1fff) << 15) | (ea >> 60) */ + mfspr r21,DAR + rldicl r20,r21,36,32 /* Permits a full 32b of ESID */ + rldicr r20,r20,15,48 + rldicl r21,r21,4,60 + or r20,r20,r21 + + li r21,9 /* VSID_RANDOMIZER */ + sldi r21,r21,32 + oris r21,r21,58231 + ori r21,r21,39831 + + mulld r20,r20,r21 + clrldi r20,r20,28 /* r20 = vsid */ + + /* Search the SLB for a free entry */ + li r22,1 +1: + slbmfee r23,r22 + rldicl r23,r23,37,63 + cmpwi r23,0 + beq 3f /* Found an invalid entry */ + + addi r22,r22,1 + cmpldi r22,64 + blt 1b + + /* Didn't find a free entry -- for now trap out. Do castouts later DRENG */ + li r3,0 +#ifdef CONFIG_XMON + bl .xmon +#endif +2: b 2b + + /* r20 = vsid, r22 = entry */ +3: + /* Put together the vsid portion of the entry. */ + li r21,0 + rldimi r21,r20,12,0 + ori r20,r21,1024 + + /* Put together the esid portion of the entry. */ + mfspr r21,DAR /* Get the new esid */ + rldicl r21,r21,36,28 /* Permits a full 36b of ESID */ + li r23,0 + rldimi r23,r21,28,0 /* Insert esid */ + oris r21,r23,2048 /* valid bit */ + rldimi r21,r22,0,52 /* Insert entry */ - ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */ - ld r21,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */ - mtspr SRR1,r21 + isync + slbmte r20,r21 + isync + + /* All done -- return from exception. */ + mfsprg r20,3 /* Load the PACA pointer */ + ld r21,PACAEXCSP(r20) /* Get the exception frame pointer */ + addi r22,r21,EXC_FRAME_SIZE + ld r23,48(r21) /* get saved CR */ + std r22,PACAEXCSP(r20) /* pop off the exception frame */ + mtcr r23 /* restore CR */ + ld r22,0(r21) /* Get SRR0 from exc. frame */ + ld r23,8(r21) /* Get SRR1 from exc. frame */ mtspr SRR0,r22 - lwz r21,PACACCR(r20) /* Restore the clobbered regs */ - mtcr r21 - ld r1, PACAR1(r20) - ld r21,PACAR21(r20) - ld r22,PACAR22(r20) - mfspr r20,SPRG2 + mtspr SRR1,r23 + ld r20,16(r21) /* restore r20 from exc. frame */ + ld r22,32(r21) /* restore r22 and r23 */ + ld r23,40(r21) + ld r21,24(r21) /* restore r21 last */ rfid +_TRACEBACK(do_slb_bolted) _GLOBAL(do_stab_SI) mflr r21 /* Save LR in r21 */ - /* r21 restored later from r1 */ + /* * r3 contains the faulting address * r4 contains the required access permissions @@ -787,108 +1020,72 @@ */ bl .ste_allocate /* build STE if possible */ - mtlr r21 /* restore LR */ - mr r21,r1 /* restore r21 */ - or. r3,r3,r3 /* Check return code */ - bnelr /* Return to DSI or ISI on failure */ - - /* - * The STE has been created/updated. Restore and retry the faulting inst - */ - - ld r3,_CCR(r21) - ld r4,_LINK(r21) - ld r5,_CTR(r21) - ld r6,_XER(r21) - - mtcrf 0xff,r3 - mtlr r4 - mtctr r5 - mtspr XER,r6 - REST_8GPRS(0,r21) - REST_4GPRS(8,r21) - REST_2GPRS(12,r21) - mtspr SRR1,r23 - mtspr SRR0,r22 - ld r20,GPR20(r21) - ld r22,GPR22(r21) - ld r23,GPR23(r21) - ld r21,GPR21(r21) - rfid + beq fast_exception_return /* Return from exception on success */ + mtlr r21 /* restore LR */ + blr /* Return to DSI or ISI on failure */ /* - * This code finishes saving the registers to the exception frame - * and jumps to the appropriate handler for the exception. Address - * translation is already on. - */ -_GLOBAL(transfer_to_handler) -/* - * Save the rest of the registers into the pt_regs structure - */ - std r22,_NIP(r21) - std r23,_MSR(r21) - std r6,TRAP(r21) - ld r6,GPR6(r21) - SAVE_2GPRS(14, r21) - SAVE_4GPRS(16, r21) - SAVE_8GPRS(24, r21) -/* - * Clear the RESULT field + * This code finishes saving the registers to the exception frame. + * Address translation is already on. */ +_GLOBAL(save_remaining_regs) + /* + * Save the rest of the registers into the pt_regs structure + */ + std r22,_NIP(r1) + std r23,_MSR(r1) + std r6,TRAP(r1) + ld r6,GPR6(r1) + SAVE_2GPRS(14, r1) + SAVE_4GPRS(16, r1) + SAVE_8GPRS(24, r1) + + /* + * Clear the RESULT field + */ li r22,0 - std r22,RESULT(r21) -/* - * Test if from user state; result will be tested later - */ + std r22,RESULT(r1) + + /* + * Test if from user state; result will be tested later + */ andi. r23,r23,MSR_PR /* Set CR for later branch */ -/* - * Indicate that r1 contains the kernel stack and - * get the Kernel TOC and CURRENT pointers from the Paca - */ + + /* + * Indicate that r1 contains the kernel stack and + * get the Kernel TOC and CURRENT pointers from the Paca + */ mfspr r23,SPRG3 /* Get PACA */ std r22,PACAKSAVE(r23) /* r1 is now kernel sp */ ld r2,PACATOC(r23) /* Get Kernel TOC pointer */ ld r22,PACACURRENT(r23) /* Get CURRENT */ -/* - * If from user state, update THREAD.regs - */ + + /* + * If from user state, update THREAD.regs + */ beq 2f /* Modify THREAD.regs if from user */ addi r24,r1,STACK_FRAME_OVERHEAD std r24,THREAD+PT_REGS(r22) 2: -/* - * Since we store 'current' in the PACA now, we don't need to - * set it here. When r2 was used as 'current' it had to be - * set here because it could have been changed by the user. - */ + /* + * Since we store 'current' in the PACA now, we don't need to + * set it here. When r2 was used as 'current' it had to be + * set here because it could have been changed by the user. + */ -/* - * Check for kernel stack overflow - */ - addi r24,r22,TASK_STRUCT_SIZE /* check for kernel stack overflow */ - cmpld 0,r1,r22 - cmpld 1,r1,r24 - crand 1,1,4 - bgt- .stack_ovf /* if r22 < r1 < r22+TASK_STRUCT_SIZE */ - -/* - * Get the handler address and its return address and - * rfid to the handler - */ -/* MIKEC: Do we really need to rfid here? Since we are already running - with relocate on, could we just branch to the handler? - We also will be turning on MSR.EE for some interrupts here -*/ - mflr r23 - ld r24,0(r23) /* virtual address of handler */ - ld r23,8(r23) /* where to go when done */ - mtspr SRR0,r24 - mtspr SRR1,r20 - mtlr r23 - rfid /* jump to handler */ + SET_REG_TO_CONST(r22, MSR_KERNEL) + +#ifdef DO_SOFT_DISABLE + stb r20,PACAPROCENABLED(r23) /* possibly soft enable */ + ori r22,r22,MSR_EE /* always hard enable */ +#else + rldimi r22,r20,15,48 /* Insert desired EE value */ +#endif + + mtmsrd r22 + blr -#ifdef CONFIG_SMP /* * On pSeries, secondary processors spin in the following code. * At entry, r3 = this processor's number (in Linux terms, not hardware). @@ -918,11 +1115,12 @@ ldx r1,r3,r28 cmpi 0,r23,0 +#ifdef CONFIG_SMP #ifdef SECONDARY_PROCESSORS bne .__secondary_start #endif +#endif b 1b /* Loop until told to go */ -#endif /* CONFIG_SMP */ _GLOBAL(__start_initialization_iSeries) @@ -947,6 +1145,8 @@ li r3,1 /* Indicate to skip adding esid C to table */ bl .stab_initialize + bl .iSeries_fixup_klimit + b .start_here_common _GLOBAL(__start_initialization_pSeries) @@ -1106,20 +1306,6 @@ copy_to_here: /* - * On kernel stack overflow, load up an initial stack pointer - * and call StackOverflow(regs), which should not return. - */ -_STATIC(stack_ovf) - addi r3,r1,STACK_FRAME_OVERHEAD - LOADADDR(r1,init_task_union) - addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD - LOADADDR(r24,StackOverflow) - SET_REG_TO_CONST(r20, MSR_KERNEL); - mtspr SRR0,r24 - mtspr SRR1,r20 - rfid - -/* * Disable FP for the task which had the FPU previously, * and save its floating-point registers in its thread_struct. * Enables the FPU for use in the kernel on return. @@ -1130,7 +1316,7 @@ mfmsr r5 /* grab the current MSR */ ori r5,r5,MSR_FP mtmsrd r5 /* enable use of fpu now */ - SYNC + isync /* * For SMP, we don't do lazy FPU switching because it just gets too * horrendously complex, especially when a task switches from one CPU @@ -1166,19 +1352,7 @@ std r4,last_task_used_math@l(r3) #endif /* CONFIG_SMP */ /* restore registers and return */ - ld r3,_CCR(r21) - ld r4,_LINK(r21) - mtcrf 0xff,r3 - mtlr r4 - REST_2GPRS(1, r21) - REST_4GPRS(3, r21) - /* we haven't used ctr or xer */ - mtspr SRR1,r23 - mtspr SRR0,r22 - REST_GPR(20, r21) - REST_2GPRS(22, r21) - ld r21,GPR21(r21) - rfid + b fast_exception_return /* * FP unavailable trap from kernel - print a message, but let @@ -1206,7 +1380,7 @@ mfmsr r5 ori r5,r5,MSR_FP mtmsrd r5 /* enable use of fpu now */ - SYNC + isync cmpi 0,r3,0 beqlr- /* if no previous owner, done */ addi r3,r3,THREAD /* want THREAD of task */ @@ -1255,6 +1429,7 @@ std r2,PACATOC(r25) li r6,0 std r6,PACAKSAVE(r25) + stb r6,PACAPROCENABLED(r25) #ifndef CONFIG_PPC_ISERIES /* Initialize the page table pointer register. */ @@ -1295,6 +1470,9 @@ /* enable MMU and jump to start_secondary */ LOADADDR(r3,.start_secondary) SET_REG_TO_CONST(r4, MSR_KERNEL) +#ifdef DO_SOFT_DISABLE + ori r4,r4,MSR_EE +#endif mtspr SRR0,r3 mtspr SRR1,r4 rfid @@ -1312,7 +1490,7 @@ rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) or r11,r11,r12 mtmsrd r11 - SYNC + isync blr /* @@ -1327,7 +1505,7 @@ rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) andc r11,r11,r12 mtmsrd r11 - SYNC + isync blr @@ -1466,14 +1644,19 @@ std r2,PACATOC(r4) li r5,0 std r0,PACAKSAVE(r4) -/* - * Register old definition new definition - * - * SPRG0 temp - used to save gpr reserved for hypervisor - * SPRG1 temp - used to save gpr unused - * SPRG2 0 or kernel stack frame temp - used to save gpr - * SPRG3 Linux thread virt addr of Paca - */ + + /* ptr to hardware interrupt stack for processor 0 */ + LOADADDR(r3, hardware_int_paca0) + li r5,0x1000 + sldi r5,r5,3 + subi r5,r5,STACK_FRAME_OVERHEAD + + add r3,r3,r5 + std r3,PACAHRDWINTSTACK(r4) + + li r3,0 + stb r3,PACAHRDWINTCOUNT(r4) + /* * Restore the parms passed in from the bootloader. @@ -1488,6 +1671,15 @@ /* Load up the kernel context */ 5: +#ifdef DO_SOFT_DISABLE + mfspr r4,SPRG3 + li r5,0 + stb r5,PACAPROCENABLED(r4) /* Soft Disabled */ + mfmsr r5 + ori r5,r5,MSR_EE /* Hard Enabled */ + mtmsrd r5 +#endif + bl .start_kernel /* @@ -1514,6 +1706,11 @@ .globl bolted_dir bolted_dir: .space 4096 + + .globl hardware_int_paca0 +hardware_int_paca0: + .space 8*4096 + /* 4096 * 31 bytes of storage */ .globl stab_array diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/htab.c linuxppc64_2_4/arch/ppc64/kernel/htab.c --- linux-2.4.9-ac10/arch/ppc64/kernel/htab.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/htab.c Thu Sep 27 13:46:21 2001 @@ -38,6 +38,7 @@ #include #include #include +#include #include /* For iSeries */ @@ -47,13 +48,8 @@ * HPTE --> PowerPC Hashed Page Table Entry */ -HTAB htab_data = {NULL, NULL, 0, 0, 0}; +HTAB htab_data = {NULL, 0, 0, 0, 0}; -static ssize_t ppc_htab_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_htab_write(struct file * file, const char * buffer, - size_t count, loff_t *ppos); -static long ppc_htab_lseek(struct file * file, loff_t offset, int orig); int proc_dol2crvec(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp); @@ -72,8 +68,6 @@ extern inline void make_ste(unsigned long stab, unsigned long esid, unsigned long vsid); -extern pgd_t * bolted_pgd; - extern char _stext[], _etext[], __start_naca[], __end_stab[]; static spinlock_t hash_table_lock = SPIN_LOCK_UNLOCKED; @@ -83,6 +77,7 @@ #define RELOC(x) (*PTRRELOC(&(x))) extern unsigned long htab_size( unsigned long ); +unsigned long hpte_getword0_iSeries( unsigned long slot ); static inline void create_pte_mapping(unsigned long start, unsigned long end, @@ -113,24 +108,36 @@ * Calculate the required size of the htab. We want the number of * PTEGs to equal one half the number of real pages. */ - htab_size_bytes = htab_size(_naca->physicalMemorySize); + htab_size_bytes = 1UL << _naca->pftSize; pteg_count = htab_size_bytes >> 7; - _htab_data->htab_num_ptegs = pteg_count; - _htab_data->htab_hash_mask = pteg_count - 1; - /* Find storage for the HPT. Must be contiguous in - * the absolute address space. - */ - table = lmb_alloc(htab_size_bytes, htab_size_bytes); - if ( !table ) - panic("ERROR, cannot find space for HPTE\n"); - _htab_data->htab = (HPTE *)__a2v(table); + /* For debug, make the HTAB 1/8 as big as it normally would be. */ + ifppcdebug(PPCDBG_HTABSIZE) { + pteg_count >>= 3; + htab_size_bytes = pteg_count << 7; + } - /* htab absolute addr + encoded htabsize */ - RELOC(_SDR1) = table + __ilog2(pteg_count) - 11; + _htab_data->htab_num_ptegs = pteg_count; + _htab_data->htab_hash_mask = pteg_count - 1; - /* Initialize the HPT with no entries */ - cacheable_memzero((void *)table, htab_size_bytes); + if(_machine == _MACH_pSeries) { + /* Find storage for the HPT. Must be contiguous in + * the absolute address space. + */ + table = lmb_alloc(htab_size_bytes, htab_size_bytes); + if ( !table ) + panic("ERROR, cannot find space for HPTE\n"); + _htab_data->htab = (HPTE *)__a2v(table); + + /* htab absolute addr + encoded htabsize */ + RELOC(_SDR1) = table + __ilog2(pteg_count) - 11; + + /* Initialize the HPT with no entries */ + cacheable_memzero((void *)table, htab_size_bytes); + } else { + _htab_data->htab = NULL; + RELOC(_SDR1) = 0; + } mode_ro = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RXRX; mode_rw = _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX; @@ -194,44 +201,42 @@ for(;x;x|=1); } -void invalidate_hpte( unsigned long slot ) +/* Functions to invalidate a HPTE */ +static void hpte_invalidate_iSeries( unsigned long slot ) { + HvCallHpt_invalidateSetSwBitsGet( slot, 0, 0 ); +} - /* Invalidate the HPTE */ - - if ( _machine == _MACH_iSeries ) { - HvCallHpt_invalidateSetSwBitsGet( slot, 0, 0 ); - } - else { - /* Local copy of the first doubleword of the HPTE */ - union { - unsigned long d; - Hpte_dword0 h; - } hpte_dw0; +static void hpte_invalidate_pSeries( unsigned long slot ) +{ + /* Local copy of the first doubleword of the HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; - /* Locate the HPTE */ - HPTE * hptep = htab_data.htab + slot; + /* Locate the HPTE */ + HPTE * hptep = htab_data.htab + slot; - /* Get the first doubleword of the HPTE */ - hpte_dw0.d = hptep->dw0.dword0; - - /* Invalidate the hpte */ - hptep->dw0.dword0 = 0; + /* Get the first doubleword of the HPTE */ + hpte_dw0.d = hptep->dw0.dword0; - /* Invalidate the tlb */ - { - unsigned long vsid, group, pi, pi_high; - - vsid = hpte_dw0.h.avpn >> 5; - group = slot >> 3; - if(hpte_dw0.h.h) { - group = ~group; - } - pi = (vsid ^ group) & 0x7ff; - pi_high = (hpte_dw0.h.avpn & 0x1f) << 11; - pi |= pi_high; - _tlbie(pi << 12); - } + /* Invalidate the hpte */ + hptep->dw0.dword0 = 0; + + /* Invalidate the tlb */ + { + unsigned long vsid, group, pi, pi_high; + + vsid = hpte_dw0.h.avpn >> 5; + group = slot >> 3; + if(hpte_dw0.h.h) { + group = ~group; + } + pi = (vsid ^ group) & 0x7ff; + pi_high = (hpte_dw0.h.avpn & 0x1f) << 11; + pi |= pi_high; + _tlbie(pi << 12); } } @@ -240,48 +245,73 @@ * return slot index (if in primary group) * return -slot index (if in secondary group) */ -long select_hpte_slot( unsigned long vpn ) +static long hpte_selectslot_iSeries( unsigned long vpn ) { - -/****** Add code for iSeries ******/ - - HPTE * hptep; HPTE hpte; + long ret_slot, orig_slot; unsigned long primary_hash; unsigned long hpteg_slot; - long ret_slot, orig_slot; + unsigned long slot; unsigned i, k; + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; - if ( _machine == _MACH_iSeries ) { - ret_slot = orig_slot = HvCallHpt_findValid( &hpte, vpn ); - if ( hpte.dw0.dw0.v ) { /* If valid ...what do we do now? */ - udbg_printf( "select_hpte_slot: vpn 0x%016lx already valid at slot 0x%016lx\n", vpn, ret_slot ); - udbg_printf( "select_hpte_slot: returned hpte 0x%016lx 0x%016lx\n", hpte.dw0.dword0, hpte.dw1.dword1 ); - - return (0x8000000000000000); -/* panic("select_hpte_slot found entry already valid\n"); */ - } - if ( ret_slot == -1 ) { /* -1 indicates no available slots */ - /* - * Eventually we will have a hypervisor call which will allow us to conditionally - * invalidate an hpte depending on whether it is bolted. For now, we need to look - * each entry to see if it is bolted before we invalidate it. - */ - - - /* ADD CODE HERE */ - /* add code to cast out a non-bolted hpte */ - } - else { - if ( ret_slot < 0 ) { - ret_slot &= 0x7fffffffffffffff; - ret_slot = -ret_slot; + ret_slot = orig_slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { /* If valid ...what do we do now? */ + udbg_printf( "hpte_selectslot_iSeries: vpn 0x%016lx already valid at slot 0x%016lx\n", vpn, ret_slot ); + udbg_printf( "hpte_selectslot_iSeries: returned hpte 0x%016lx 0x%016lx\n", hpte.dw0.dword0, hpte.dw1.dword1 ); + + return (0x8000000000000000); + /* panic("select_hpte_slot found entry already valid\n"); */ + } + if ( ret_slot == -1 ) { /* -1 indicates no available slots */ + + /* No available entry found in secondary group */ + + PMC_SW_SYSTEM(htab_capacity_castouts); + + primary_hash = hpt_hash( vpn ); + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + k = htab_data.next_round_robin++ & 0x7; + + for ( i=0; idw0.dword0; - } + HPTE hpte; + HvCallHpt_get( &hpte, slot ); + dword0 = hpte.dw0.dword0; return dword0; } -long find_hpte( unsigned long vpn ) +unsigned long hpte_getword0_pSeries( unsigned long slot ) +{ + unsigned long dword0; + HPTE * hptep = htab_data.htab + slot; + + dword0 = hptep->dw0.dword0; + return dword0; +} + +static long hpte_find_iSeries(unsigned long vpn) { HPTE hpte; long slot; - if ( _machine == _MACH_iSeries ) { - slot = HvCallHpt_findValid( &hpte, vpn ); - if ( hpte.dw0.dw0.v ) { - if ( slot < 0 ) { - slot &= 0x7fffffffffffffff; - slot = -slot; - } + slot = HvCallHpt_findValid( &hpte, vpn ); + if ( hpte.dw0.dw0.v ) { + if ( slot < 0 ) { + slot &= 0x7fffffffffffffff; + slot = -slot; } - else - slot = -1; - } - else { - union { - unsigned long d; - Hpte_dword0 h; - } hpte_dw0; - unsigned long hash; - unsigned long i,j; + } else + slot = -1; + return slot; +} - hash = hpt_hash( vpn ); - for ( j=0; j<2; ++j ) { - slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; - for ( i=0; i> 11 ) ) && - ( hpte_dw0.h.v ) && - ( hpte_dw0.h.h == j ) ) { - /* HPTE matches */ - if ( j ) - slot = -slot; - return slot; - } - ++slot; +static long hpte_find_pSeries(unsigned long vpn) +{ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + long slot; + unsigned long hash; + unsigned long i,j; + + hash = hpt_hash( vpn ); + for ( j=0; j<2; ++j ) { + slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; + for ( i=0; i> 11 ) ) && + ( hpte_dw0.h.v ) && + ( hpte_dw0.h.h == j ) ) { + /* HPTE matches */ + if ( j ) + slot = -slot; + return slot; } - hash = ~hash; + ++slot; } - slot = -1; + hash = ~hash; } - return slot; + return -1; } /* This function is called by btmalloc to bolt an entry in the hpt */ @@ -421,7 +454,7 @@ spin_lock_irqsave( &hash_table_lock, flags ); - hpte_slot = select_hpte_slot( vpn ); + hpte_slot = ppc_md.hpte_selectslot( vpn ); hash = 0; if ( hpte_slot < 0 ) { if ( hpte_slot == 0x8000000000000000 ) { @@ -434,7 +467,7 @@ hash = 1; hpte_slot = -hpte_slot; } - create_valid_hpte( hpte_slot, vpn, pa >> 12, hash, ptep, + ppc_md.hpte_create_valid( hpte_slot, vpn, pa >> 12, hash, ptep, hpteflags, bolted ); if ( ptep ) { @@ -465,10 +498,10 @@ * The HPTE is set with the vpn, rpn (converted to absolute) * and flags */ -void create_valid_hpte(unsigned long slot, unsigned long vpn, - unsigned long prpn, unsigned hash, - void * ptep, unsigned hpteflags, - unsigned bolted ) +static void hpte_create_valid_iSeries(unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + unsigned bolted ) { /* Local copy of HPTE */ struct { @@ -500,33 +533,64 @@ lhpte.dw0.h.v = 1; /* Now fill in the actual HPTE */ + HvCallHpt_addValidate( slot, hash, (HPTE *)&lhpte ); +} - PPCDBG(PPCDBG_MM, "create_valid_hpte: slot=0x%lx, hash=%d, hpte=0x%016lx 0x%016lx\n", slot, hash, lhpte.dw0.d, lhpte.dw1.d ); +static void hpte_create_valid_pSeries(unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + unsigned bolted) +{ + /* Local copy of HPTE */ + struct { + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } dw0; + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } dw1; + } lhpte; - if ( _machine == _MACH_iSeries ) { - HvCallHpt_addValidate( slot, hash, (HPTE *)&lhpte ); - } - else { - HPTE * hptep = htab_data.htab + slot; + unsigned long avpn = vpn >> 11; + unsigned long arpn = physRpn_to_absRpn( prpn ); - /* Set the second dword first so that the valid bit - * is the last thing set - */ + HPTE *hptep; - hptep->dw1.dword1 = lhpte.dw1.d; + /* Fill in the local HPTE with absolute rpn, avpn and flags */ + lhpte.dw1.d = 0; + lhpte.dw1.h.rpn = arpn; + lhpte.dw1.f.flags = hpteflags; - /* Guarantee the second dword is visible before - * the valid bit - */ + lhpte.dw0.d = 0; + lhpte.dw0.h.avpn = avpn; + lhpte.dw0.h.h = hash; + lhpte.dw0.h.bolted = bolted; + lhpte.dw0.h.v = 1; - __asm__ __volatile__ ("eieio" : : : "memory"); + /* Now fill in the actual HPTE */ + hptep = htab_data.htab + slot; - /* Now set the first dword including the valid bit */ - hptep->dw0.dword0 = lhpte.dw0.d; + /* Set the second dword first so that the valid bit + * is the last thing set + */ + + hptep->dw1.dword1 = lhpte.dw1.d; - __asm__ __volatile__ ("ptesync" : : : "memory"); + /* Guarantee the second dword is visible before + * the valid bit + */ + + __asm__ __volatile__ ("eieio" : : : "memory"); - } + /* Now set the first dword including the valid bit */ + hptep->dw0.dword0 = lhpte.dw0.d; + + __asm__ __volatile__ ("ptesync" : : : "memory"); } /* find_linux_pte returns the address of a linux pte for a given @@ -564,49 +628,69 @@ 1 ) ); } -static void updateHptePP( long slot, unsigned long newpp, unsigned long va ) +static void hpte_updatepp_iSeries(long slot, unsigned long newpp, unsigned long va) { - if ( _machine == _MACH_iSeries ) { - HvCallHpt_setPp( slot, newpp ); - } - else { - /* Local copy of first doubleword of HPTE */ - union { - unsigned long d; - Hpte_dword0 h; - } hpte_dw0; - - /* Local copy of second doubleword of HPTE */ - union { - unsigned long d; - Hpte_dword1 h; - Hpte_dword1_flags f; - } hpte_dw1; - - HPTE * hptep = htab_data.htab + slot; - - /* Turn off valid bit in HPTE */ - hpte_dw0.d = hptep->dw0.dword0; - hpte_dw0.h.v = 0; - hptep->dw0.dword0 = hpte_dw0.d; - - /* Ensure it is out of the tlb too */ - _tlbie( va ); - - /* Insert the new pp bits into the HPTE */ - hpte_dw1.d = hptep->dw1.dword1; - hpte_dw1.h.pp = newpp; - hptep->dw1.dword1 = hpte_dw1.d; + HvCallHpt_setPp( slot, newpp ); +} - /* Ensure it is visible before validating */ - __asm__ __volatile__ ("eieio" : : : "memory"); +static void hpte_updatepp_pSeries(long slot, unsigned long newpp, unsigned long va) +{ + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } hpte_dw1; + + HPTE * hptep = htab_data.htab + slot; + + /* Turn off valid bit in HPTE */ + hpte_dw0.d = hptep->dw0.dword0; + hpte_dw0.h.v = 0; + hptep->dw0.dword0 = hpte_dw0.d; + + /* Ensure it is out of the tlb too */ + _tlbie( va ); + + /* Insert the new pp bits into the HPTE */ + hpte_dw1.d = hptep->dw1.dword1; + hpte_dw1.h.pp = newpp; + hptep->dw1.dword1 = hpte_dw1.d; + + /* Ensure it is visible before validating */ + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* Turn the valid bit back on in HPTE */ + hpte_dw0.h.v = 1; + hptep->dw0.dword0 = hpte_dw0.d; - /* Turn the valid bit back on in HPTE */ - hpte_dw0.h.v = 1; - hptep->dw0.dword0 = hpte_dw0.d; + __asm__ __volatile__ ("ptesync" : : : "memory"); +} - __asm__ __volatile__ ("ptesync" : : : "memory"); - } +/* This is called very early. */ +void htpe_init_iSeries(void) +{ + ppc_md.hpte_invalidate = hpte_invalidate_iSeries; + ppc_md.hpte_updatepp = hpte_updatepp_iSeries; + ppc_md.hpte_getword0 = hpte_getword0_iSeries; + ppc_md.hpte_selectslot = hpte_selectslot_iSeries; + ppc_md.hpte_create_valid = hpte_create_valid_iSeries; + ppc_md.hpte_find = hpte_find_iSeries; +} +void htpe_init_pSeries(void) +{ + ppc_md.hpte_invalidate = hpte_invalidate_pSeries; + ppc_md.hpte_updatepp = hpte_updatepp_pSeries; + ppc_md.hpte_getword0 = hpte_getword0_pSeries; + ppc_md.hpte_selectslot = hpte_selectslot_pSeries; + ppc_md.hpte_create_valid = hpte_create_valid_pSeries; + ppc_md.hpte_find = hpte_find_pSeries; } /* Handle a fault by adding an HPTE @@ -640,9 +724,7 @@ * we're bolting the entire 0xC0... region. */ udbg_printf("Doh!!! hash_page saw a kernel address...\n"); -#if defined(CONFIG_XMON) - xmon(0); -#endif + PPCDBG_ENTER_DEBUGGER(); for(;x;x|=1); vsid = get_kernel_vsid( ea ); @@ -671,7 +753,6 @@ } vsid = get_vsid(mm->context, ea ); break; - case BOLTED_REGION_ID: default: /* Not a valid range, send the problem up to do_page_fault */ return 1; @@ -760,7 +841,7 @@ if ( pte_val(old_pte) & _PAGE_HPTENOIX ) { unsigned long slot; pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; - slot = find_hpte( vpn ); + slot = ppc_md.hpte_find( vpn ); if ( slot != -1 ) { if ( slot < 0 ) { pte_val(old_pte) |= _PAGE_SECONDARY; @@ -800,12 +881,12 @@ slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; slot += (pte_val(old_pte) & _PAGE_GROUP_IX) >> 12; /* If there is an HPTE for this page it is indexed by slot */ - hpte_dw0.d = get_hpte0( slot ); + hpte_dw0.d = ppc_md.hpte_getword0( slot ); if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && (hpte_dw0.h.v) && (hpte_dw0.h.h == secondary ) ){ /* HPTE matches */ - updateHptePP( slot, newpp, va ); + ppc_md.hpte_updatepp( slot, newpp, va ); if ( !pte_same( old_pte, new_pte ) ) *ptep = new_pte; } @@ -822,7 +903,7 @@ * * Find an available HPTE slot */ - slot = select_hpte_slot( vpn ); + slot = ppc_md.hpte_selectslot( vpn ); /* Debug code */ if ( slot == 0x8000000000000000 ) { @@ -865,7 +946,7 @@ hpteflags = (pte_val(new_pte) & 0x1f8) | newpp; /* Create the HPTE */ - create_valid_hpte( slot, vpn, prpn, hash_ind, ptep, hpteflags, 0 ); + ppc_md.hpte_create_valid( slot, vpn, prpn, hash_ind, ptep, hpteflags, 0 ); } @@ -905,11 +986,6 @@ ptep = find_linux_pte(pgdir, ea); pa = pte_pagenr(*ptep) << PAGE_SHIFT; break; - case BOLTED_REGION_ID: - pgdir = bolted_pgd; - ptep = find_linux_pte(pgdir, ea); - pa = pte_pagenr(*ptep) << PAGE_SHIFT; - break; default: break; } @@ -943,12 +1019,12 @@ /* If there is an HPTE for this page it is indexed by slot */ spin_lock_irqsave( &hash_table_lock, flags); - hpte_dw0.d = get_hpte0( slot ); + hpte_dw0.d = ppc_md.hpte_getword0( slot ); if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && (hpte_dw0.h.v) && (hpte_dw0.h.h == secondary ) ){ /* HPTE matches */ - invalidate_hpte( slot ); + ppc_md.hpte_invalidate( slot ); } else { unsigned k; @@ -959,7 +1035,7 @@ hash = ~hash; slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; for ( k=0; k<8; ++k ) { - hpte_dw0.d = get_hpte0( slot+k ); + hpte_dw0.d = ppc_md.hpte_getword0( slot+k ); if ( ( hpte_dw0.h.avpn == (vpn >> 11) ) && ( hpte_dw0.h.v ) && ( hpte_dw0.h.h == secondary ) ) { @@ -1101,67 +1177,55 @@ return 0; } -static long -ppc_htab_lseek(struct file * file, loff_t offset, int orig) -{ - switch (orig) { - case 0: - file->f_pos = offset; - return(file->f_pos); - case 1: - file->f_pos += offset; - return(file->f_pos); - case 2: - return(-EINVAL); - default: - return(-EINVAL); - } -} -/* - * Allow user to define performance counters and resize the hash table - */ -static ssize_t ppc_htab_write(struct file * file, const char * buffer, - size_t count, loff_t *ppos) +static __inline__ void set_pp_bit(unsigned long pp, HPTE *addr) { - return -EINVAL; + unsigned long old; + unsigned long *p = (unsigned long *)(&(addr->dw1)); + + __asm__ __volatile__( + "1: ldarx %0,0,%3\n\ + rldimi %0,%2,0,62\n\ + stdcx. %0,0,%3\n\ + bne 1b" + : "=&r" (old), "=m" (*p) + : "r" (pp), "r" (p), "m" (*p) + : "cc"); } /* - * print some useful info about the hash table. This function - * is _REALLY_ slow (see the nested for loops below) but nothing - * in here should be really timing critical. -- Cort + * Update the page protection bits. Intended to be used to create + * guard pages for kernel data structures on pages which are bolted + * in the HPT. Assumes pages being operated on will not be stolen. */ -static ssize_t ppc_htab_read(struct file * file, char * buf, - size_t count, loff_t *ppos) +void updateBoltedHptePP( unsigned long newpp, unsigned long ea ) { - return -EINVAL; -} + unsigned long vsid,va,vpn,flags; + long slot; -/* round mem size up to next power of 2 -- htab size must be a power of 2 */ -unsigned long -rounded_mem_size(unsigned long mem_size) -{ - unsigned long rnd_mem_size; + + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; - rnd_mem_size = 1UL << (unsigned long)__ilog2(mem_size); - if ( rnd_mem_size < mem_size ) - rnd_mem_size <<= 1; + slot = ppc_md.hpte_find( vpn ); + + if ( _machine == _MACH_iSeries ) { + HvCallHpt_setPp( slot, newpp ); + } else { + HPTE * hptep = htab_data.htab + slot; - return rnd_mem_size; -} + if (!htab_data.htab) return; + set_pp_bit(newpp , hptep ); -unsigned long -htab_size(unsigned long mem_size) -{ - unsigned long offset = reloc_offset(); - struct Naca *_naca = RELOC(naca); - unsigned long rnd_mem_size = rounded_mem_size(mem_size); - unsigned long pteg_count = (rnd_mem_size >> (12 + 1)); /* #pages/2 */ - /* For debug, make the HTAB 1/8 as big as it normally would be. */ - if(_naca->debug_switch & PPCDBG_HTABSIZE) { - pteg_count >>= 3; + /* Ensure it is out of the tlb too */ + spin_lock_irqsave( &hash_table_lock, flags ); + _tlbie( va ); + spin_unlock_irqrestore( &hash_table_lock, flags ); + + /* Ensure it is visible before validating */ + __asm__ __volatile__ ("eieio" : : : "memory"); + __asm__ __volatile__ ("ptesync" : : : "memory"); } - return (pteg_count << 7); /* pteg_count*128B/PTEG */ } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_IoMmTable.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_IoMmTable.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.c Sat Sep 22 08:10:50 2001 @@ -30,27 +30,32 @@ #include #include #include +#include #include "iSeries_IoMmTable.h" #include "pci.h" -void iSeries_allocateDeviceBars(struct pci_dev* PciDevPtr); /*******************************************************************/ /* Table defines */ /* Entry size is 4 MB * 1024 Entries = 4GB. */ /*******************************************************************/ -#define iSeries_IoMmTable_Entry_Size 0x00400000 #define iSeries_IoMmTable_Size 1024 -#define iSeries_Base_Io_Memory 0xFFFFFFFF +unsigned long iSeries_IoMmTable_Entry_Size = 0x0000000000400000; +unsigned long iSeries_Base_Io_Memory = 0xE000000000000000; + +/*******************************************************************/ +/* Lookup Tables. */ +/*******************************************************************/ +struct iSeries_Device_Node* iSeries_IoMmTable[iSeries_IoMmTable_Size]; +u8 iSeries_IoBarTable[iSeries_IoMmTable_Size]; /*******************************************************************/ /* Static and Global variables */ /*******************************************************************/ -struct pci_dev* iSeries_IoMmTable[iSeries_IoMmTable_Size]; -u8 iSeries_IoBarTable[iSeries_IoMmTable_Size]; -static int iSeries_CurrentIndex; +static long iSeries_CurrentIndex; static char* iSeriesPciIoText = "iSeries PCI I/O"; static spinlock_t iSeriesIoMmTableLock = SPIN_LOCK_UNLOCKED; + /*******************************************************************/ /* iSeries_IoMmTable_Initialize */ /*******************************************************************/ @@ -59,15 +64,16 @@ /* methods. */ /*******************************************************************/ void iSeries_IoMmTable_Initialize(void) { - int Index; + int Index = 0; spin_lock(&iSeriesIoMmTableLock); - for(Index=0;Indexresource[BarNumber]; iSeries_IoMmTable_AllocateEntry(PciDev, BarNumber); @@ -98,90 +102,94 @@ /*******************************************************************/ /* - Allocates the number of entries required in table base on BAR */ /* size. */ -/* - This version, allocates top down, starting at 4GB. */ +/* - This version, allocates starting at iSeries_Base_Io_Memory and*/ +/* allocate up. */ /* - The size is round up to be a multiple of entry size. */ -/* - CurrentIndex is decremented to keep track of the last entry. */ +/* - CurrentIndex is incremented to keep track of the last entry. */ /* - Builds the resource entry for allocated BARs. */ /*******************************************************************/ void iSeries_IoMmTable_AllocateEntry(struct pci_dev* PciDev, int BarNumber) { - struct resource* BarResource = &PciDev->resource[BarNumber]; - int BarSize = BarResource->end - BarResource->start; - unsigned long BarStartAddr; - unsigned long BarEndAddr; - /***************************************************************/ - /* No space to allocate, skip Allocation. */ - /***************************************************************/ - if(BarSize == 0) return; /* Quick stage exit */ - - /***************************************************************/ - /* Allocate the table entries needed. */ - /***************************************************************/ + struct resource* BarResource = &PciDev->resource[BarNumber]; + long BarSize = pci_resource_len(PciDev,BarNumber); + /*************************************************************** + * No space to allocate, quick exit, skip Allocation. * + ***************************************************************/ + if(BarSize == 0) return; + PPCDBG(PPCDBG_BUSWALK,"iSeries_IoMmTable_AllocateEntry Bar:%2d Index:0x%08X\n",BarNumber,iSeries_CurrentIndex); + /************************************************************** + * Set Resource values. * + ***************************************************************/ + BarResource->name = iSeriesPciIoText; + BarResource->start = iSeries_IoMmTable_Entry_Size*(iSeries_CurrentIndex); + BarResource->start+= iSeries_Base_Io_Memory; + BarResource->end = BarResource->start+BarSize-1; + /************************************************************** + * Allocate the number of table entries needed. * + ***************************************************************/ spin_lock(&iSeriesIoMmTableLock); - while(BarSize > 0) { - iSeries_IoMmTable[iSeries_CurrentIndex] = PciDev; + while(BarSize > 0 ) { + iSeries_IoMmTable [iSeries_CurrentIndex] = (struct iSeries_Device_Node*)PciDev->sysdata; iSeries_IoBarTable[iSeries_CurrentIndex] = BarNumber; BarSize -= iSeries_IoMmTable_Entry_Size; - --iSeries_CurrentIndex; /* Next Free entry */ + ++iSeries_CurrentIndex; } spin_unlock(&iSeriesIoMmTableLock); - BarStartAddr = iSeries_IoMmTable_Entry_Size*(iSeries_CurrentIndex+1); - BarEndAddr = BarStartAddr + (BarResource->end - BarResource->start); - /***************************************************************/ - /* Build Resource info */ - /***************************************************************/ - BarResource->name = iSeriesPciIoText; - BarResource->start = (long)BarStartAddr; - BarResource->end = (long)BarEndAddr; - - PPCDBG(PPCDBG_BUSWALK,"BarAloc %04X-%016LX\n",iSeries_CurrentIndex+1,BarStartAddr); } /*******************************************************************/ /* Translates an I/O Memory address to pci_dev* */ /*******************************************************************/ -struct pci_dev* iSeries_xlateIoMmAddress(unsigned long* IoAddress) { - int PciDevIndex = (unsigned long)IoAddress/iSeries_IoMmTable_Entry_Size; - struct pci_dev* PciDev = iSeries_IoMmTable[PciDevIndex]; - if(PciDev == 0) { - printk("PCI: Invalid I/O Address: 0x%016LX\n",IoAddress); - PCIFR("Invalid MMIO Address 0x%016LX",(unsigned long)IoAddress); +struct iSeries_Device_Node* iSeries_xlateIoMmAddress(void* IoAddress) { + long TableIndex = ((unsigned long)IoAddress-iSeries_Base_Io_Memory)/iSeries_IoMmTable_Entry_Size; + struct iSeries_Device_Node* DevNode = iSeries_IoMmTable[TableIndex]; + if(DevNode == NULL) { + printk("PCI: Invalid I/O Address: %p\n",IoAddress); + PCIFR( "Invalid MMIO Address %p", IoAddress); } - return PciDev; + return DevNode; } /************************************************************************/ /* Returns the Bar number of Address */ /************************************************************************/ -int iSeries_IoMmTable_Bar(unsigned long* IoAddress) { - int BarIndex = (unsigned long)IoAddress/iSeries_IoMmTable_Entry_Size; - int BarNumber = iSeries_IoBarTable[BarIndex]; - return BarNumber; -} -/************************************************************************/ -/* Return the Bar Base Address or 0. */ -/************************************************************************/ -unsigned long* iSeries_IoMmTable_BarBase(unsigned long* IoAddress) { - unsigned long BaseAddr = 0; - struct pci_dev* PciDev = iSeries_xlateIoMmAddress(IoAddress); - if(PciDev != NULL) { - int BarNumber = iSeries_IoMmTable_Bar(IoAddress); - if(BarNumber != -1) { - BaseAddr = PciDev->resource[BarNumber].start; - } - } - return (unsigned long*)BaseAddr; +int iSeries_IoMmTable_Bar(void* IoAddress) { + long TableIndex = ((unsigned long)IoAddress-iSeries_Base_Io_Memory)/iSeries_IoMmTable_Entry_Size; + return iSeries_IoBarTable[TableIndex]; +} +/************************************************************************/ +/* Return the Bar Base Address or NULL */ +/************************************************************************/ +void* iSeries_IoMmTable_BarBase(void* IoAddress) { + int BarNumber; + long TableIndex = ((unsigned long)IoAddress-iSeries_Base_Io_Memory)/iSeries_IoMmTable_Entry_Size; + struct iSeries_Device_Node* Node = iSeries_IoMmTable[TableIndex]; + if(Node == NULL ) return NULL; + + BarNumber = iSeries_IoMmTable_Bar(IoAddress); + if(BarNumber == 0xFF) return NULL; + + return (void*)(pci_resource_start(Node->PciDev,BarNumber)); } /************************************************************************/ /* Return the Bar offset within the Bar Space */ /* Note: Assumes that address is valid. */ /************************************************************************/ -unsigned long iSeries_IoMmTable_BarOffset(unsigned long* IoAddress) { +unsigned long iSeries_IoMmTable_BarOffset(void* IoAddress) { return (unsigned long)IoAddress-(unsigned long)iSeries_IoMmTable_BarBase(IoAddress); } -/************************************************************************/ -/* Return 0 if Address is valid I/O Address */ -/************************************************************************/ -int iSeries_Is_IoMmAddress(unsigned long* IoAddress) { - if( iSeries_xlateIoMmAddress(IoAddress) == NULL) return 1; - else return 0; +/************************************************************************ + * List the table entries out. + * <4>PCI: 0. PciNode: 0xc0000000014cc300 Bar 0, + * <4>Pci: 1. Max Entries = 8 + ************************************************************************/ +void iSeries_IoMmTable_Status(void) { + int Loop = 0; + struct iSeries_Device_Node* PciNode = NULL; + for(Loop = 0;Loop < iSeries_CurrentIndex;++Loop) { + if (PciNode != iSeries_IoMmTable[Loop] ) { + PciNode = iSeries_IoMmTable[Loop] ; + printk("\nPCI: %3d. PciNode: 0x%p Bar ",Loop,PciNode); + } + printk("%2d, ",iSeries_IoBarTable[Loop]); + } + printk("\nPci: %3d. Max Entries = %d\n",Loop, (int)(Loop*sizeof(void*)) ); } - - + \ No newline at end of file diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_IoMmTable.h linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.h --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_IoMmTable.h Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_IoMmTable.h Sat Sep 22 08:11:22 2001 @@ -24,9 +24,12 @@ /************************************************************************/ /* Change Activity: */ /* Created December 12, 2000 */ +/* Ported to ppc64, August 30, 2001 */ /* End Change Activity */ /************************************************************************/ +struct pci_dev; + /************************************************************************/ /* iSeries_IoMmTable_Initialize */ /************************************************************************/ @@ -45,7 +48,7 @@ /* - Allocates ALL pci_dev BAR's and updates the resources with the BAR */ /* value. BARS with zero length will not have the resources. The */ /* HvCallPci_getBarParms is used to get the size of the BAR space. */ -/* It calls as400_IoMmTable_AllocateEntry to allocate each entry. */ +/* It calls iSeries_IoMmTable_AllocateEntry to allocate each entry. */ /* */ /* Parameters: */ /* pci_dev = Pointer to pci_dev structure that will be mapped to pseudo */ @@ -77,23 +80,22 @@ /************************************************************************/ /* iSeries_xlateIoMmAddress */ /************************************************************************/ -/* - Translates an I/O Memory address to pci_dev that has been allocated*/ -/* the psuedo I/O Address. */ +/* - Translates an I/O Memory address to Device Node that has been the */ +/* allocated the psuedo I/O Address. */ /* */ /* Parameters: */ /* IoAddress = I/O Memory Address. */ /* */ /* Return: */ -/* A pci_dev pointer to the device mapped to the I/O address. */ +/* A pci_dev to the device mapped to the I/O address. */ /************************************************************************/ -extern struct pci_dev* iSeries_xlateIoMmAddress(unsigned long* IoAddress); +extern struct iSeries_Device_Node* iSeries_xlateIoMmAddress(void* IoAddress); /************************************************************************/ /* Helper Methods */ /************************************************************************/ -extern int iSeries_IoMmTable_Bar(unsigned long *IoAddress); -extern unsigned long* iSeries_IoMmTable_BarBase(unsigned long* IoAddress); -extern unsigned long iSeries_IoMmTable_BarOffset(unsigned long* IoAddress); -extern int iSeries_Is_IoMmAddress(unsigned long* IoAddress); - +extern int iSeries_IoMmTable_Bar(void* IoAddress); +extern void* iSeries_IoMmTable_BarBase(void* IoAddress); +extern unsigned long iSeries_IoMmTable_BarOffset(void* IoAddress); +extern void iSeries_IoMmTable_Status(void); #endif /* _ISERIES_IOMMTABLE_H */ diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_VpdInfo.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_VpdInfo.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_VpdInfo.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_VpdInfo.c Thu Sep 27 11:19:20 2001 @@ -0,0 +1,295 @@ +/************************************************************************/ +/* File iSeries_vpdInfo.c created by Allan Trautman on Fri Feb 2 2001. */ +/************************************************************************/ +/* This code gets the card location of the hardware */ +/* Copyright (C) 20yy */ +/* */ +/* 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 */ +/************************************************************************/ +/* Change Activity: */ +/* Created, Feb 2, 2001 */ +/* Ported to ppc64, August 20, 2001 */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +//#include +#include +#include "pci.h" + +/************************************************/ +/* Size of Bus VPD data */ +/************************************************/ +#define BUS_VPDSIZE 1024 +/************************************************/ +/* Bus Vpd Tags */ +/************************************************/ +#define VpdEndOfDataTag 0x78 +#define VpdEndOfAreaTag 0x79 +#define VpdIdStringTag 0x82 +#define VpdVendorAreaTag 0x84 +/************************************************/ +/* Mfg Area Tags */ +/************************************************/ +#define VpdFruFlag 0x4647 // "FG" +#define VpdFruFrameId 0x4649 // "FI" +#define VpdSlotMapFormat 0x4D46 // "MF" +#define VpdAsmPartNumber 0x504E // "PN" +#define VpdFruSerial 0x534E // "SN" +#define VpdSlotMap 0x534D // "SM" + +/************************************************/ +/* Structures of the areas */ +/************************************************/ +struct MfgVpdAreaStruct { + u16 Tag; + u8 TagLength; + u8 AreaData1; + u8 AreaData2; +}; +typedef struct MfgVpdAreaStruct MfgArea; +#define MFG_ENTRY_SIZE 3 + +struct SlotMapStruct { + u8 AgentId; + u8 SecondaryAgentId; + u8 PhbId; + char CardLocation[3]; + char Parms[8]; + char Reserved[2]; +}; +typedef struct SlotMapStruct SlotMap; +#define SLOT_ENTRY_SIZE 16 + +/**************************************************************** + * * + * Bus, Card, Board, FrameId, CardLocation. * + ****************************************************************/ +LocationData* iSeries_GetLocationData(struct pci_dev* PciDev) { + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)PciDev->sysdata; + LocationData* LocationPtr = (LocationData*)kmalloc(LOCATION_DATA_SIZE, GFP_KERNEL); + if(LocationPtr == NULL) { + printk("PCI: LocationData area allocation failed!\n"); + return NULL; + } + memset(LocationPtr,0,LOCATION_DATA_SIZE); + LocationPtr->Bus = DevNode->BusNumber; + LocationPtr->Board = DevNode->Board; + LocationPtr->FrameId = DevNode->FrameId; + LocationPtr->Card = PCI_SLOT(DevNode->DevFn); + strcpy(&LocationPtr->CardLocation,&DevNode->CardLocation); + return LocationPtr; +} + +/************************************************************************/ +/* Formats the device information. */ +/* - Pass in pci_dev* pointer to the device. */ +/* - Pass in buffer to place the data. Danger here is the buffer must */ +/* be as big as the client says it is. Should be at least 128 bytes.*/ +/* Return will the length of the string data put in the buffer. */ +/* Format: */ +/* PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet */ +/* controller */ +/************************************************************************/ +int iSeries_Device_Information(struct pci_dev* PciDev,char* Buffer, int BufferSize) { + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)PciDev->sysdata; + char* BufPtr = Buffer; + int LineLen = 0; + + if(DevNode == NULL) { + LineLen = sprintf(BufPtr+LineLen, "PCI: iSeries_Device_Information DevNode is NULL"); + return LineLen; + } + + if(BufferSize >= 128) { + LineLen = sprintf(BufPtr+LineLen,"PCI: Bus%3d, Device%3d, Vendor %04X ", + DevNode->BusNumber, PCI_SLOT(PciDev->devfn),PciDev->vendor); + + LineLen += sprintf(BufPtr+LineLen,"Frame%3d, Card %4s ", DevNode->FrameId,DevNode->CardLocation); + + if(pci_class_name(PciDev->class >> 8) == 0) { + LineLen += sprintf(BufPtr+LineLen,"0x%04X ",(int)(PciDev->class >> 8)); + } + else { + LineLen += sprintf(BufPtr+LineLen,"%s",pci_class_name(PciDev->class >> 8) ); + } + } + return LineLen; +} +/*****************************************************************/ +/* Parse the Slot Area */ +/*****************************************************************/ +void iSeries_Parse_SlotArea(SlotMap* MapPtr,int MapLen, struct iSeries_Device_Node* DevNode) { + int SlotMapLen = MapLen; + SlotMap* SlotMapPtr = MapPtr; + /*************************************************************/ + /* Parse Slot label until we find the one requrested */ + /*************************************************************/ + while(SlotMapLen > 0) { + if(SlotMapPtr->AgentId == DevNode->AgentId ) { + /*******************************************************/ + /* If Phb wasn't found, grab the entry first one found.*/ + /*******************************************************/ + if(DevNode->PhbId == 0xff) { + DevNode->PhbId = SlotMapPtr->PhbId; + } + /**************************************************/ + /* Found it, extract the data. */ + /**************************************************/ + if( SlotMapPtr->PhbId == DevNode->PhbId ) { + memcpy(&DevNode->CardLocation,&SlotMapPtr->CardLocation,3); + DevNode->CardLocation[3] = 0; + //printk("PCI: Slot %p\n",SlotMapPtr); + break; + } + } + /*********************************************************/ + /* Point to the next Slot */ + /*********************************************************/ + SlotMapPtr = (SlotMap*)((char*)SlotMapPtr+SLOT_ENTRY_SIZE); + SlotMapLen -= SLOT_ENTRY_SIZE; + } +} +/*****************************************************************/ +/* Parse the Mfg Area */ +/*****************************************************************/ +static void iSeries_Parse_MfgArea(u8* AreaData,int AreaLen, struct iSeries_Device_Node* DevNode) { + MfgArea* MfgAreaPtr = (MfgArea*)AreaData; + int MfgAreaLen = AreaLen; + u16 SlotMapFmt = 0; + + /*************************************************************/ + /* Parse Mfg Data */ + /*************************************************************/ + while(MfgAreaLen > 0) { + int MfgTagLen = MfgAreaPtr->TagLength; + /*******************************************************/ + /* Frame ID (FI 4649020310 ) */ + /*******************************************************/ + if (MfgAreaPtr->Tag == VpdFruFrameId) { /* FI */ + DevNode->FrameId = MfgAreaPtr->AreaData1; + } + /*******************************************************/ + /* Slot Map Format (MF 4D46020004 ) */ + /*******************************************************/ + else if(MfgAreaPtr->Tag == VpdSlotMapFormat){ /* MF */ + SlotMapFmt = (MfgAreaPtr->AreaData1*256)+(MfgAreaPtr->AreaData2); + } + /*******************************************************/ + /* Slot Map (SM 534D90 */ + /*******************************************************/ + else if(MfgAreaPtr->Tag == VpdSlotMap){ /* SM */ + SlotMap* SlotMapPtr; + if(SlotMapFmt == 0x1004) SlotMapPtr = (SlotMap*)((char*)MfgAreaPtr+MFG_ENTRY_SIZE+1); + else SlotMapPtr = (SlotMap*)((char*)MfgAreaPtr+MFG_ENTRY_SIZE); + iSeries_Parse_SlotArea(SlotMapPtr,MfgTagLen, DevNode); + } + /*********************************************************/ + /* Point to the next Mfg Area */ + /* Use defined size, sizeof give wrong answer */ + /*********************************************************/ + MfgAreaPtr = (MfgArea*)((char*)MfgAreaPtr + MfgTagLen + MFG_ENTRY_SIZE); + MfgAreaLen -= (MfgTagLen + MFG_ENTRY_SIZE); + } +} +/*****************************************************************/ +/* Look for "BUS".. Data is not Null terminated. */ +/* PHBID of 0xFF indicates PHB was not found in VPD Data. */ +/*****************************************************************/ +static int iSeries_Parse_PhbId(u8* AreaPtr,int AreaLength) { + u8* PhbPtr = AreaPtr; + int DataLen = AreaLength; + char PhbId = 0xFF; + while(DataLen > 0) { + if(*PhbPtr == 'B' && *(PhbPtr+1) == 'U' && *(PhbPtr+2) == 'S') { + PhbPtr += 3; + while(*PhbPtr == ' ') ++PhbPtr; + PhbId = (*PhbPtr & 0x0F); + break; + } + ++PhbPtr; + --DataLen; + } + return PhbId; +} + +/****************************************************************/ +/* Parse out the VPD Areas */ +/****************************************************************/ +static void iSeries_Parse_Vpd(u8* VpdData, int VpdDataLen, struct iSeries_Device_Node* DevNode) { + u8* TagPtr = VpdData; + int DataLen = VpdDataLen-3; + /*************************************************************/ + /* Parse the Areas */ + /*************************************************************/ + while(*TagPtr != VpdEndOfAreaTag && DataLen > 0) { + int AreaLen = *(TagPtr+1) + (*(TagPtr+2)*256); + u8* AreaData = TagPtr+3; + + if (*TagPtr == VpdIdStringTag) { + DevNode->PhbId = iSeries_Parse_PhbId(AreaData,AreaLen); + } + else if(*TagPtr == VpdVendorAreaTag) { + iSeries_Parse_MfgArea(AreaData,AreaLen,DevNode); + } + /********************************************************* + * Point to next Area. + *********************************************************/ + TagPtr = AreaData + AreaLen; + DataLen -= AreaLen; + } +} +/**************************************************************** + * iSeries_Get_Location_Code(struct iSeries_Device_Node*) * + * + ****************************************************************/ +void iSeries_Get_Location_Code(struct iSeries_Device_Node* DevNode) { + int BusVpdLen = 0; + u8* BusVpdPtr = (u8*)kmalloc(BUS_VPDSIZE, GFP_KERNEL); + if(BusVpdPtr == NULL) { + printk("PCI: Bus VPD Buffer allocation failure.\n"); + return; + } + BusVpdLen = HvCallPci_getBusVpd(DevNode->BusNumber,REALADDR(BusVpdPtr),BUS_VPDSIZE); + if(BusVpdLen == 0) { + kfree(BusVpdPtr); + printk("PCI: Bus VPD Buffer zero length.\n"); + return; + } + //printk("PCI: BusVpdPtr: %p, %d\n",BusVpdPtr, BusVpdLen); + /*************************************************************/ + /* Make sure this is what I think it is */ + /*************************************************************/ + if(*BusVpdPtr != VpdIdStringTag) { /*0x82 */ + printk("PCI: Bus VPD Buffer missing starting tag.\n"); + kfree(BusVpdPtr); + return; + } + /***************************************************************/ + /***************************************************************/ + iSeries_Parse_Vpd(BusVpdPtr,BusVpdLen, DevNode); + kfree(BusVpdPtr); +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_dma.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_dma.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_dma.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_dma.c Wed Dec 31 18:00:00 1969 @@ -1,1121 +0,0 @@ -/* - * iSeries_dma.c - * Copyright (C) 2001 Mike Corrigan IBM Corporation - * - * Dynamic DMA mapping support. - * - * Manages the TCE space assigned to this partition - * - * modeled from pci-dma.c - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*#define DEBUG_TCE 1 */ - -/*HACK HACK HACK */ - -u8 iSeries_Get_Bus( struct pci_dev * dv ) -{ - return 0; -} - -/* HACK HACK HACK */ - -unsigned long phb_tce_table_init( void * x ) -{ - return (unsigned long)x; -} - - -struct TceTable virtBusTceTable; /* Tce table for virtual bus */ - -struct TceTable * tceTables[256]; /* Tce tables for 256 busses - * Bus 255 is the virtual bus - * zero indicates no bus defined - */ - /* allocates a contiguous range of tces (power-of-2 size) */ -static long alloc_tce_range( struct TceTable *, - unsigned order ); - /* allocates a contiguous range of tces (power-of-2 size) - * assumes lock already held - */ -static long alloc_tce_range_nolock( struct TceTable *, - unsigned order ); - /* frees a contiguous range of tces (power-of-2 size) */ -static void free_tce_range( struct TceTable *, - long tcenum, - unsigned order ); - /* frees a contiguous rnage of tces (power-of-2 size) - * assumes lock already held - */ -static void free_tce_range_nolock( struct TceTable *, - long tcenum, - unsigned order ); - /* allocates a range of tces and sets them to the pages */ -static dma_addr_t get_tces( struct TceTable *, - unsigned order, - void *page, - unsigned numPages, - int tceType, - int direction ); -static void free_tces( struct TceTable *, - dma_addr_t tce, - unsigned order, - unsigned numPages ); -static long test_tce_range( struct TceTable *, - long tcenum, - unsigned order ); - -static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, - dma_addr_t dma_addr, unsigned long numTces ); - -static unsigned long num_tces_sg( struct scatterlist *sg, - int nents ); - -static dma_addr_t create_tces_sg( struct TceTable *tbl, - struct scatterlist *sg, - int nents, - unsigned numTces, - int tceType, - int direction ); - -static unsigned __inline__ count_leading_zeros32( unsigned long x ) -{ - unsigned lz; - asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); - return lz; -} - -static void __inline__ build_tce( struct TceTable * tbl, long tcenum, - unsigned long uaddr, int tceType, int direction ) -{ - u64 setTceRc; - union Tce tce; - - tce.wholeTce = 0; - tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; - /* If for virtual bus */ - if ( tceType == TCE_VB ) { - tce.tceBits.valid = 1; - tce.tceBits.allIo = 1; - if ( direction != PCI_DMA_TODEVICE ) - tce.tceBits.readWrite = 1; - } - /* If for PCI bus */ - else { - tce.tceBits.readWrite = 1; // Read allowed - if ( direction != PCI_DMA_TODEVICE ) - tce.tceBits.pciWrite = 1; - } - setTceRc = HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); - if ( setTceRc ) { - printk("build_tce: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", - setTceRc, (u64)tbl->index, (u64)tcenum, tce.wholeTce ); - } - -} - - - -/* Build a TceTable structure. This contains a multi-level bit map which - * is used to manage allocation of the tce space. - */ - -struct TceTable * build_tce_table( struct HvTceTableManagerCB * tceTableParms, - struct TceTable * tbl ) -{ - unsigned long bits, bytes, totalBytes; - unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; - unsigned i, k, m; - unsigned char * pos, * p, b; - - tbl->size = tceTableParms->size; - tbl->busNumber = tceTableParms->busNumber; - tbl->startOffset = tceTableParms->startOffset; - tbl->index = tceTableParms->index; - spin_lock_init( &(tbl->lock) ); - - tbl->mlbm.maxLevel = 0; - - /* Compute number of bits and bytes for each level of the - * multi-level bit map - */ - totalBytes = 0; - bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); - - for ( i=0; imlbm.level[i].map = pos; - tbl->mlbm.maxLevel = i; - - if ( numBits[i] & 1 ) { - p = pos + numBytes[i] - 1; - m = (( numBits[i] % 8) - 1) & 7; - *p = 0x80 >> m; -#ifdef DEBUG_TCE - printk("build_tce_table: level %d last bit %x\n", i, 0x80>>m ); -#endif - } - } - else - tbl->mlbm.level[i].map = 0; - pos += numBytes[i]; - /* see the comment up above on the totalBytes calculation - * for why we do this. */ - pos += ((numBytes[i] + 7) / 8) * 8; - - tbl->mlbm.level[i].numBits = numBits[i]; - tbl->mlbm.level[i].numBytes = numBytes[i]; - - } - - /* For the highest level, turn on all the bits */ - - i = tbl->mlbm.maxLevel; - p = tbl->mlbm.level[i].map; - m = numBits[i]; -#ifdef DEBUG_TCE - printk("build_tce_table: highest level (%d) has all bits set\n", i); -#endif - for (k=0; k= 8 ) { - /* handle full bytes */ - *p++ = 0xff; - m -= 8; - } - else { - /* handle the last partial byte */ - b = 0x80; - *p = 0; - while (m) { - *p |= b; - b >>= 1; - --m; - } - } - } - - return tbl; - -} - -static long alloc_tce_range( struct TceTable *tbl, unsigned order ) -{ - long retval; - unsigned long flags; - - /* Lock the tce allocation bitmap */ - spin_lock_irqsave( &(tbl->lock), flags ); - - /* Do the actual work */ - retval = alloc_tce_range_nolock( tbl, order ); - - /* Unlock the tce allocation bitmap */ - spin_unlock_irqrestore( &(tbl->lock), flags ); - - return retval; -} - -static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) -{ - unsigned long numBits, numBytes; - unsigned long i, bit, block, mask; - long tcenum; - u32 * map; - - /* If the order (power of 2 size) requested is larger than our - * biggest, indicate failure - */ - if ( order > tbl->mlbm.maxLevel ) { - printk("alloc_tce_range_nolock: invalid order requested, order = %d\n", order ); - return -1; - } - - numBits = tbl->mlbm.level[order].numBits; - numBytes = tbl->mlbm.level[order].numBytes; - map = (u32 *)(tbl->mlbm.level[order].map); - - /* Initialize return value to -1 (failure) */ - tcenum = -1; - - /* Loop through the bytes of the bitmap */ - for (i=0; i> bit); - *map &= mask; - /* compute the index into our tce table for - * the first tce in the block - */ -#ifdef DEBUG_TCE - printk("alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); -#endif - tcenum = block << order; - break; - } - ++map; - } - -#ifdef DEBUG_TCE - if ( tcenum == -1 ) { - printk("alloc_tce_range_nolock: no available blocks of order = %d\n", order ); - if ( order < tbl->mlbm.maxLevel ) - printk("alloc_tce_range_nolock: trying next bigger size\n" ); - else - printk("alloc_tce_range_nolock: maximum size reached...failing\n"); - } -#endif - - /* If no block of the requested size was found, try the next - * size bigger. If one of those is found, return the second - * half of the block to freespace and keep the first half - */ - if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { - tcenum = alloc_tce_range_nolock( tbl, order+1 ); - if ( tcenum != -1 ) { - free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); - - /* Do the actual work */ - free_tce_range_nolock( tbl, tcenum, order ); - - /* Unlock the tce allocation bitmap */ - spin_unlock_irqrestore( &(tbl->lock), flags ); - -} - -static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ) -{ - unsigned long block; - unsigned byte, bit, mask, b; - unsigned char * map, * bytep; - - if ( order > tbl->mlbm.maxLevel ) { - printk("free_tce_range: order too large, order = %d, tcenum = %ld\n", order, tcenum ); - return; - } - - block = tcenum >> order; - if ( tcenum != (block << order ) ) { - printk("free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); - return; - } - if ( block >= tbl->mlbm.level[order].numBits ) { - printk("free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); - return; - } -#ifdef DEBUG_TCE - if ( test_tce_range( tbl, tcenum, order ) ) { - printk("free_tce_range: freeing range not completely allocated.\n"); - printk("free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); - } -#endif - map = tbl->mlbm.level[order].map; - byte = block / 8; - bit = block % 8; - mask = 0x80 >> bit; - bytep = map + byte; -#ifdef DEBUG_TCE - printk("free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); - if ( *bytep & mask ) - printk("free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); -#endif - *bytep |= mask; - - /* If there is a higher level in the bit map than this we may be - * able to buddy up this block with its partner. - * If this is the highest level we can't buddy up - * If this level has an odd number of bits and - * we are freeing the last block we can't buddy up - */ - if ( ( order < tbl->mlbm.maxLevel ) && - ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || - ( block < tbl->mlbm.level[order].numBits-1 ) ) ) { - - /* See if we can buddy up the block we just freed */ - bit &= 6; /* get to the first of the buddy bits */ - mask = 0xc0 >> bit; /* build two bit mask */ - b = *bytep & mask; /* Get the two bits */ - if ( 0 == (b ^ mask) ) { /* If both bits are on */ - /* both of the buddy blocks are free we can combine them */ - *bytep ^= mask; /* turn off the two bits */ - block = ( byte * 8 ) + bit; /* block of first of buddies */ - tcenum = block << order; - /* free the buddied block */ -#ifdef DEBUG_TCE - printk("free_tce_range: buddying up block %ld and block %ld\n", block, block+1); -#endif - free_tce_range_nolock( tbl, tcenum, order+1 ); - } - } -} - -static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) -{ - unsigned long block; - unsigned byte, bit, mask, b; - long retval, retLeft, retRight; - unsigned char * map; - - map = tbl->mlbm.level[order].map; - block = tcenum >> order; - byte = block / 8; /* Byte within bitmap */ - bit = block % 8; /* Bit within byte */ - mask = 0x80 >> bit; - b = (*(map+byte) & mask ); /* 0 if block is allocated, else free */ - if ( b ) - retval = 1; /* 1 == block is free */ - else - retval = 0; /* 0 == block is allocated */ - /* Test bits at all levels below this to ensure that all agree */ - - if (order) { - retLeft = test_tce_range( tbl, tcenum, order-1 ); - retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); - if ( retLeft || retRight ) { - retval = 2; - } - } - - /* Test bits at all levels above this to ensure that all agree */ - - return retval; -} - -static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) -{ - long tcenum; - unsigned long uaddr; - unsigned i; - dma_addr_t retTce = NO_TCE; - - uaddr = (unsigned long)page & PAGE_MASK; - - /* Allocate a range of tces */ - tcenum = alloc_tce_range( tbl, order ); - if ( tcenum != -1 ) { - /* We got the tces we wanted */ - tcenum += tbl->startOffset; /* Offset into real TCE table */ - retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ - /* Setup a tce for each page */ - for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; - - tcenum = dma_addr >> PAGE_SHIFT; - tcenum -= tbl->startOffset; - - if ( tcenum > maxTcenum ) { - printk("free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", - tcenum, maxTcenum ); - printk("free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); - printk("free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); - printk("free_tces: size %lu\n", (unsigned long)tbl->size ); - printk("free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); - printk("free_tces: index %lu\n", (unsigned long)tbl->index ); - return; - } - - freeTce = tcenum; - - for (i=0; iindex, (u64)tcenum, tce.wholeTce ); - if ( setTceRc ) { - printk("free_tces: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", - setTceRc, (u64)tbl->index, (u64)tcenum, tce.wholeTce ); - } - - ++tcenum; - } - - free_tce_range( tbl, freeTce, order ); - -} - -void __init create_virtual_bus_tce_table(void) -{ - struct TceTable * t; - struct HvTceTableManagerCB virtBusTceTableParms; - u64 absParmsPtr; - - virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ - virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ - - absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); - HvCallXm_getTceTableParms( absParmsPtr ); - - t = build_tce_table( &virtBusTceTableParms, &virtBusTceTable ); - if ( t ) { - tceTables[255] = t; - printk("Virtual Bus TCE table built successfully.\n"); - printk(" TCE table size = %ld entries\n", - (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); - printk(" TCE table token = %d\n", - (unsigned)t->index ); - printk(" TCE table start entry = 0x%lx\n", - (unsigned long)t->startOffset ); - } - else - printk("Virtual Bus TCE table failed.\n"); -} - -void __init create_pci_bus_tce_table( unsigned busNumber ) -{ - struct TceTable * t; - struct TceTable * newTceTable; - struct HvTceTableManagerCB pciBusTceTableParms; - u64 absParmsPtr; - unsigned i; - - if ( busNumber > 254 ) { - printk("PCI Bus TCE table failed.\n"); - printk(" Invalid bus number %u\n", busNumber ); - return; - } - - newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); - - pciBusTceTableParms.busNumber = busNumber; - pciBusTceTableParms.virtualBusFlag = 0; - - absParmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); - HvCallXm_getTceTableParms( absParmsPtr ); - - /* Determine if the table identified by the index and startOffset - * returned by the hypervisor for this bus has already been created. - */ - - for ( i=0; i<255; ++i ) { - t = tceTables[i]; - if ( t ) { - if ( ( t->index == pciBusTceTableParms.index ) && - ( t->startOffset == pciBusTceTableParms.startOffset ) ) { - if ( t->size != pciBusTceTableParms.size ) - printk("PCI Bus %d Shares a TCE table with Bus %d, but sizes differ\n", busNumber, i ); - else - printk("PCI Bus %d Shares a TCE table with Bus %d\n", busNumber, i ); - tceTables[busNumber] = t; - break; - } - } - } - - if ( ! tceTables[busNumber] ) { - t = build_tce_table( &pciBusTceTableParms, newTceTable ); - if ( t ) { - tceTables[busNumber] = t; - printk("PCI Bus TCE table built successfully.\n"); - printk(" TCE table size = %ld entries\n", - (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); - printk(" TCE table token = %d\n", - (unsigned)t->index ); - printk(" TCE table start entry = 0x%lx\n", - (unsigned long)t->startOffset ); - } - else { - kfree( newTceTable ); - printk("PCI Bus TCE table failed.\n"); - } - } -} - - -/* Allocates a contiguous real buffer and creates TCEs over it. - * Returns the virtual address of the buffer and sets dma_handle - * to the dma address (tce) of the first page. - */ -void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, - dma_addr_t *dma_handle) -{ - struct TceTable * tbl; - void *ret = NULL; - unsigned order, nPages, bus; - dma_addr_t tce; - int tceType; - - size = PAGE_ALIGN(size); - order = get_order(size); - nPages = 1 << order; - - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) { - bus = 255; - tceType = TCE_VB; - } - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = ISERIES_GET_BUS( hwdev ); - tceType = TCE_PCI; -#else - BUG(); - return NULL; -#endif /* CONFIG_PCI */ - } - - tbl = tceTables[bus]; - if ( tbl ) { - /* Alloc enough pages (and possibly more) */ - ret = (void *)__get_free_pages( GFP_ATOMIC, order ); - if ( ret ) { - /* Page allocation succeeded */ - memset(ret, 0, nPages << PAGE_SHIFT); - /* Set up tces to cover the allocated range */ - tce = get_tces( tbl, order, ret, nPages, tceType, - PCI_DMA_BIDIRECTIONAL ); - if ( tce == NO_TCE ) { -/*#ifdef DEBUG_TCE */ - printk("pci_alloc_consistent: get_tces failed\n" ); -/*#endif */ - free_pages( (unsigned long)ret, order ); - ret = NULL; - } - else - { - *dma_handle = tce; - } - } -/*#ifdef DEBUG_TCE */ - else - printk("pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); -/*#endif*/ - } - - return ret; -} - -void pci_free_consistent(struct pci_dev *hwdev, size_t size, - void *vaddr, dma_addr_t dma_handle) -{ - struct TceTable * tbl; - unsigned order, nPages, bus; - - size = PAGE_ALIGN(size); - order = get_order(size); - nPages = 1 << order; - - if ( order > 10 ) - printk("pci_free_consistent: order=%d, size=%ld, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", - order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); - - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) - bus = 255; - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = ISERIES_GET_BUS( hwdev ); -#else - BUG(); - return; -#endif /* CONFIG_PCI */ - } - - if ( bus > 255 ) { - printk("pci_free_consistent: invalid bus # %d\n", bus ); - printk("pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); - } - - tbl = tceTables[bus]; - - if ( tbl ) { - free_tces( tbl, dma_handle, order, nPages ); - free_pages( (unsigned long)vaddr, order ); - } -} - -/* Creates TCEs for a user provided buffer. The user buffer must be - * contiguous real kernel storage (not vmalloc). The address of the buffer - * passed here is the kernel (virtual) address of the buffer. The buffer - * need not be page aligned, the dma_addr_t returned will point to the same - * byte within the page as vaddr. - */ -dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) -{ - struct TceTable * tbl; - dma_addr_t dma_handle; - unsigned long uaddr; - unsigned order, nPages, bus; - int tceType; - - if ( direction == PCI_DMA_NONE ) - BUG(); - - dma_handle = NO_TCE; - - uaddr = (unsigned long)vaddr; - nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); - order = get_order( nPages & PAGE_MASK ); - nPages >>= PAGE_SHIFT; - - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) { - bus = 255; - tceType = TCE_VB; - } - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = ISERIES_GET_BUS( hwdev ); - tceType = TCE_PCI; -#else - BUG(); - return NO_TCE; -#endif /* CONFIG_PCI */ - - } - - tbl = tceTables[bus]; - - if ( tbl ) { - dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, - direction ); - dma_handle |= ( uaddr & ~PAGE_MASK ); - } - - return dma_handle; -} - -void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) -{ - struct TceTable * tbl; - unsigned order, nPages, bus; - - if ( direction == PCI_DMA_NONE ) - BUG(); - - nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); - order = get_order( nPages & PAGE_MASK ); - nPages >>= PAGE_SHIFT; - - if ( order > 10 ) - printk("pci_unmap_single: order=%d, size=%ld, nPages=%d, dma_handle=%016lx\n", - order, size, nPages, (unsigned long)dma_handle ); - - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) - bus = 255; - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = ISERIES_GET_BUS( hwdev ); -#else - BUG(); - return; -#endif /* CONFIG_PCI */ - } - - if ( bus > 255 ) { - printk("pci_unmap_single: invalid bus # %d\n", bus ); - printk("pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); - } - - tbl = tceTables[bus]; - - if ( tbl ) - free_tces( tbl, dma_handle, order, nPages ); - -} - -/* Figure out how many TCEs are actually going to be required - * to map this scatterlist. This code is not optimal. It - * takes into account the case where entry n ends in the same - * page in which entry n+1 starts. It does not handle the - * general case of entry n ending in the same page in which - * entry m starts. - */ -static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) -{ - unsigned long nTces, numPages, startPage, endPage, prevEndPage; - unsigned i; - - prevEndPage = 0; - nTces = 0; - - for (i=0; iaddress >> PAGE_SHIFT; - endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; - numPages = endPage - startPage + 1; - /* Simple optimization: if the previous entry ended - * in the same page in which this entry starts - * then we can reduce the required pages by one. - * This matches assumptions in fill_scatterlist_sg and - * create_tces_sg - */ - if ( startPage == prevEndPage ) - --numPages; - nTces += numPages; - prevEndPage = endPage; - sg++; - } - return nTces; -} - -/* Fill in the dma data in the scatterlist - * return the number of dma sg entries created - */ -static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, - dma_addr_t dma_addr , unsigned long numTces) -{ - struct scatterlist *dma_sg; - u32 cur_start_dma; - unsigned long cur_len_dma, cur_end_virt, uaddr; - unsigned num_dma_ents; - - dma_sg = sg; - num_dma_ents = 1; - - /* Process the first sg entry */ - cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); - cur_len_dma = sg->length; - /* cur_end_virt holds the address of the byte immediately after the - * end of the current buffer. - */ - cur_end_virt = (unsigned long)sg->address + cur_len_dma; - /* Later code assumes that unused sg->dma_address and sg->dma_length - * fields will be zero. Other archs seem to assume that the user - * (device driver) guarantees that...I don't want to depend on that - */ - sg->dma_address = sg->dma_length = 0; - - /* Process the rest of the sg entries */ - while (--nents) { - ++sg; - /* Clear possibly unused fields. Note: sg >= dma_sg so - * this can't be clearing a field we've already set - */ - sg->dma_address = sg->dma_length = 0; - - /* Check if it is possible to make this next entry - * contiguous (in dma space) with the previous entry. - */ - - /* The entries can be contiguous in dma space if - * the previous entry ends immediately before the - * start of the current entry (in virtual space) - * or if the previous entry ends at a page boundary - * and the current entry starts at a page boundary. - */ - uaddr = (unsigned long)sg->address; - if ( ( uaddr != cur_end_virt ) && - ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || - ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { - /* This entry can not be contiguous in dma space. - * save the previous dma entry and start a new one - */ - dma_sg->dma_address = cur_start_dma; - dma_sg->dma_length = cur_len_dma; - - ++dma_sg; - ++num_dma_ents; - - cur_start_dma += cur_len_dma-1; - /* If the previous entry ends and this entry starts - * in the same page then they share a tce. In that - * case don't bump cur_start_dma to the next page - * in dma space. This matches assumptions made in - * num_tces_sg and create_tces_sg. - */ - if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) - cur_start_dma &= PAGE_MASK; - else - cur_start_dma = PAGE_ALIGN(cur_start_dma+1); - cur_start_dma += ( uaddr & (~PAGE_MASK) ); - cur_len_dma = 0; - } - /* Accumulate the length of this entry for the next - * dma entry - */ - cur_len_dma += sg->length; - cur_end_virt = uaddr + sg->length; - } - /* Fill in the last dma entry */ - dma_sg->dma_address = cur_start_dma; - dma_sg->dma_length = cur_len_dma; - - if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) - { - printk("fill_scatterlist_sg: numTces %ld, used tces %d\n", - numTces, - (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); - } - - - return num_dma_ents; -} - -/* Call the hypervisor to create the TCE entries. - * return the number of TCEs created - */ -static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, - int nents, unsigned numTces, int tceType, int direction ) -{ - unsigned order, i, j; - unsigned long startPage, endPage, prevEndPage, numPages, uaddr; - long tcenum, starttcenum; - dma_addr_t dmaAddr; - - dmaAddr = NO_TCE; - - order = get_order( numTces << PAGE_SHIFT ); - /* allocate a block of tces */ - tcenum = alloc_tce_range( tbl, order ); - if ( tcenum != -1 ) { - tcenum += tbl->startOffset; - starttcenum = tcenum; - dmaAddr = tcenum << PAGE_SHIFT; - prevEndPage = 0; - for (j=0; jaddress >> PAGE_SHIFT; - endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; - numPages = endPage - startPage + 1; - - uaddr = (unsigned long)sg->address; - - /* If the previous entry ended in the same page that - * the current page starts then they share that - * tce and we reduce the number of tces we need - * by one. This matches assumptions made in - * num_tces_sg and fill_scatterlist_sg - */ - if ( startPage == prevEndPage ) { - --numPages; - uaddr += PAGE_SIZE; - } - - for (i=0; idma_address = pci_map_single( hwdev, sg->address, - sg->length, direction ); - sg->dma_length = sg->length; - return 1; - } - - if ( direction == PCI_DMA_NONE ) - BUG(); - - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) { - bus = 255; - tceType = TCE_VB; - } - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = ISERIES_GET_BUS( hwdev ); - tceType = TCE_PCI; -#else - BUG(); - return 0; -#endif /* CONFIG_PCI */ - } - - tbl = tceTables[bus]; - - if ( tbl ) { - /* Compute the number of tces required */ - numTces = num_tces_sg( sg, nents ); - /* Create the tces and get the dma address */ - dma_handle = create_tces_sg( tbl, sg, nents, numTces, - tceType, direction ); - - /* Fill in the dma scatterlist */ - num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); - } - - return num_dma; -} - -void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) -{ - struct TceTable * tbl; - unsigned order, numTces, bus, i; - dma_addr_t dma_end_page, dma_start_page; - - if ( direction == PCI_DMA_NONE ) - BUG(); - - dma_start_page = sg->dma_address & PAGE_MASK; - for ( i=nelms; i>0; --i ) { - unsigned k = i - 1; - if ( sg[k].dma_length ) { - dma_end_page = ( sg[k].dma_address + - sg[k].dma_length - 1 ) & PAGE_MASK; - break; - } - } - - numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; - order = get_order( numTces << PAGE_SHIFT ); - - if ( order > 10 ) - printk("pci_unmap_sg: order=%d, numTces=%d, nelms=%d, dma_start_page=%016lx, dma_end_page=%016lx\n", - order, numTces, nelms, (unsigned long)dma_start_page, (unsigned long)dma_end_page ); - - - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) - bus = 255; - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = ISERIES_GET_BUS( hwdev ); -#else - BUG(); - return; -#endif /* CONFIG_PCI */ - } - - if ( bus > 255 ) { - printk("pci_unmap_sg: invalid bus # %d\n", bus ); - printk("pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); - } - - - tbl = tceTables[bus]; - - if ( tbl ) - free_tces( tbl, dma_start_page, order, numTces ); - -} - diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_irq.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_irq.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_irq.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_irq.c Wed Sep 19 20:33:58 2001 @@ -0,0 +1,251 @@ +/************************************************************************/ +/* This module supports the iSeries PCI bus interrupt handling */ +/* Copyright (C) 20yy */ +/* */ +/* 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 */ +/************************************************************************/ +/* Change Activity: */ +/* Created, December 13, 2000 by Wayne Holm */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +hw_irq_controller iSeries_IRQ_handler = { + "iSeries irq controller", + iSeries_startup_IRQ, /* startup */ + iSeries_shutdown_IRQ, /* shutdown */ + iSeries_enable_IRQ, /* enable */ + iSeries_disable_IRQ, /* disable */ + NULL, /* ack */ + iSeries_end_IRQ, /* end */ + NULL /* set_affinity */ +}; + + +struct iSeries_irqEntry { + u32 dsa; + struct iSeries_irqEntry* next; +}; + +struct iSeries_irqAnchor { + u8 valid : 1; + u8 reserved : 7; + u16 entryCount; + struct iSeries_irqEntry* head; +}; + +struct iSeries_irqAnchor iSeries_irqMap[NR_IRQS]; + +void iSeries_init_irqMap(int irq); + +/* This is called by init_IRQ. set in ppc_md.init_IRQ by iSeries_setup.c */ +void __init iSeries_init_IRQ(void) { + int i; + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].handler = &iSeries_IRQ_handler; + irq_desc[i].status = 0; + irq_desc[i].status |= IRQ_DISABLED; + irq_desc[i].depth = 1; + iSeries_init_irqMap(i); + } + /* Register PCI event handler and open an event path */ + PPCDBG(PPCDBG_BUSWALK,"Register PCI event handler and open an event path\n"); + XmPciLpEvent_init(); + return; +} + +/********************************************************************** + * Called by iSeries_init_IRQ + * Prevent IRQs 0 and 255 from being used. IRQ 0 appears in + * uninitialized devices. IRQ 255 appears in the PCI interrupt + * line register if a PCI error occurs, + *********************************************************************/ +void __init iSeries_init_irqMap(int irq) { + iSeries_irqMap[irq].valid = (irq == 0 || irq == 255)? 0 : 1; + iSeries_irqMap[irq].entryCount = 0; + iSeries_irqMap[irq].head = NULL; +} + +/* This is called out of iSeries_scan_slot to allocate an IRQ for an EADS slot */ +/* It calculates the irq value for the slot. */ +int __init iSeries_allocate_IRQ(HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) { + u8 idsel = (deviceId >> 4); + u8 function = deviceId & 0x0F; + int irq = ((((busNumber-1)*16 + (idsel-1)*8 + function)*9/8) % 254) + 1; + return irq; +} + +/* This is called out of iSeries_scan_slot to assign the EADS slot to its IRQ number */ +int __init iSeries_assign_IRQ(int irq, HvBusNumber busNumber, HvSubBusNumber subBusNumber, HvAgentId deviceId) { + int rc; + u32 dsa = (busNumber << 16) | (subBusNumber << 8) | deviceId; + struct iSeries_irqEntry* newEntry; + unsigned long flags; + + if (irq < 0 || irq >= NR_IRQS) { + return -1; + } + newEntry = kmalloc(sizeof(*newEntry), GFP_KERNEL); + if (newEntry == NULL) { + return -ENOMEM; + } + newEntry->dsa = dsa; + newEntry->next = NULL; + /******************************************************************** + * Probably not necessary to lock the irq since allocation is only + * done during buswalk, but it should not hurt anything except a + * little performance to be smp safe. + *******************************************************************/ + spin_lock_irqsave(&irq_desc[irq].lock, flags); + + if (iSeries_irqMap[irq].valid) { + /* Push the new element onto the irq stack */ + newEntry->next = iSeries_irqMap[irq].head; + iSeries_irqMap[irq].head = newEntry; + ++iSeries_irqMap[irq].entryCount; + rc = 0; + PPCDBG(PPCDBG_BUSWALK,"iSeries_assign_IRQ 0x%04X.%02X.%02X = 0x%04X\n",busNumber, subBusNumber, deviceId, irq); + } + else { + printk("PCI: Something is wrong with the iSeries_irqMap. \n"); + kfree(newEntry); + rc = -1; + } + spin_unlock_irqrestore(&irq_desc[irq].lock, flags); + return rc; +} + + +/* This is called by iSeries_activate_IRQs */ +unsigned int iSeries_startup_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, function, mask; + for(entry=iSeries_irqMap[irq].head; entry!=NULL; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + function = deviceId & 0x0F; + /* Link the IRQ number to the bridge */ + HvCallXm_connectBusUnit(bus, subBus, deviceId, irq); + /* Unmask bridge interrupts in the FISR */ + mask = 0x01010000 << function; + HvCallPci_unmaskFisr(bus, subBus, deviceId, mask); + PPCDBG(PPCDBG_BUSWALK,"iSeries_activate_IRQ 0x%02X.%02X.%02X Irq:0x%02X\n",bus,subBus,deviceId,irq); + } + return 0; +} + +/* This is called out of iSeries_fixup to activate interrupt + * generation for usable slots */ +void __init iSeries_activate_IRQs() { + int irq; + unsigned long flags; + for (irq=0; irq < NR_IRQS; irq++) { + spin_lock_irqsave(&irq_desc[irq].lock, flags); + irq_desc[irq].handler->startup(irq); + spin_unlock_irqrestore(&irq_desc[irq].lock, flags); + } +} + +/* this is not called anywhere currently */ +void iSeries_shutdown_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, function, mask; + + /* irq should be locked by the caller */ + + for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + function = deviceId & 0x0F; + /* Invalidate the IRQ number in the bridge */ + HvCallXm_connectBusUnit(bus, subBus, deviceId, 0); + /* Mask bridge interrupts in the FISR */ + mask = 0x01010000 << function; + HvCallPci_maskFisr(bus, subBus, deviceId, mask); + } + +} + +/*********************************************************** + * This will be called by device drivers (via disable_IRQ) + * to disable INTA in the bridge interrupt status register. + ***********************************************************/ +void iSeries_disable_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, mask; + + /* The IRQ has already been locked by the caller */ + + for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + /* Mask secondary INTA */ + mask = 0x80000000; + HvCallPci_maskInterrupts(bus, subBus, deviceId, mask); + PPCDBG(PPCDBG_BUSWALK,"iSeries_disable_IRQ 0x%02X.%02X.%02X 0x%04X\n",bus,subBus,deviceId,irq); + } +} + +/*********************************************************** + * This will be called by device drivers (via enable_IRQ) + * to enable INTA in the bridge interrupt status register. + ***********************************************************/ +void iSeries_enable_IRQ(unsigned int irq) { + struct iSeries_irqEntry* entry; + u32 bus, subBus, deviceId, mask; + + /* The IRQ has already been locked by the caller */ + for(entry=iSeries_irqMap[irq].head; entry; entry=entry->next) { + bus = (entry->dsa >> 16) & 0xFFFF; + subBus = (entry->dsa >> 8) & 0xFF; + deviceId = entry->dsa & 0xFF; + /* Unmask secondary INTA */ + mask = 0x80000000; + HvCallPci_unmaskInterrupts(bus, subBus, deviceId, mask); + PPCDBG(PPCDBG_BUSWALK,"iSeries_enable_IRQ 0x%02X.%02X.%02X 0x%04X\n",bus,subBus,deviceId,irq); + } +} + +/* Need to define this so ppc_irq_dispatch_handler will NOT call + enable_IRQ at the end of interrupt handling. However, this + does nothing because there is not enough information provided + to do the EOI HvCall. This is done by XmPciLpEvent.c */ +void iSeries_end_IRQ(unsigned int irq) { +} + diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_pci.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_pci.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_pci.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_pci.c Tue Oct 2 17:06:49 2001 @@ -27,62 +27,63 @@ #include #include #include -#include #include #include -#include #include #include -//#include #include -#include #include #include #include -//#include -#include - #include -#include -#include #include +#include +#include #include +#include #include #include +#include +#include "iSeries_IoMmTable.h" #include "pci.h" extern struct pci_controller* hose_head; extern struct pci_controller** hose_tail; +extern int global_phb_number; +extern int panic_timeout; + extern struct Naca *naca; extern struct device_node *allnodes; extern unsigned long phb_tce_table_init(struct pci_controller *phb); +extern unsigned long iSeries_Base_Io_Memory; extern struct pci_ops iSeries_pci_ops; extern struct flightRecorder* PciFr; -int PciTraceFlag = 0; - -struct pci_dev; - -struct i_device_node { - struct list_head iDevice_Chain; - char* Name; - char* Type; - struct pci_dev* PciDev; - int BusNumber; - int SubBus; - int ReturnCode; - int DevFn; -}; +/******************************************************************* + * Counters and control flags. + *******************************************************************/ +extern long Pci_Io_Read_Count; +extern long Pci_Io_Write_Count; +extern long Pci_Cfg_Read_Count; +extern long Pci_Cfg_Write_Count; +extern long Pci_Error_Count; + +extern int Pci_Retry_Max; +extern int Pci_Error_Flag; +extern int Pci_Trace_Flag; -LIST_HEAD(iDevice_List); +extern void iSeries_MmIoTest(void); /******************************************************************* * Forward declares of prototypes. *******************************************************************/ +struct iSeries_Device_Node* find_Device_Node(struct pci_dev* PciDev); +struct iSeries_Device_Node* get_Device_Node(struct pci_dev* PciDev); + unsigned long find_and_init_phbs(void); void fixup_resources(struct pci_dev *dev); void iSeries_pcibios_fixup(void); @@ -91,58 +92,98 @@ void iSeries_Scan_PHBs_Slots(struct pci_controller* Phb); void iSeries_Scan_EADs_Bridge(HvBusNumber Bus, HvSubBusNumber SubBus, int IdSel); int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, HvSubBusNumber SubBus, int MaxAgents); +void list_device_nodes(void); + +struct pci_dev; + +LIST_HEAD(Global_Device_List); + +int DeviceCount = 0; /********************************************************************************** - * - * + * Dump the iSeries Temp Device Node + *<4>buswalk [swapper : - DeviceNode: 0xC000000000634300 + *<4>00. Device Node = 0xC000000000634300 + *<4> - PciDev = 0x0000000000000000 + *<4> - tDevice = 0x 17:01.00 0x1022 00 + *<4> 4. Device Node = 0xC000000000634480 + *<4> - PciDev = 0x0000000000000000 + *<4> - Device = 0x 18:38.16 Irq:0xA7 Vendor:0x1014 Flags:0x00 + *<4> - Devfn = 0xB0: 22.18 **********************************************************************************/ -#define ISERIES_PCI_READ_OP(size, call, type) \ -/***********************************************************************************/ \ -int iSeries_pci_read_config_##size(struct pci_dev* PciDev, int PciOffset, type ReadValue) { \ - struct i_device_node* DeviceNode = PciDev->sysdata; \ - if(DeviceNode->BusNumber == 0xFF) DeviceNode->ReturnCode = 0x301; return DeviceNode->ReturnCode; \ - DeviceNode->ReturnCode = HvCallPci_config##call(DeviceNode->BusNumber, DeviceNode->SubBus, \ - DeviceNode->DevFn, PciOffset, ReadValue); \ - if(DeviceNode->ReturnCode != 0 ) { \ - PCIFR("RC##size: %02X,%02X,%02X,%04X Rtn: %04X", \ - DeviceNode->BusNumber, DeviceNode->SubBus, DeviceNode->DevFn, PciOffset,DeviceNode->ReturnCode); \ - } \ - return DeviceNode->ReturnCode; \ -} -/***********************************************************************************/ \ -#define ISERIES_PCI_WRITE_OP(size, call, type) \ -/***********************************************************************************/ \ -int iSeries_pci_write_config_##size(struct pci_dev* PciDev, int PciOffset, type WriteValue) { \ - struct i_device_node* DeviceNode = PciDev->sysdata; \ - DeviceNode->ReturnCode = HvCallPci_config##call(DeviceNode->BusNumber, DeviceNode->SubBus, \ - DeviceNode->DevFn, PciOffset, WriteValue); \ - if(DeviceNode->ReturnCode != 0 ) { \ - PCIFR("RC##size: %02X,%02X,%02X,%04X Rtn: %04X", \ - DeviceNode->BusNumber, DeviceNode->SubBus, DeviceNode->DevFn, PciOffset,DeviceNode->ReturnCode); \ - } \ - return DeviceNode->ReturnCode; \ -} - - ISERIES_PCI_READ_OP( byte, Load8, u8*) - ISERIES_PCI_READ_OP( word, Load16, u16*) - ISERIES_PCI_READ_OP( dword,Load32, u32*) - ISERIES_PCI_WRITE_OP(byte, Store8, u8) - ISERIES_PCI_WRITE_OP(word, Store16,u16) - ISERIES_PCI_WRITE_OP(dword,Store32,u32) - -/************************************************************************/ -/* Branch Table */ -/************************************************************************/ -struct pci_ops iSeries_pci_ops = { - iSeries_pci_read_config_byte, - iSeries_pci_read_config_word, - iSeries_pci_read_config_dword, - iSeries_pci_write_config_byte, - iSeries_pci_write_config_word, - iSeries_pci_write_config_dword -}; +void dumpDevice_Node(struct iSeries_Device_Node* DeviceNode) { + udbg_printf("Device Node = 0x%016LX\n",DeviceNode); + udbg_printf(" - PciDev = 0x%016LX\n",DeviceNode->PciDev); + udbg_printf(" - Device = 0x%4X:%02X.%02X (0x%02X)\n", + DeviceNode->BusNumber, + DeviceNode->SubBus, + DeviceNode->AgentId, + DeviceNode->DevFn); + udbg_printf(" = Irq:0x%02X Vendor:0x%04X Flags:0x%02X\n", + DeviceNode->Irq, + DeviceNode->Vendor, + DeviceNode->Flags ); + udbg_printf(" - Location = %s\n",DeviceNode->CardLocation); +} +/********************************************************************************** + * Walk down the device node chain + **********************************************************************************/ +void list_device_nodes(void) { + struct list_head* Device_Node_Ptr = Global_Device_List.next; + while(Device_Node_Ptr != &Global_Device_List) { + dumpDevice_Node( (struct iSeries_Device_Node*)Device_Node_Ptr ); + Device_Node_Ptr = Device_Node_Ptr->next; + } +} +/*********************************************************************** + * build_device_node(u16 Bus, int SubBus, u8 DevFn) + * + ***********************************************************************/ +struct iSeries_Device_Node* build_device_node(HvBusNumber Bus, HvSubBusNumber SubBus, int AgentId, int Func) { + struct iSeries_Device_Node* DeviceNode; + + PPCDBG(PPCDBG_BUSWALK,"- "__FUNCTION__" 0x%02X.%02X.%02X Func: %02X\n",Bus,SubBus,AgentId, Func); + + + DeviceNode = kmalloc(sizeof(struct iSeries_Device_Node), GFP_KERNEL); + if(DeviceNode == NULL) return NULL; + + memset(DeviceNode,0,sizeof(struct iSeries_Device_Node) ); + list_add_tail(&DeviceNode->Device_List,&Global_Device_List); + DeviceNode->DsaAddr = ((u64)Bus<<48)+((u64)SubBus<<40)+((u64)0x10<<32); + DeviceNode->BusNumber = Bus; + DeviceNode->SubBus = SubBus; + DeviceNode->AgentId = AgentId; + DeviceNode->DevFn = (0x80 | ((AgentId & 0x07) << 3) | (Func & 0x03)); + DeviceNode->IoRetry = 0; + iSeries_Get_Location_Code(DeviceNode); + return DeviceNode; +} +/**************************************************************************** +* +* Allocate pci_controller(phb) initialized common variables. +* +*****************************************************************************/ +struct pci_controller* pci_alloc_pci_controllerX(char *model, enum phb_types controller_type) +{ + struct pci_controller *hose; + hose = (struct pci_controller*)kmalloc(sizeof(struct pci_controller), GFP_KERNEL); + if(hose == NULL) return NULL; + + memset(hose, 0, sizeof(struct pci_controller)); + if(strlen(model) < 8) strcpy(hose->what,model); + else memcpy(hose->what,model,7); + hose->type = controller_type; + hose->global_number = global_phb_number; + global_phb_number++; + + *hose_tail = hose; + hose_tail = &hose->next; + return hose; +} + /**************************************************************************** * * unsigned int __init find_and_init_phbs(void) @@ -154,57 +195,33 @@ * owned or fully owned by this guest partition. ****************************************************************************/ unsigned long __init find_and_init_phbs(void) { - struct pci_controller* hose; - HvBusNumber BusNumber; - int LxBusNumber = 0; /* Linux Bus number for grins */ + struct pci_controller* phb; + HvBusNumber BusNumber; PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); - /* Check to make sure the device probing will work on this iSeries Release. */ - if(hvReleaseData.xVrmIndex !=3) { - printk("PCI: iSeries Lpar and Linux native PCI I/O code is incompatible.\n"); - printk("PCI: A newer version of the Linux kernel is need for this iSeries release.\n"); - return -1; - } - /* Check all possible buses. */ - for (BusNumber = 0; BusNumber < 256; BusNumber++) { + /* Check all possible buses. */ + for (BusNumber = 0; BusNumber < 256; BusNumber++) { int RtnCode = HvCallXm_testBus(BusNumber); if (RtnCode == 0) { - PPCDBG(PPCDBG_BUSWALK, "Create iSeries PHB controller: %04X\n",BusNumber); - PCIFR("Create iSeries PHB controller: %04X",BusNumber); - - PPCDBG(PPCDBG_BUSWALK, "PCI: Allocate pci_controller for PBH HV\n"); - hose = (struct pci_controller *)kmalloc(sizeof(struct pci_controller),GFP_KERNEL); - if(hose == NULL) { + phb = pci_alloc_pci_controllerX("PHB HV", phb_type_hypervisor); + if(phb == NULL) { printk("PCI: Allocate pci_controller failed.\n"); - PCIFR( "PCI: Allocate pci_controller failed.\n"); - return -1; + PCIFR( "Allocate pci_controller failed."); + return -1; } - memset(hose, 0, sizeof(struct pci_controller)); - *hose_tail = hose; - hose_tail = &hose->next; - strcpy(hose->what,"PHB HV"); - hose->type = phb_type_hypervisor; - hose->global_number = BusNumber; - *hose_tail = hose; - hose_tail = &hose->next; - - hose->local_number = BusNumber; /* Stuff HV bus number away. */ - hose->first_busno = LxBusNumber;/* This just for debug. pcibios will */ - hose->last_busno = 0xff; /* assign the bus numbers later. */ - hose->ops = &iSeries_pci_ops; /* Cnfg Reg Ops routines. */ - LxBusNumber += 1; /* Keep track for debug. */ - - /*********************/ - /* TCE Table for Bus */ - /*********************/ - //create_pci_bus_tce_table(BusNumber); - - /************************************************************************* - * Find and connect the devices. - *************************************************************************/ - iSeries_Scan_PHBs_Slots(hose); + phb->pci_mem_offset = phb->local_number = BusNumber; + phb->first_busno = BusNumber; + phb->last_busno = BusNumber; + phb->ops = &iSeries_pci_ops; + + PPCDBG(PPCDBG_BUSWALK, "Create iSeries pci_controller(%p), Bus: %04X\n",phb,BusNumber); + PCIFR("Create iSeries PHB controller: %04X",BusNumber); + /***************************************************/ + /* Find and connect the devices. */ + /***************************************************/ + iSeries_Scan_PHBs_Slots(phb); } } return 0; @@ -214,29 +231,87 @@ * * Chance to initialize and structures or variable before PCI Bus walk. * + *<4>buswalk [swapper : iSeries_pcibios_init Entry. + *<4>buswalk [swapper : IoMmTable Initialized 0xC00000000034BD30 + *<4>buswalk [swapper : find_and_init_phbs Entry + *<4>buswalk [swapper : Create iSeries pci_controller:(0xC00000001F5C7000), Bus 0x0017 + *<4>buswalk [swapper : Connect EADs: 0x17.00.12 = 0x00 + *<4>buswalk [swapper : iSeries_assign_IRQ 0x0017.00.12 = 0x0091 + *<4>buswalk [swapper : - allocate and assign IRQ 0x17.00.12 = 0x91 + *<4>buswalk [swapper : - FoundDevice: 0x17.28.10 = 0x12AE + *<4>buswalk [swapper : - build_device_node 0x17.28.12 + *<4>buswalk [swapper : iSeries_pcibios_init Exit. ***********************************************************************/ void iSeries_pcibios_init(void) { + struct pci_controller *phb; PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry.\n"); - /* find_and_init_phbs(); comment out until debugged. */ + + iSeries_IoMmTable_Initialize(); + + find_and_init_phbs(); + + /* Create the TCE Tables */ + phb = hose_head; + while(phb != NULL) { + create_pci_bus_tce_table(phb->local_number); + phb = phb->next; + } + pci_assign_all_busses = 0; PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit.\n"); } /*********************************************************************** * iSeries_pcibios_fixup(void) - * - * - * ***********************************************************************/ void __init iSeries_pcibios_fixup(void) { - struct pci_dev *dev; + struct pci_dev* PciDev; + struct iSeries_Device_Node* DeviceNode; + char Buffer[256]; + int DeviceCount = 0; + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry.\n"); - return; /* Just return until debugged. */ - pci_assign_all_busses = 0; + /******************************************************/ + /* Fix up at the device node and pci_dev relationship */ + /******************************************************/ + pci_for_each_dev(PciDev) { + DeviceNode = find_Device_Node(PciDev); + if(DeviceNode != NULL) { + ++DeviceCount; + PciDev->sysdata = (void*)DeviceNode; + DeviceNode->PciDev = PciDev; + + PPCDBG(PPCDBG_BUSWALK,"PciDev 0x%p <==> DevNode 0x%p\n",PciDev,DeviceNode ); + + iSeries_allocateDeviceBars(PciDev); + + PPCDBGCALL(PPCDBG_BUSWALK,dumpPci_Dev(PciDev) ); - pci_for_each_dev(dev) { - PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(dev) ); + iSeries_Device_Information(PciDev,Buffer, sizeof(Buffer) ); + printk("%d. %s\n",DeviceCount,Buffer); + + } else { + printk("PCI: Device Tree not found for 0x%016lX\n",(unsigned long)PciDev); + } } + iSeries_IoMmTable_Status(); + + iSeries_activate_IRQs(); + + // This is test code. + // mf_displaySrc(0xC9000100); + // Pci_CfgIoTest(); + // mf_displaySrc(0xC9000500); + // Pci_MMIoTest(); + // mf_displaySrc(0xC9000999); +} +/*********************************************************************** + * iSeries_pcibios_fixup_bus(int Bus) + * + ***********************************************************************/ +void iSeries_pcibios_fixup_bus(struct pci_bus* PciBus) { + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__"(0x%04X) Entry.\n",PciBus->number); + } /*********************************************************************** * find_floppy(void) @@ -247,8 +322,8 @@ * Note: On iSeries there will only be a virtual diskette. ***********************************************************************/ struct pci_dev* -find_floppy() { - PPCDBG(PPCDBG_BUSWALK,"\tFind Floppy pci_dev.. None on iSeries.\n"); +find_floppy(void) { + PPCDBG(PPCDBG_BUSWALK,"- Find Floppy pci_dev.. None on iSeries.\n"); return NULL; } @@ -256,57 +331,14 @@ /*********************************************************************** * fixup_resources(struct pci_dev *dev) * - * - * - * ***********************************************************************/ -void -fixup_resources(struct pci_dev *dev) { - struct pci_controller* phb = (struct pci_controller *)dev->sysdata; - - PPCDBG(PPCDBG_BUSWALK, "fixup_resources:\n"); - PPCDBG(PPCDBG_BUSWALK, "\tphb = 0x%016LX\n", phb); - PPCDBG(PPCDBG_BUSWALK, "\tphb->local_number = 0x%004X \n", phb->local_number); +void fixup_resources(struct pci_dev *PciDev) { + PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" PciDev %p\n",PciDev); } -/*********************************************************************** - * build_device_node(u16 Bus, int SubBus, u8 DevFn) - * - * - * char* loc-code - * int vendor-id - * int device-id.. - * int BusNumber - * int SubBus - * int ReturnCode - * - * int regs - * int interrupts - * char* loc-code - ***********************************************************************/ -void -build_device_node(HvBusNumber BusNumber, HvSubBusNumber SubBus, u8 DevFn) {\ - struct i_device_node* iDevice = kmalloc(sizeof(struct i_device_node), GFP_KERNEL); - list_add_tail(&iDevice->iDevice_Chain,&iDevice_List); - - - -} - -void list_device_nodes(void) { - struct list_head* iDevice_Node_Ptr = iDevice_List.next; - int index = 1; - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); - while(iDevice_Node_Ptr != &iDevice_List) { - iDevice_Node_Ptr = iDevice_Node_Ptr->next; - ++index; - } -} - - /******************************************************************************** -* Loop through each node function to find usable bridges. +* Loop through each node function to find usable EADs bridges. *********************************************************************************/ void iSeries_Scan_PHBs_Slots(struct pci_controller* Phb) { struct HvCallPci_DeviceInfo* DevInfo; @@ -316,8 +348,6 @@ int IdSel = 1; int MaxAgents = 8; - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); - DevInfo = (struct HvCallPci_DeviceInfo*)kmalloc(sizeof(struct HvCallPci_DeviceInfo), GFP_KERNEL); if(DevInfo == NULL) return; @@ -327,18 +357,15 @@ for (IdSel=1; IdSel < MaxAgents; ++IdSel) { HvRc = HvCallPci_getDeviceInfo(Bus, SubBus, IdSel,REALADDR(DevInfo), sizeof(struct HvCallPci_DeviceInfo)); if (HvRc == 0) { - PPCDBG(PPCDBG_BUSWALK,"\tFound Device 0x%02X\n",DevInfo->deviceType); if(DevInfo->deviceType == HvCallPci_NodeDevice) { - PPCDBG(PPCDBG_BUSWALK,"\tFound EADs Bridge(NodeDevice)\n"); iSeries_Scan_EADs_Bridge(Bus, SubBus, IdSel); } else { - printk("PCI: Invalid System Configuration. \n"); + printk("PCI: Invalid System Configuration(0x%02X.\n",DevInfo->deviceType); } } } kfree(DevInfo); - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit\n"); } @@ -351,8 +378,6 @@ int Function; int HvRc; - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); - BridgeInfo = (struct HvCallPci_BridgeInfo*)kmalloc(sizeof(struct HvCallPci_BridgeInfo), GFP_KERNEL); if(BridgeInfo == NULL) return; @@ -363,41 +388,46 @@ AgentId = ISERIES_PCI_AGENTID(IdSel, Function); HvRc = HvCallXm_connectBusUnit(Bus, SubBus, AgentId, 0); if (HvRc == 0) { + /* Connect EADs: 0x18.00.12 = 0x00 */ PPCDBG(PPCDBG_BUSWALK,"Connect EADs: 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId); HvRc = HvCallPci_getBusUnitInfo(Bus, SubBus, AgentId, REALADDR(BridgeInfo), sizeof(struct HvCallPci_BridgeInfo)); if (HvRc == 0) { - PPCDBG(PPCDBG_BUSWALK,"\tBridgeInfo..: 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId, - BridgeInfo->busUnitInfo.deviceType); - if (BridgeInfo->busUnitInfo.deviceType == HvCallPci_BridgeDevice) { - PPCDBG(PPCDBG_BUSWALK,"\tScan_Bridge_Slot...: 0x%02X.%02X.%02X\n",Bus, SubBus, AgentId); - iSeries_Scan_Bridge_Slot(Bus, BridgeInfo->subBusNumber,BridgeInfo->maxAgents); + /* Scan_Bridge_Slot...: 0x18.00.12 */ + iSeries_Scan_Bridge_Slot(Bus,BridgeInfo->subBusNumber,BridgeInfo->maxAgents); } + else { + printk("PCI: Invalid Bridge Configuration(0x%02X)\n",BridgeInfo->busUnitInfo.deviceType); + } } } } kfree(BridgeInfo); - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit\n"); } /******************************************************************************** * * This assumes that the node slot is always on the primary bus! +* *********************************************************************************/ int iSeries_Scan_Bridge_Slot(HvBusNumber Bus, HvSubBusNumber SubBus, int MaxAgents) { + struct iSeries_Device_Node* DeviceNode; u16 VendorId = 0; int HvRc = 0; int Irq = 0; int IdSel = ISERIES_GET_DEVICE_FROM_SUBBUS(SubBus); int Function = ISERIES_GET_FUNCTION_FROM_SUBBUS(SubBus); HvAgentId AgentId = ISERIES_PCI_AGENTID(IdSel, Function); - int ValidSlots = 0; + HvAgentId EADsIdSel = ISERIES_PCI_AGENTID(IdSel, Function); + int FirstSlotId= 0; - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Entry\n"); - + /**********************************************************/ + /* iSeries_allocate_IRQ.: 0x18.00.12(0xA3) */ + /**********************************************************/ Irq = iSeries_allocate_IRQ(Bus, 0, AgentId); - PPCDBG(PPCDBG_BUSWALK,"\tiSeries_allocate_IRQ.: 0x%02X.%02X.%02X(0x%02X)\n", Bus, SubBus, AgentId, Irq); + iSeries_assign_IRQ(Irq, Bus, 0, AgentId); + PPCDBG(PPCDBG_BUSWALK,"- allocate and assign IRQ 0x%02X.%02X.%02X = 0x%02X\n",Bus, 0, AgentId, Irq ); /**************************************************************************** * Connect all functions of any device found. @@ -405,27 +435,465 @@ for (IdSel = 1; IdSel <= MaxAgents; ++IdSel) { for (Function = 0; Function < 8; ++Function) { AgentId = ISERIES_PCI_AGENTID(IdSel, Function); - HvRc = HvCallXm_connectBusUnit(Bus, SubBus, AgentId, Irq); - if (HvRc == 0) { - PPCDBG(PPCDBG_BUSWALK,"\tConnect....: 0x%02X.%02X.%02X Irq: 0x%02X\n",Bus, SubBus, AgentId, Irq); - HvRc = HvCallPci_configLoad16(Bus, SubBus, AgentId, PCI_VENDOR_ID, &VendorId); - if (HvRc == 0) { - PPCDBG(PPCDBG_BUSWALK,"\tVendorId...: 0x%02X.%02X.%02X = 0x%04X\n",Bus, SubBus, AgentId, VendorId); - ++ValidSlots; + if( HvCallXm_connectBusUnit(Bus, SubBus, AgentId, Irq) == 0) { + if( HvCallPci_configLoad16(Bus, SubBus, AgentId, PCI_VENDOR_ID, &VendorId) == 0) { + /**********************************************************/ + /* FoundDevice: 0x18.28.10 = 0x12AE */ + /**********************************************************/ + HvCallPci_configStore8(Bus, SubBus, AgentId, PCI_INTERRUPT_LINE, Irq); + PPCDBG(PPCDBG_BUSWALK,"- FoundDevice: 0x%02X.%02X.%02X = 0x%04X\n", + Bus, SubBus, AgentId, VendorId); + ++DeviceCount; + PCIFR("Device(%4d): 0x%02X.%02X.%02X",DeviceCount,Bus, SubBus, AgentId); + DeviceNode = build_device_node(Bus, SubBus, EADsIdSel, Function); + DeviceNode->Vendor = VendorId; + DeviceNode->Irq = Irq; + + /*********************************************************** + * On the first device/function, assign irq to slot + ***********************************************************/ + if(Function == 0) { + FirstSlotId = AgentId; + // AHT iSeries_assign_IRQ(Irq, Bus, SubBus, AgentId); + //iSeries_assign_IRQ(Irq, Bus, 0, AgentId); + } } } - if (HvRc != 0) { - PPCDBG(PPCDBG_BUSWALK,"\tConnect/Read Vendor failed: 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId, HvRc); - } - } - /**************************************************************************** - * If a device is found, assign irq to slot - ****************************************************************************/ - if (ValidSlots > 0) { - PPCDBG(PPCDBG_BUSWALK,"\tiSeries_assign_IRQ 0x%02X.%02X.%02X = 0x%02X\n",Bus, SubBus, AgentId, Irq ); - iSeries_assign_IRQ(Irq, Bus, SubBus, AgentId); } } - PPCDBG(PPCDBG_BUSWALK,__FUNCTION__" Exit\n"); return HvRc; +} +/************************************************************************/ +/* I/0 Memory copy MUST use mmio commands on iSeries */ +/* To do; For performance, include the hv call directly */ +/************************************************************************/ +void* iSeries_memset(void* dest, char c, size_t Count) { + u8 ByteValue = c; + long NumberOfBytes = Count; + char* IoBuffer = dest; + while(NumberOfBytes > 0) { + iSeries_Write_Byte( ByteValue, (void*)IoBuffer ); + ++IoBuffer; + -- NumberOfBytes; + } + return dest; +} +void* iSeries_memcpy_toio(void *dest, void *source, size_t count) { + char *dst = dest; + char *src = source; + long NumberOfBytes = count; + while(NumberOfBytes > 0) { + iSeries_Write_Byte(*src++, (void*)dst++); + -- NumberOfBytes; + } + return dest; +} +void* iSeries_memcpy_fromio(void *dest, void *source, size_t count) { + char *dst = dest; + char *src = source; + long NumberOfBytes = count; + while(NumberOfBytes > 0) { + *dst++ = iSeries_Read_Byte( (void*)src++); + -- NumberOfBytes; + } + return dest; +} +/********************************************************************************** + * Look down the chain to find the matching Device Device + **********************************************************************************/ +struct iSeries_Device_Node* find_Device_Node(struct pci_dev* PciDev) { + struct list_head* Device_Node_Ptr = Global_Device_List.next; + int Bus = PciDev->bus->number; + int DevFn = PciDev->devfn; + + while(Device_Node_Ptr != &Global_Device_List) { + struct iSeries_Device_Node* DevNode = (struct iSeries_Device_Node*)Device_Node_Ptr; + if(Bus == DevNode->BusNumber && DevFn == DevNode->DevFn) { + return DevNode; + } + Device_Node_Ptr = Device_Node_Ptr->next; + } + return NULL; +} + +/********************************************************************************** + * Function to check if the Device Node is really the device node for the device. + * check to see if the Node passed is a pci_dev or device_node. + **********************************************************************************/ +struct iSeries_Device_Node* check_Device_Node(struct iSeries_Device_Node* Node) { + /* NULL Pointers */ + if(Node->PciDev == NULL || (struct pci_dev*)Node->PciDev->sysdata == NULL) { + return find_Device_Node((struct pci_dev*)Node ); + } + /* Does pci_dev point back to node. */ + else if(Node == Node->PciDev->sysdata) { + return Node; + } + /* Else go searching. */ + else { + return find_Device_Node((struct pci_dev*)Node); + } +} +/******************************************************************/ +/* Returns the device node for the passed pci_dev */ +/* Sanity Check Node PciDev to passed pci_dev */ +/* If none is found, returns a NULL which the client must handle. */ +/******************************************************************/ +struct iSeries_Device_Node* get_Device_Node(struct pci_dev* PciDev) { + struct iSeries_Device_Node* Node; + Node = (struct iSeries_Device_Node*)PciDev->sysdata; + if(Node == NULL ) { + Node = find_Device_Node(PciDev); + } + else if(Node->PciDev != PciDev) { + Node = find_Device_Node(PciDev); + } + return Node; +} +/********************************************************************************** + * + * Read PCI Config Space Code + * + **********************************************************************************/ +/** BYTE ****************************************************************************************/ +int iSeries_Node_read_config_byte(struct iSeries_Device_Node* DevNode, int Offset, u8* ReadValue) { + u8 ReadData; + if(DevNode == NULL) { return 0x301; } + ++Pci_Cfg_Read_Count; + DevNode->ReturnCode = HvCallPci_configLoad8(DevNode->BusNumber,DevNode->SubBus,0x10, + Offset,&ReadData); + if(Pci_Trace_Flag == 1) { + printk("RCB: 0x%02X.%02X 0x%04X = 0x%02X\n",DevNode->BusNumber,DevNode->DevFn,Offset,ReadData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: RCB(%016lX) Error: 0x%04X\n",DevNode->DsaAddr,DevNode->ReturnCode); + PCIFR( "RCB(%016lX) Error: 0x%04X", DevNode->DsaAddr,DevNode->ReturnCode); + } + *ReadValue = ReadData; + return DevNode->ReturnCode; +} +/** WORD ****************************************************************************************/ +int iSeries_Node_read_config_word(struct iSeries_Device_Node* DevNode, int Offset, u16* ReadValue) { + u16 ReadData; + if(DevNode == NULL) { return 0x301; } + ++Pci_Cfg_Read_Count; + DevNode->ReturnCode = HvCallPci_configLoad16(DevNode->BusNumber,DevNode->SubBus,0x10, + Offset,&ReadData); + if(Pci_Trace_Flag == 1) { + printk("RCW: 0x%02X.%02X 0x%04X = 0x%04X\n",DevNode->BusNumber,DevNode->DevFn,Offset,ReadData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: RCW(%016lX) Error: 0x%04X\n",DevNode->DsaAddr,DevNode->ReturnCode); + PCIFR( "RCW(%016lX) Error: 0x%04X", DevNode->DsaAddr,DevNode->ReturnCode); + } + *ReadValue = ReadData; + return DevNode->ReturnCode; +} +/** DWORD ****************************************************************************************/ +int iSeries_Node_read_config_dword(struct iSeries_Device_Node* DevNode, int Offset, u32* ReadValue) { u32 ReadData; + if(DevNode == NULL) { return 0x301; } + ++Pci_Cfg_Read_Count; + DevNode->ReturnCode = HvCallPci_configLoad32(DevNode->BusNumber,DevNode->SubBus,0x10, + Offset,&ReadData); + if(Pci_Trace_Flag == 1) { + printk("RCL: 0x%02X.%02X 0x%04X = 0x%08X\n",DevNode->BusNumber,DevNode->DevFn,Offset,ReadData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: RCL(%016lX) Error: 0x%04X\n",DevNode->DsaAddr,DevNode->ReturnCode); + PCIFR( "RCL(%016lX) Error: 0x%04X", DevNode->DsaAddr,DevNode->ReturnCode); + } + *ReadValue = ReadData; + return DevNode->ReturnCode; +} +int iSeries_pci_read_config_byte(struct pci_dev* PciDev, int Offset, u8* ReadValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_read_config_byte( DevNode ,Offset,ReadValue); +} +int iSeries_pci_read_config_word(struct pci_dev* PciDev, int Offset, u16* ReadValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_read_config_word( DevNode ,Offset,ReadValue ); +} +int iSeries_pci_read_config_dword(struct pci_dev* PciDev, int Offset, u32* ReadValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_read_config_dword(DevNode ,Offset,ReadValue ); +} +/********************************************************************************** + * + * Write PCI Config Space Code + * + **********************************************************************************/ +/** BYTE ****************************************************************************************/ +int iSeries_Node_write_config_byte(struct iSeries_Device_Node* DevNode, int Offset, u8 WriteData) { + ++Pci_Cfg_Write_Count; + DevNode->ReturnCode = HvCallPci_configStore8(DevNode->BusNumber,DevNode->SubBus,0x10, + Offset,WriteData); + if(Pci_Trace_Flag == 1) { + printk("WCB: 0x%02X.%02X 0x%04X = 0x%02X\n",DevNode->BusNumber,DevNode->DevFn,Offset,WriteData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: WCB(%016lX) Error: 0x%04X\n",DevNode->DsaAddr,DevNode->ReturnCode); + PCIFR( "WCB(%016lX) Error: 0x%04X", DevNode->DsaAddr,DevNode->ReturnCode); + } + return DevNode->ReturnCode; +} +/** WORD ****************************************************************************************/ +int iSeries_Node_write_config_word(struct iSeries_Device_Node* DevNode, int Offset, u16 WriteData) { + ++Pci_Cfg_Write_Count; + DevNode->ReturnCode = HvCallPci_configStore16(DevNode->BusNumber,DevNode->SubBus,0x10, + Offset,WriteData); + if(Pci_Trace_Flag == 1) { + printk("WCW: 0x%02X.%02X 0x%04X = 0x%04X\n",DevNode->BusNumber,DevNode->DevFn,Offset,WriteData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: WCW(%016lX) Error: 0x%04X\n",DevNode->DsaAddr,DevNode->ReturnCode); + PCIFR( "WCW(%016lX) Error: 0x%04X", DevNode->DsaAddr,DevNode->ReturnCode); + } + return DevNode->ReturnCode; +} +/** DWORD ****************************************************************************************/ +int iSeries_Node_write_config_dword(struct iSeries_Device_Node* DevNode, int Offset, u32 WriteData) { + ++Pci_Cfg_Write_Count; + DevNode->ReturnCode = HvCallPci_configStore32(DevNode->BusNumber,DevNode->SubBus,0x10, + Offset,WriteData); + if(Pci_Trace_Flag == 1) { + printk("WCL: 0x%02X.%02X 0x%04X = 0x%08X\n",DevNode->BusNumber,DevNode->DevFn,Offset,WriteData); + } + if(DevNode->ReturnCode != 0 ) { + printk("PCI: WCL(%016lX) Error: 0x%04X\n",DevNode->DsaAddr,DevNode->ReturnCode); + PCIFR( "WCL(%016lX) Error: 0x%04X",DevNode->DsaAddr,DevNode->ReturnCode); + } + return DevNode->ReturnCode; +} +int iSeries_pci_write_config_byte( struct pci_dev* PciDev,int Offset, u8 WriteValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_write_config_byte( DevNode,Offset,WriteValue); +} +int iSeries_pci_write_config_word( struct pci_dev* PciDev,int Offset,u16 WriteValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_write_config_word( DevNode,Offset,WriteValue); +} +int iSeries_pci_write_config_dword(struct pci_dev* PciDev,int Offset,u32 WriteValue) { + struct iSeries_Device_Node* DevNode = get_Device_Node(PciDev); + if(DevNode == NULL) return 0x0301; + return iSeries_Node_write_config_dword(DevNode,Offset,WriteValue); +} + +/************************************************************************/ +/* Branch Table */ +/************************************************************************/ +struct pci_ops iSeries_pci_ops = { + iSeries_pci_read_config_byte, + iSeries_pci_read_config_word, + iSeries_pci_read_config_dword, + iSeries_pci_write_config_byte, + iSeries_pci_write_config_word, + iSeries_pci_write_config_dword +}; + +/************************************************************************ + * Check Return Code + * -> On Failure, print and log information. + * Increment Retry Count, if exceeds max, panic partition. + * -> If in retry, print and log success + ************************************************************************ + * PCI: Device 23.90 ReadL I/O Error( 0): 0x1234 + * PCI: Device 23.90 ReadL Retry( 1) + * PCI: Device 23.90 ReadL Retry Successful(1) + ************************************************************************/ +int CheckReturnCode(char* TextHdr, struct iSeries_Device_Node* DevNode) { + ++Pci_Error_Count; + if(DevNode->ReturnCode != 0) { + PCIFR( "Device 0x%04X:%02X :%s: I/O Error(%2d): 0x%04X", + DevNode->BusNumber, DevNode->DevFn,TextHdr,DevNode->IoRetry,DevNode->ReturnCode); + printk("Device 0x%04X:%02X :%s: I/O Error(%2d): 0x%04X\n", + DevNode->BusNumber, DevNode->DevFn,TextHdr,DevNode->IoRetry,DevNode->ReturnCode); + /*******************************************************/ + /* Bump the retry and check for retry count exceeded. */ + /* If, Exceeded, panic the system. */ + /*******************************************************/ + ++DevNode->IoRetry; + if(DevNode->IoRetry > Pci_Retry_Max && Pci_Error_Flag > 1 ) { + mf_displaySrc(0xB6000103); + panic_timeout = 0; + panic("PCI: Hardware I/O Error, SRC B6000103, Automatic Reboot Disabled.\n"); + } + } + /******************************************************************** + * If retry was in progress, log success and rest retry count * + *********************************************************************/ + else if(DevNode->IoRetry > 0) { + DevNode->IoRetry = 0; + PCIFR("Device 0x%04X:%02X %s Retry Successful(%2d).",DevNode->BusNumber, DevNode->DevFn, + TextHdr, DevNode->IoRetry); + } + return DevNode->ReturnCode; +} + +/************************************************************************/ +/* Read MM I/O Instructions for the iSeries */ +/* On MM I/O error, all ones are returned and iSeries_pci_IoError is cal*/ +/* else, data is returned in big Endian format. */ +/************************************************************************/ +/* iSeries_Read_Byte = Read Byte ( 8 bit) */ +/* iSeries_Read_Word = Read Word (16 bit) */ +/* iSeries_Read_Long = Read Long (32 bit) */ +/************************************************************************/ +u8 iSeries_Read_Byte(void* IoAddress) { + u8 IoData, Data; + struct iSeries_Device_Node* DevNode = check_Device_Node(iSeries_xlateIoMmAddress(IoAddress) ); + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + int BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + if(DevNode != 0) { + ++Pci_Io_Read_Count; + DevNode->ReturnCode = HvCallPci_barLoad8(DevNode->BusNumber,DevNode->SubBus,0x10, + BarNumber,BarOffset,&IoData); + Data = IoData; + if(Pci_Trace_Flag == 1) { + printk("RDB: IoAddress 0x%016lX = 0x%02X\n",(unsigned long)IoAddress, Data); + printk("RDB: 0x%02X.%02X Bar: 0x%02X/0x%04X\n", + DevNode->BusNumber,DevNode->SubBus,BarNumber,BarOffset); + } + if(DevNode->ReturnCode != 0) { + CheckReturnCode("RDB",DevNode); + printk("RDB: %s Error: 0x%04X\n",DevNode->PciDev->slot_name,DevNode->ReturnCode); + } + } + return Data; +} + +u16 iSeries_Read_Word(void* IoAddress) { + struct iSeries_Device_Node* DevNode = check_Device_Node(iSeries_xlateIoMmAddress(IoAddress) ); + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + int BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + u16 IoData, Data; + + if(DevNode != 0) { + ++Pci_Io_Read_Count; + DevNode->ReturnCode = HvCallPci_barLoad16(DevNode->BusNumber,DevNode->SubBus,0x10, + BarNumber,BarOffset,&IoData); + Data = swab16(IoData); + + if(Pci_Trace_Flag == 1) { + printk("RDW: IoAddress 0x%016lX = 0x%04X\n",(unsigned long)IoAddress, Data); + printk("RDW: 0x%02X.%02X Bar: 0x%02X/0x%04X\n", + DevNode->BusNumber,DevNode->SubBus,BarNumber,BarOffset); + } + if(DevNode->ReturnCode != 0) { + CheckReturnCode("RDW",DevNode); + printk("RDW: %s Error: 0x%04X\n",DevNode->PciDev->slot_name,DevNode->ReturnCode); + } + } + return Data; +} +u32 iSeries_Read_Long(void* IoAddress) { + struct iSeries_Device_Node* DevNode = check_Device_Node(iSeries_xlateIoMmAddress(IoAddress) ); + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + int BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + u32 IoData, Data; + + if(DevNode != 0) { + ++Pci_Io_Read_Count; + DevNode->ReturnCode = HvCallPci_barLoad32(DevNode->BusNumber,DevNode->SubBus,0x10, + BarNumber,BarOffset,&IoData); + Data = swab32(IoData); + + if(Pci_Trace_Flag == 1) { + printk("RDL: IoAddress 0x%016lX = 0x%08X\n",(unsigned long)IoAddress, Data); + printk("RDL: 0x%02X.%02X Bar: 0x%02X/0x%04X\n", + DevNode->BusNumber,DevNode->SubBus,BarNumber,BarOffset); + } + if(DevNode->ReturnCode != 0) { + CheckReturnCode("RDL",DevNode); + printk("RDL: %s Error: 0x%04X\n",DevNode->PciDev->slot_name,DevNode->ReturnCode); + } + } + return Data; +} +/************************************************************************/ +/* Write MM I/O Instructions for the iSeries */ +/************************************************************************/ +/* iSeries_Write_Byte = Write Byte (8 bit) */ +/* iSeries_Write_Word = Write Word(16 bit) */ +/* iSeries_Write_Long = Write Long(32 bit) */ +/************************************************************************/ +void iSeries_Write_Byte(u8 Data, void* IoAddress) { + struct iSeries_Device_Node* DevNode = check_Device_Node(iSeries_xlateIoMmAddress(IoAddress) ); + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + int BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + u8 IoData = Data; + + if(DevNode != 0 && BarNumber != -1) { + ++Pci_Io_Write_Count; + DevNode->ReturnCode = HvCallPci_barStore8 (DevNode->BusNumber,DevNode->SubBus,0x10, + BarNumber,BarOffset,IoData); + if(Pci_Trace_Flag == 1) { + printk("WWB: IoAddress 0x%016lX = 0x%02X\n",(unsigned long)IoAddress,Data); + printk("WWB: 0x%02X.%02X Bar: 0x%02X/0x%04X\n", + DevNode->BusNumber,DevNode->SubBus,BarNumber,BarOffset); + } + if(DevNode->ReturnCode != 0) { + CheckReturnCode("WWB",DevNode); + printk("WWB: %s Error: 0x%04X\n",DevNode->PciDev->slot_name,DevNode->ReturnCode); + } + } +} +void iSeries_Write_Word(u16 Data, void* IoAddress) { + struct iSeries_Device_Node* DevNode = check_Device_Node(iSeries_xlateIoMmAddress(IoAddress) ); + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + int BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + u16 IoData = swab16(Data); + + if(DevNode != 0 ) { + ++Pci_Io_Write_Count; + DevNode->ReturnCode = HvCallPci_barStore16(DevNode->BusNumber,DevNode->SubBus,0x10, + BarNumber,BarOffset,IoData); + if(Pci_Trace_Flag == 1) { + printk("WWW: IoAddress 0x%016lX = 0x%04X\n",(unsigned long)IoAddress,Data); + printk("WWW: 0x%02X.%02X Bar: 0x%02X/0x%04X\n", + DevNode->BusNumber,DevNode->SubBus,BarNumber,BarOffset); + } + if(DevNode->ReturnCode != 0) { + CheckReturnCode("WWW",DevNode); + printk("WWW: %s Error: 0x%04X\n",DevNode->PciDev->slot_name,DevNode->ReturnCode); + } + } +} +void iSeries_Write_Long(u32 Data, void* IoAddress) { + struct iSeries_Device_Node* DevNode = check_Device_Node(iSeries_xlateIoMmAddress(IoAddress) ); + int BarNumber = iSeries_IoMmTable_Bar(IoAddress); + int BarOffset = iSeries_IoMmTable_BarOffset(IoAddress); + u32 IoData = swab32(Data); + + if(DevNode != 0 && BarNumber != -1) { + ++Pci_Io_Write_Count; + DevNode->ReturnCode = HvCallPci_barStore32(DevNode->BusNumber,DevNode->SubBus,0x10, + BarNumber,BarOffset,IoData); + if(Pci_Trace_Flag == 1) { + printk("WWL: IoAddress 0x%016lX = 0x%08X\n",(unsigned long)IoAddress, Data); + printk("WWL: 0x%02X.%02X Bar: 0x%02X/0x%04X\n", + DevNode->BusNumber,DevNode->SubBus,BarNumber,BarOffset); + } + if(DevNode->ReturnCode != 0) { + CheckReturnCode("WWL",DevNode); + printk("WWL: %s Error: 0x%04X\n",DevNode->PciDev->slot_name,DevNode->ReturnCode); + } + } +} +/* + * This is called very early before the page table is setup. + * There are warnings here because of type mismatches.. Okay for now. AHT + */ +void +iSeries_pcibios_init_early(void) { + ppc_md.pcibios_read_config_byte = iSeries_Node_read_config_byte; + ppc_md.pcibios_read_config_word = iSeries_Node_read_config_word; + ppc_md.pcibios_read_config_dword = iSeries_Node_read_config_dword; + ppc_md.pcibios_write_config_byte = iSeries_Node_write_config_byte; + ppc_md.pcibios_write_config_word = iSeries_Node_write_config_word; + ppc_md.pcibios_write_config_dword = iSeries_Node_write_config_dword; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_pci_reset.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_pci_reset.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_pci_reset.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_pci_reset.c Thu Sep 20 19:46:15 2001 @@ -0,0 +1,147 @@ +/************************************************************************/ +/* File iSeries_pci_reset.c created by Allan Trautman on Mar 21 2001. */ +/************************************************************************/ +/* This code supports the pci interface on the IBM iSeries systems. */ +/* Copyright (C) 20yy */ +/* */ +/* 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 */ +/************************************************************************/ +/* Change Activity: */ +/* Created, March 20, 2001 */ +/* April 30, 2001, Added return codes on functions. */ +/* September 10, 2001, Ported to ppc64. */ +/* End Change Activity */ +/************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "pci.h" + +/************************************************************************/ +/* Interface to toggle the reset line */ +/* Time is in .1 seconds, need for seconds. */ +/************************************************************************/ +int iSeries_Device_ToggleReset(struct pci_dev* PciDev, int AssertTime, int DelayTime) { + unsigned long AssertDelay, WaitDelay; + struct iSeries_Device_Node* DeviceNode = (struct iSeries_Device_Node*)PciDev->sysdata; + if(DeviceNode == NULL) { + printk("PCI: Pci Reset Failed, Device Node not found for pci_dev %p\n",PciDev); + return -1; + } + /******************************************************************** + * Set defaults, Assert is .5 second, Wait is 3 seconds. + ********************************************************************/ + if(AssertTime == 0) AssertDelay = ( 5 * HZ)/10; + else AssertDelay = (AssertTime*HZ)/10; + if(WaitDelay == 0) WaitDelay = (30 * HZ)/10; + else WaitDelay = (DelayTime* HZ)/10; + + /******************************************************************** + * Assert reset + ********************************************************************/ + DeviceNode->ReturnCode = HvCallPci_setSlotReset(DeviceNode->BusNumber, 0x00, DeviceNode->AgentId, 1); + if(DeviceNode->ReturnCode == 0) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(AssertDelay); /* Sleep for the time */ + DeviceNode->ReturnCode = HvCallPci_setSlotReset(DeviceNode->BusNumber, 0x00, DeviceNode->AgentId, 0); + + /*************************************************************** + * Wait for device to reset + ***************************************************************/ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(WaitDelay); + } + if(DeviceNode->ReturnCode == 0) { + PCIFR("Slot 0x%04X.%02 Reset\n",DeviceNode->BusNumber,DeviceNode->AgentId ); + } + else { + printk("PCI: Slot 0x%04X.%02X Reset Failed, RCode: %04X\n",DeviceNode->BusNumber,DeviceNode->AgentId,DeviceNode->ReturnCode); + PCIFR( "Slot 0x%04X.%02X Reset Failed, RCode: %04X\n",DeviceNode->BusNumber,DeviceNode->AgentId,DeviceNode->ReturnCode); + } + return DeviceNode->ReturnCode; +} + +/************************************************************************ + * Saves the config registers for a device. * + ************************************************************************ + * Note: This does byte reads so the data may appear byte swapped, * + * The data returned in the pci_config_reg_save_area structure to be * + * used to the restore of the data. If the save failed, the data * + * data will not be restore. Yes I know, you are most likey toast. * + ************************************************************************/ +int pci_save_config_regs(struct pci_dev* PciDev,struct pci_config_reg_save_area* SaveArea){ + memset(SaveArea,0x00,sizeof(struct pci_config_reg_save_area) ); + SaveArea->PciDev = PciDev; + SaveArea->RCode = 0; + SaveArea->Register = 0; + /****************************************************************** + * Save All the Regs, NOTE: restore skips the first 16 bytes. * + ******************************************************************/ + while(SaveArea->Register < REG_SAVE_SIZE && SaveArea->RCode == 0) { + SaveArea->RCode = pci_read_config_byte(PciDev, SaveArea->Register, &SaveArea->Regs[SaveArea->Register]); + ++SaveArea->Register; + } + if(SaveArea->RCode != 0) { /* Ouch */ + SaveArea->Flags = 0x80; + printk("PCI: pci_restore_save_regs failed! %p\n 0x%04X",PciDev,SaveArea->RCode); + PCIFR( "pci_restore_save_regs failed! %p\n 0x%04X",PciDev,SaveArea->RCode); + } + else { + SaveArea->Flags = 0x01; + } + return SaveArea->RCode; +} +/************************************************************************ + * Restores the registers saved via the save function. See the save * + * function for details. * + ************************************************************************/ +int pci_restore_config_regs(struct pci_dev* PciDev,struct pci_config_reg_save_area* SaveArea) { + if(SaveArea->PciDev != PciDev || SaveArea->Flags == 0x80 || SaveArea->RCode != 0) { + printk("PCI: pci_restore_config_regs failed! %p\n",PciDev); + return -1; + } + /****************************************************************** + * Don't touch the Cmd or BIST regs, user must restore those. * + * Restore PCI_VENDOR_ID & PCI_DEVICE_ID * + * Restore PCI_CACHE_LINE_SIZE & PCI_LATENCY_TIMER * + * Restore Saved Regs from 0x10 to 0x3F * + ******************************************************************/ + SaveArea->Register = 0; + while(SaveArea->Register < REG_SAVE_SIZE && SaveArea->RCode == 0) { + SaveArea->RCode = pci_write_config_byte(PciDev,SaveArea->Register,SaveArea->Regs[SaveArea->Register]); + ++SaveArea->Register; + if( SaveArea->Register == PCI_COMMAND) SaveArea->Register = PCI_CACHE_LINE_SIZE; + else if(SaveArea->Register == PCI_HEADER_TYPE) SaveArea->Register = PCI_BASE_ADDRESS_0; + } + if(SaveArea->RCode != 0) { + printk("PCI: pci_restore_config_regs failed! %p\n 0x%04X",PciDev,SaveArea->RCode); + PCIFR( "pci_restore_config_regs failed! %p\n 0x%04X",PciDev,SaveArea->RCode); + } + return SaveArea->RCode; +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_setup.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c --- linux-2.4.9-ac10/arch/ppc64/kernel/iSeries_setup.c Wed Oct 3 12:11:09 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c Wed Sep 26 13:41:13 2001 @@ -48,17 +48,21 @@ #include #include #include -#include /* Function Prototypes */ extern void abort(void); +#ifdef CONFIG_PPC_ISERIES static void build_iSeries_Memory_Map( void ); static void setup_iSeries_cache_sizes( void ); static void iSeries_bolt_kernel(unsigned long saddr, unsigned long eaddr); +#endif void build_valid_hpte( unsigned long vsid, unsigned long ea, unsigned long pa, pte_t * ptep, unsigned hpteflags, unsigned bolted ); extern void ppcdbg_initialize(void); +extern void iSeries_pcibios_init(void); +extern void iSeries_pcibios_fixup(void); +extern void iSeries_pcibios_fixup_bus(int); /* Global Variables */ @@ -77,6 +81,9 @@ extern struct Naca *naca; extern int rd_size; /* Defined in drivers/block/rd.c */ extern unsigned long klimit; +extern unsigned long embedded_sysmap_start; +extern unsigned long embedded_sysmap_end; +static int mf_initialized = 0; /* * void __init iSeries_init_early() @@ -85,7 +92,7 @@ void __init iSeries_init_early(void) { - +#ifdef CONFIG_PPC_ISERIES ppcdbg_initialize(); #if defined(CONFIG_BLK_DEV_INITRD) @@ -107,32 +114,27 @@ #endif /* CONFIG_BLK_DEV_INITRD */ { - ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); + /* ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); */ } - /* Initialize the table which translate Linux physical addresses to - * AS/400 absolute addresses - */ - - build_iSeries_Memory_Map(); - - setup_iSeries_cache_sizes(); - - /* Initialize machine-dependency vectors */ - ppc_md.setup_arch = iSeries_setup_arch; ppc_md.setup_residual = iSeries_setup_residual; ppc_md.get_cpuinfo = iSeries_get_cpuinfo; ppc_md.irq_cannonicalize = NULL; ppc_md.init_IRQ = iSeries_init_IRQ; + ppc_md.init_ras_IRQ = NULL; ppc_md.get_irq = iSeries_get_irq; ppc_md.init = NULL; + ppc_md.pcibios_fixup = iSeries_pcibios_fixup; + ppc_md.pcibios_fixup_bus = iSeries_pcibios_fixup_bus; + ppc_md.restart = iSeries_restart; ppc_md.power_off = iSeries_power_off; ppc_md.halt = iSeries_halt; ppc_md.time_init = NULL; + ppc_md.get_boot_time = iSeries_get_boot_time; ppc_md.set_rtc_time = iSeries_set_rtc_time; ppc_md.get_rtc_time = iSeries_get_rtc_time; ppc_md.calibrate_decr = iSeries_calibrate_decr; @@ -149,11 +151,27 @@ ppc_md.ppc_kbd_sysrq_xlate = NULL; #endif - if ( itLpNaca.xPirEnvironMode == 0 ) - piranha_simulator = 1; + htpe_init_iSeries(); + tce_init_iSeries(); + + /* Initialize the table which translate Linux physical addresses to + * AS/400 absolute addresses + */ + + build_iSeries_Memory_Map(); + + setup_iSeries_cache_sizes(); + + /* Initialize machine-dependency vectors */ - return; +#ifdef CONFIG_SMP + smp_init_iSeries(); +#endif + + if ( itLpNaca.xPirEnvironMode == 0 ) + piranha_simulator = 1; +#endif } /* @@ -188,15 +206,13 @@ iSeries_proc_early_init(); mf_init(); + mf_initialized = 1; + mb(); iSeries_proc_callback( &pmc_proc_init ); - - viopath_init(); - - return; } - +#ifdef CONFIG_PPC_ISERIES /* * The iSeries may have very large memories ( > 128 GB ) and a partition * may get memory in "chunks" that may be anywhere in the 2**52 real @@ -421,6 +437,7 @@ } } } +#endif /* CONFIG_PPC_ISERIES */ /* * Document me. @@ -514,77 +531,6 @@ return (len); } -#ifdef CONFIG_SMP -void iSeries_spin_lock(spinlock_t *lock) -{ - int i; - for (;;) { - for (i=1024; i>0; --i) { - HMT_low(); - if ( lock->lock == 0 ) { - HMT_medium(); - if ( __spin_trylock(lock) ) - return; - } - } - HMT_medium(); - HvCallCfg_getLps(); - } -} - -void iSeries_read_lock(__rwlock_t *rw) -{ - int i; - for (;;) { - for (i=1024; i>0; --i) { - HMT_low(); - if ( rw->lock >= 0 ) { - HMT_medium(); - if ( __read_trylock(rw) ) - return; - } - } - HMT_medium(); - HvCallCfg_getLps(); - } -} - -void iSeries_write_lock(__rwlock_t *rw) -{ - int i; - for (;;) { - for (i=1024; i>0; --i) { - HMT_low(); - if ( rw->lock == 0 ) { - HMT_medium(); - if ( __write_trylock(rw) ) - return; - } - } - HMT_medium(); - HvCallCfg_getLps(); - } -} - -void iSeries_brlock_spin( spinlock_t *lock ) -{ - int i; - for(;;) { - for (i=1024; i>0; --i) { - HMT_low(); - if (lock->lock == 0) { - HMT_medium(); - return; - } - - } - HMT_medium(); - HvCallCfg_getLps(); - } -} - -#endif /* CONFIG_SMP */ - int iSeries_get_cpuinfo(char *buffer) { int len = 0; @@ -594,46 +540,6 @@ return len; } -static char * lpEventTypes[9]= { -/* 0123456789012345678901234567890 */ - "Hypervisor\t\t", - "Machine Facilities\t", - "Session Manager\t\t", - "SPD I/O\t\t\t", - "Virtual Bus\t\t", - "PCI I/O\t\t\t", - "RIO I/O\t\t\t", - "Virtual Lan\t\t", - "Virtual I/O\t\t" - }; - -int get_ioevent_data(char *buffer) -{ - int len = 0; - unsigned i; - - len += sprintf(len+buffer,"LpEventQueue 0\n"); - len += sprintf(len+buffer," events processed:\t%lu\n", - (unsigned long)xItLpQueue.xLpIntCount ); - /* display event counts by type */ - for (i=0; ixLpPaca.xIntDword.xFields.xDecrInt = 1; - __sti(); + process_iSeries_events(); } int idled(void) @@ -104,6 +92,8 @@ for (;;) { if ( paca->xLpPaca.xSharedProc ) { + if ( ItLpQueue_isLpIntPending( paca->lpQueuePtr ) ) + process_iSeries_events(); if ( !current->need_resched ) yield_shared_processor(); } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ioctl32.c linuxppc64_2_4/arch/ppc64/kernel/ioctl32.c --- linux-2.4.9-ac10/arch/ppc64/kernel/ioctl32.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ioctl32.c Tue Sep 25 13:52:29 2001 @@ -543,54 +543,6 @@ return err; } -static int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) -{ - struct ifreq ifr; - mm_segment_t old_fs; - int err, len; - u32 data, ethcmd; - - if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) - return -EFAULT; - ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); - if (!ifr.ifr_data) - return -EAGAIN; - - __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); - - if (get_user(ethcmd, (u32 *)A(data))) { - err = -EFAULT; - goto out; - } - switch (ethcmd) { - case ETHTOOL_GDRVINFO: len = sizeof(struct ethtool_drvinfo); break; - case ETHTOOL_GSET: - case ETHTOOL_SSET: - default: len = sizeof(struct ethtool_cmd); break; - } - - if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { - err = -EFAULT; - goto out; - } - - old_fs = get_fs(); - set_fs (KERNEL_DS); - err = sys_ioctl (fd, cmd, (unsigned long)&ifr); - set_fs (old_fs); - if (!err) { - u32 data; - - __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); - len = copy_to_user((char *)A(data), ifr.ifr_data, len); - if (len) - err = -EFAULT; - } - -out: - free_page((unsigned long)ifr.ifr_data); - return err; -} static int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg) { @@ -1656,10 +1608,10 @@ set_fs(old_fs); if(err == 0) { - __u32 redptr = (__u32)cmap.red; - __u32 greenptr = (__u32)cmap.green; - __u32 blueptr = (__u32)cmap.blue; - __u32 transpptr = (__u32)cmap.transp; + __u32 redptr = (__u32)(__u64)cmap.red; + __u32 greenptr = (__u32)(__u64)cmap.green; + __u32 blueptr = (__u32)(__u64)cmap.blue; + __u32 transpptr = (__u32)(__u64)cmap.transp; err = put_user(cmap.start, &((struct fb_cmap32 *)arg)->start); err |= __put_user(cmap.len, &((struct fb_cmap32 *)arg)->len); @@ -1690,10 +1642,10 @@ if (err) { err = -EFAULT; } else { - cmap.red = (__u16 *)redptr; - cmap.green = (__u16 *)greenptr; - cmap.blue = (__u16 *)blueptr; - cmap.transp = (__u16 *)transpptr; + cmap.red = (__u16 *)(__u64)redptr; + cmap.green = (__u16 *)(__u64)greenptr; + cmap.blue = (__u16 *)(__u64)blueptr; + cmap.transp = (__u16 *)(__u64)transpptr; set_fs (KERNEL_DS); err = sys_ioctl (fd, cmd, (unsigned long)&cmap); set_fs (old_fs); @@ -1820,7 +1772,7 @@ goto out; } - err = copy_from_user(iobuf.buffer, A(iobuf32.buffer), iobuf.length); + err = copy_from_user(iobuf.buffer, (void *)A(iobuf32.buffer), iobuf.length); if (err) { err = -EFAULT; goto out; @@ -1834,7 +1786,7 @@ goto out; if(iobuf.buffer && iobuf.length > 0) { - err = copy_to_user(A(iobuf32.buffer), iobuf.buffer, iobuf.length); + err = copy_to_user((void *)A(iobuf32.buffer), iobuf.buffer, iobuf.length); if (err) { err = -EFAULT; goto out; @@ -1874,7 +1826,7 @@ goto out; } - err = copy_from_user(sioc.arg, A(sioc32.arg), sioc32.length); + err = copy_from_user(sioc.arg, (void *)A(sioc32.arg), sioc32.length); if (err) { err = -EFAULT; goto out; @@ -1889,7 +1841,7 @@ } if(sioc.arg && sioc.length > 0) { - err = copy_to_user(A(sioc32.arg), sioc.arg, sioc.length); + err = copy_to_user((void *)A(sioc32.arg), sioc.arg, sioc.length); if (err) { err = -EFAULT; goto out; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/irq.c linuxppc64_2_4/arch/ppc64/kernel/irq.c --- linux-2.4.9-ac10/arch/ppc64/kernel/irq.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/irq.c Wed Sep 26 13:41:13 2001 @@ -23,14 +23,6 @@ * instead of just grabbing them. Thus setups with different IRQ numbers * shouldn't result in any weird surprises, and installing new handlers * should be easier. - * - * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the - * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit - * mask register (of which only 16 are defined), hence the weird shifting - * and compliment of the cached_irq_mask. I want to be able to stuff - * this right into the SIU SMASK register. - * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx - * to reduce code space and undefined function references. */ @@ -83,9 +75,6 @@ int ppc_spurious_interrupts = 0; struct irqaction *ppc_irq_action[NR_IRQS]; -unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; -unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; -atomic_t ppc_n_lost_interrupts; unsigned long lpEvent_count = 0; #ifdef CONFIG_XMON extern void xmon(struct pt_regs *regs); @@ -538,22 +527,18 @@ /* every arch is required to have a get_irq -- Cort */ irq = ppc_md.get_irq( regs ); - if ( irq < 0 ) - { + if ( irq >= 0 ) { + ppc_irq_dispatch_handler( regs, irq ); + if (ppc_md.post_irq) + ppc_md.post_irq( regs, irq ); + } else { /* -2 means ignore, already handled */ - if (irq != -2) - { + if (irq != -2) { printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", - irq, regs->nip); + irq, regs->nip); ppc_spurious_interrupts++; } - goto out; } - ppc_irq_dispatch_handler( regs, irq ); - if (ppc_md.post_irq) - ppc_md.post_irq( regs, irq ); - - out: } /* if on iSeries partition */ else { @@ -565,7 +550,7 @@ } #endif /* CONFIG_SMP */ lpq = paca->lpQueuePtr; - if ( lpq ) + if ( lpq && ItLpQueue_isLpIntPending( lpq ) ) lpEvent_count += ItLpQueue_process( lpq, regs ); } @@ -610,6 +595,7 @@ once++; ppc_md.init_IRQ(); + if(ppc_md.init_ras_IRQ) ppc_md.init_ras_IRQ(); } #ifdef CONFIG_SMP @@ -779,7 +765,7 @@ { if (count < HEX_DIGITS+1) return -EINVAL; - return sprintf (page, "%08x\n", irq_affinity[(int)data]); + return sprintf (page, "%08x\n", irq_affinity[(int)(long)data]); } static unsigned int parse_hex_value (const char *buffer, @@ -822,7 +808,7 @@ static int irq_affinity_write_proc (struct file *file, const char *buffer, unsigned long count, void *data) { - int irq = (int) data, full_count = count, err; + int irq = (int)(long) data, full_count = count, err; unsigned long new_value; if (!irq_desc[irq].handler->set_affinity) @@ -890,7 +876,7 @@ entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); entry->nlink = 1; - entry->data = (void *)irq; + entry->data = (void *)(long)irq; entry->read_proc = irq_affinity_read_proc; entry->write_proc = irq_affinity_write_proc; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/lmb.c linuxppc64_2_4/arch/ppc64/kernel/lmb.c --- linux-2.4.9-ac10/arch/ppc64/kernel/lmb.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/lmb.c Tue Sep 25 13:52:29 2001 @@ -17,6 +17,7 @@ #include #include #include +#include extern unsigned long klimit; extern unsigned long reloc_offset(void); diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/local_irq.h linuxppc64_2_4/arch/ppc64/kernel/local_irq.h --- linux-2.4.9-ac10/arch/ppc64/kernel/local_irq.h Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/local_irq.h Sat Sep 15 22:35:39 2001 @@ -22,8 +22,5 @@ extern int ppc_spurious_interrupts; extern int ppc_second_irq; extern struct irqaction *ppc_irq_action[NR_IRQS]; -extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; -extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; -extern atomic_t ppc_n_lost_interrupts; #endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/mf.c linuxppc64_2_4/arch/ppc64/kernel/mf.c --- linux-2.4.9-ac10/arch/ppc64/kernel/mf.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/mf.c Tue Sep 25 13:52:29 2001 @@ -41,6 +41,7 @@ #include #include +extern struct pci_dev * iSeries_vio_dev; /* * This is the structure layout for the Machine Facilites LPAR event @@ -289,7 +290,7 @@ if( newElement == NULL ) { - printk( KERN_ERR "mf.c: unable to kmalloc %d bytes\n", sizeof(struct StackElement) ); + printk( KERN_ERR "mf.c: unable to kmalloc %ld bytes\n", sizeof(struct StackElement) ); return NULL; } @@ -880,7 +881,7 @@ struct VspCmdData myVspCmd; int rc = 0; dma_addr_t dma_addr = 0; - char *page = pci_alloc_consistent(NULL, size, &dma_addr); + char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr); if (page == NULL) { printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n"); @@ -898,7 +899,7 @@ mb(); rc = signalVspInstruction(&myVspCmd); - pci_free_consistent(NULL, size, page, dma_addr); + pci_free_consistent(iSeries_vio_dev, size, page, dma_addr); } int mf_getCmdLine(char *cmdline, int *size, u64 side) @@ -906,7 +907,7 @@ struct VspCmdData myVspCmd; int rc = 0; int len = *size; - dma_addr_t dma_addr = pci_map_single(NULL, cmdline, *size, PCI_DMA_FROMDEVICE); + dma_addr_t dma_addr = pci_map_single(iSeries_vio_dev, cmdline, *size, PCI_DMA_FROMDEVICE); memset(cmdline, 0, *size); memset(&myVspCmd, 0, sizeof(myVspCmd)); @@ -931,7 +932,7 @@ */ } - pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + pci_unmap_single(iSeries_vio_dev, dma_addr, *size, PCI_DMA_FROMDEVICE); return len; } @@ -944,7 +945,7 @@ dma_addr_t dma_addr = 0; - char *page = pci_alloc_consistent(NULL, size, &dma_addr); + char *page = pci_alloc_consistent(iSeries_vio_dev, size, &dma_addr); if (page == NULL) { printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); @@ -975,7 +976,7 @@ } } - pci_free_consistent(NULL, size, page, dma_addr); + pci_free_consistent(iSeries_vio_dev, size, page, dma_addr); return rc; } @@ -986,7 +987,7 @@ int rc = 0; int len = *size; - dma_addr_t dma_addr = pci_map_single(NULL, buffer, *size, PCI_DMA_FROMDEVICE); + dma_addr_t dma_addr = pci_map_single(iSeries_vio_dev, buffer, *size, PCI_DMA_FROMDEVICE); memset(buffer, 0, len); @@ -1012,7 +1013,7 @@ } } - pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + pci_unmap_single(iSeries_vio_dev, dma_addr, *size, PCI_DMA_FROMDEVICE); return rc; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/misc.S linuxppc64_2_4/arch/ppc64/kernel/misc.S --- linux-2.4.9-ac10/arch/ppc64/kernel/misc.S Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/misc.S Tue Oct 2 18:35:11 2001 @@ -63,14 +63,11 @@ mr r3,r1 blr -_GLOBAL(get_paca) - mfspr r3,SPRG3; - blr - -/* void __no_use_save_flags(unsigned long *flags) */ +#ifdef CONFIG_PPC_ISERIES +/* unsigned long __no_use_save_flags(void) */ _GLOBAL(__no_use_save_flags) - mfmsr r4 - std r4,0(r3) + mfspr r4,SPRG3 + lbz r3,PACAPROCENABLED(r4) blr /* void __no_use_restore_flags(unsigned long flags) */ @@ -81,125 +78,54 @@ * sense anyway. * -- Cort */ - mfmsr r5 - mr r4,r3 - mr r3,r5 - rlwimi r3,r4,0,16,16 - /* Copy all except the MSR_EE bit from r4 (current MSR value) - to r3. This is the sort of thing the rlwimi instruction is - designed for. -- paulus. */ + mfspr r6,SPRG3 + lbz r5,PACAPROCENABLED(r6) /* Check if things are setup the way we want _already_. */ cmpw 0,r3,r5 beqlr /* are we enabling interrupts? */ - rlwinm. r0,r3,0,16,16 - beq 1f - /* Check pending interrupts - * A decrementer, IPI or PMC interrupt may have occurred - * while we were in the hypervisor (which enables) - */ + cmpi 0,r3,0 + stb r3,PACAPROCENABLED(r6) + beqlr + /* Check pending interrupts */ CHECKANYINT(r4,r5) - beq+ 1f + beqlr /* * Handle pending interrupts in interrupt context */ - mtmsrd r3 li r0,0x5555 - sc - blr -1: - sync - mtmsrd r3 - isync - blr -/* void __no_lpq_restore_flags(unsigned long flags) */ -_GLOBAL(__no_lpq_restore_flags) - mfmsr r5 - mr r4,r3 - mr r3,r5 - rlwimi r3,r4,0,16,16 - /* Copy all except the MSR_EE bit from r4 (current MSR value) - to r3. This is the sort of thing the rlwimi instruction is - designed for. -- paulus. */ - /* Check if things are setup the way we want _already_. */ - cmpw 0,r3,r5 - beqlr - sync - mtmsrd r3 - isync + sc blr _GLOBAL(__no_use_cli) - mfmsr r0 /* Get current interrupt state */ - rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ - rldicl r0,r0,48,1 - rldicl r0,r0,16,0 /* clear MSR_EE in r0 */ - mtmsrd r0 /* Update machine state */ + mfspr r5,SPRG3 + lbz r3,PACAPROCENABLED(r5) + li r4,0 + stb r4,PACAPROCENABLED(r5) blr /* Done */ _GLOBAL(__no_use_sti) - mfmsr r3 /* Get current state */ - ori r3,r3,MSR_EE /* Turn on 'EE' bit */ + mfspr r6,SPRG3 + li r3,1 + stb r3,PACAPROCENABLED(r6) /* Check for pending interrupts * A decrementer, IPI or PMC interrupt may have occurred * while we were in the hypervisor (which enables) */ CHECKANYINT(r4,r5) - beq+ 1f + beqlr /* * Handle pending interrupts in interrupt context */ - mtmsrd r3 li r0,0x5555 sc blr -1: - sync /* Some chip revs have problems here... */ - mtmsrd r3 /* Update machine state */ - blr - -/* - * We were about to enable interrupts but we have to simulate - * some interrupts that were lost by enable_irq first. - */ -#if 0 -_GLOBAL(do_fake_interrupt) - mflr r0 - std r0,16(r1) - std r3,-8(r1) - stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) -1: bl .fake_interrupt - /* Check for pending interrupt - * A decrementer, IPI or PMC interrupt may have occurred - * while we were in the hypervisor (which enables) - */ - CHECKANYINT(r4,r5) - bne- 1b - addi r1,r1,STACK_FRAME_OVERHEAD+16 - ld r3,-8(r1) - ld r0,16(r1) - mtlr r0 - mtmsrd r3 - blr -#endif -/* - * complement mask on the msr then "or" some values on. - * _nmask_and_or_msr(nmask, value_to_or) - */ -_GLOBAL(_nmask_and_or_msr) - mfmsr r0 /* Get current msr */ - andc r0,r0,r3 /* And off the bits set in r3 (first parm) */ - or r0,r0,r4 /* Or on the bits in r4 (second parm) */ - mtmsrd r0 /* Update machine state */ - SYNC - blr /* Done */ - +#endif /* * Flush instruction cache. - * This is a no-op on the 601. */ _GLOBAL(flush_instruction_cache) @@ -210,15 +136,13 @@ * the cache lines that are actually * modified */ - mfspr r3,PVR - rlwinm r3,r3,16,16,31 - cmpi 0,r3,1 - beqlr /* for 601, do nothing */ - /* 603/604 processor - use invalidate-all bit in HID0 */ + /* use invalidate-all bit in HID0 + * - is this consistent across all 64-bit cpus? -- paulus */ mfspr r3,HID0 ori r3,r3,HID0_ICFI mtspr HID0,r3 - SYNC + sync + isync blr /* @@ -269,7 +193,6 @@ 2: icbi 0,r6 add r6,r6,r7 bdnz 2b - sync isync blr @@ -308,11 +231,10 @@ * Flush a particular page from the data cache to RAM. * Note: this is necessary because the instruction cache does *not* * snoop from the data cache. - * This is a no-op on the 601 which has a unified cache. * - * void __flush_page_to_ram(void *page) + * void __flush_dcache_icache(void *page) */ -_GLOBAL(__flush_page_to_ram) +_GLOBAL(__flush_dcache_icache) /* * Flush the data cache to memory * @@ -340,130 +262,26 @@ 1: icbi 0,r3 add r3,r3,r5 bdnz 1b - sync - isync - blr - - -/* - * Flush a particular page from the instruction cache. - * Note: this is necessary because the instruction cache does *not* - * snoop from the data cache. - * - * void __flush_icache_page(void *page) - */ -_GLOBAL(__flush_icache_page) -/* - * Different systems have different cache line sizes - */ - -/* Invalidate the icache */ - - LOADADDR(r6,naca) - ld r6,0(r6) - clrrdi r3,r3,12 /* Page align */ - lhz r4,ICACHEL1LINESPERPAGE(r6) /* Get # icache lines per page */ - lhz r5,ICACHEL1LINESIZE(r6) /* Get icache line size */ - mtctr r4 -1: icbi 0,r3 - add r3,r3,r5 - bdnz 1b - sync isync blr /* - * Clear a page using the dcbz instruction, which doesn't cause any - * memory traffic (except to write out any cache lines which get - * displaced). This only works on cacheable memory. + * Copy a whole page. Assumes a 4096B page size. */ -_GLOBAL(clear_page) - LOADADDR(r6,naca) - ld r6,0(r6) - clrrdi r3,r3,12 /* Page align */ - lhz r4,DCACHEL1LINESPERPAGE(r6) /* Get # dcache lines per page */ - lhz r5,DCACHEL1LINESIZE(r6) /* Get dcache line size */ - mtctr r4 -0: dcbz 0,r3 - add r3,r3,r5 - bdnz 0b - blr - -/* - * Copy a whole page. We use the dcbz instruction on the destination - * to reduce memory traffic (it eliminates the unnecessary reads of - * the destination into cache). This requires that the destination - * is cacheable. - */ -#define COPY_32_BYTES \ - ld r6,8(r4); \ - ld r7,16(r4); \ - ld r8,24(r4); \ - ldu r9,32(r4); \ - std r6,8(r3); \ - std r7,16(r3); \ - std r8,24(r3); \ - stdu r9,32(r3) - _GLOBAL(copy_page) - - LOADADDR(r9,naca) - ld r9,0(r9) clrrdi r3,r3,12 /* Page align */ clrrdi r4,r4,12 /* Page align */ - lhz r5,DCACHEL1LINESPERPAGE(r9) /* Get # dcache lines per page */ - lhz r0,DCACHEL1LINESIZE(r9) /* Get dcache line size */ + li r5,256 mtctr r5 addi r3,r3,-8 addi r4,r4,-8 - li r10,8 - - cmpi 0,r0,32 - beq do_32_byte_line - cmpi 0,r0,64 - beq do_64_byte_line - cmpi 0,r0,128 - beq do_128_byte_line - - /* We don't have code specifically for this cache line size */ - /* Assume that the cache line size is at least 32 (and of */ - /* course a multiple of 32) */ - /* This code will work for all power-of-2 cache line sizes */ - /* from 32 to 4096 */ - -1: mr r5,r0 - dcbz r10,r3 -0: COPY_32_BYTES - addi r5,r5,-32 - or. r5,r5,r5 - bne 0b - bdnz 1b - blr - .globl do_32_byte_line -do_32_byte_line: - dcbz r10,r3 - COPY_32_BYTES - bdnz do_32_byte_line - blr - - .globl do_64_byte_line -do_64_byte_line: - dcbz r10,r3 - COPY_32_BYTES - COPY_32_BYTES - bdnz do_64_byte_line - blr - - .globl do_128_byte_line -do_128_byte_line: - dcbz r10,r3 - COPY_32_BYTES - COPY_32_BYTES - COPY_32_BYTES - COPY_32_BYTES - bdnz do_128_byte_line - blr +1: ld r6,8(r4) + ldu r7,16(r4) + std r6,8(r3) + stdu r7,16(r3) + bdnz+ 1b + blr /* * I/O string operations @@ -651,16 +469,6 @@ mfspr r3,HID0 blr -_GLOBAL(_get_ICTC) - mfspr r3,ICTC - blr - -_GLOBAL(_set_ICTC) - mtspr ICTC,r3 - blr - - - _GLOBAL(cvt_fd) lfd 0,-4(r5) /* load up fpscr value */ mtfsf 0xff,0 @@ -679,15 +487,6 @@ stfd 0,-4(r5) blr -_GLOBAL(__clear_msr_me) - mfmsr r0 /* Get current interrupt state */ - lis r3,0 - ori r3,r3,MSR_ME - andc r0,r0,r3 /* Clears bit in (r4) */ - sync /* Some chip revs have problems here */ - mtmsrd r0 /* Update machine state */ - blr - /* * Create a kernel thread * kernel_thread(fn, arg, flags) @@ -704,7 +503,9 @@ ld r5,PACACURRENT(r5) /* Get 'current' */ li r0,0 /* clear out p->thread.regs */ std r0,THREAD+PT_REGS(r5) /* since we don't have user ctx */ - + std r0,THREAD+THREAD_FLAGS(r5) + + ld r2,8(r6) ld r6,0(r6) mtlr r6 /* fn addr in lr */ mr r3,r4 /* load arg and call fn */ @@ -713,38 +514,6 @@ li r3,0 sc -/* - * This routine is just here to keep GCC happy - sigh... - */ -_GLOBAL(__main) - blr - -#define SYSCALL(name) \ -_GLOBAL(name) \ - li r0,__NR_##name; \ - sc; \ - bnslr; \ - /* MIKEC: Is errno a 32-bit int or a 64-bit long ? */\ - LOADBASE(r4,errno); \ - stw r3,errno@l(r4); \ - li r3,-1; \ - blr - -#define __NR__exit __NR_exit - -SYSCALL(sync) -SYSCALL(setsid) -SYSCALL(write) -SYSCALL(dup) -SYSCALL(execve) -SYSCALL(open) -SYSCALL(close) -SYSCALL(waitpid) -SYSCALL(fork) -SYSCALL(delete_module) -SYSCALL(_exit) -SYSCALL(lseek) -SYSCALL(read) #ifdef CONFIG_BINFMT_ELF32 /* Why isn't this a) automatic, b) written in 'C'? */ .data @@ -899,7 +668,7 @@ .llong .sys32_writev .llong .sys32_getsid .llong .sys_fdatasync - .llong .sys_sysctl + .llong .sys32_sysctl .llong .sys_mlock /* 150 */ .llong .sys_munlock .llong .sys32_mlockall @@ -977,7 +746,7 @@ .llong .sys_unlink /* 10 */ .llong .sys_execve .llong .sys_chdir - .llong .sys_time + .llong .sys64_time .llong .sys_mknod .llong .sys_chmod /* 15 */ .llong .sys_lchown diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/mk_defs.c linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c --- linux-2.4.9-ac10/arch/ppc64/kernel/mk_defs.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c Mon Oct 1 11:15:28 2001 @@ -69,13 +69,17 @@ DEFINE(PACASTABREAL, offsetof(struct Paca, xStab_data.real)); DEFINE(PACASTABVIRT, offsetof(struct Paca, xStab_data.virt)); DEFINE(PACAR1, offsetof(struct Paca, xR1)); - DEFINE(PACAR21, offsetof(struct Paca, xR21)); - DEFINE(PACAR22, offsetof(struct Paca, xR22)); - DEFINE(PACACCR, offsetof(struct Paca, xCCR)); DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr)); - DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); DEFINE(PACATOC, offsetof(struct Paca, xTOC)); + DEFINE(PACAEXCSP, offsetof(struct Paca, exception_sp)); + DEFINE(PACAHRDWINTSTACK, offsetof(struct Paca, xHrdIntStack)); + DEFINE(PACAPROCENABLED, offsetof(struct Paca, xProcEnabled)); + DEFINE(PACAHRDWINTCOUNT, offsetof(struct Paca, xHrdIntCount)); + DEFINE(PACADEFAULTDECR, offsetof(struct Paca, default_decr)); + DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); DEFINE(LPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(PACAREGSAV, offsetof(struct Paca, xRegSav)); + DEFINE(PACAEXC, offsetof(struct Paca, exception_stack)); DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); DEFINE(LPPACAANYINT, offsetof(struct ItLpPaca, xIntDword.xAnyInt)); @@ -132,6 +136,7 @@ DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3)); DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result)); DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap)); + DEFINE(SOFTE, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, softe)); DEFINE(CLONE_VM, CLONE_VM); return 0; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/open_pic.c linuxppc64_2_4/arch/ppc64/kernel/open_pic.c --- linux-2.4.9-ac10/arch/ppc64/kernel/open_pic.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/open_pic.c Fri Aug 31 15:48:52 2001 @@ -87,10 +87,10 @@ */ #ifdef CONFIG_SMP #define THIS_CPU Processor[cpu] -#define DECL_THIS_CPU int cpu = cpu_hw_index[smp_processor_id()] +#define DECL_THIS_CPU int cpu = hard_smp_processor_id() #define CHECK_THIS_CPU check_arg_cpu(cpu) #else -#define THIS_CPU Processor[cpu_hw_index[0]] +#define THIS_CPU Processor[hard_smp_processor_id()] #define DECL_THIS_CPU #define CHECK_THIS_CPU #endif /* CONFIG_SMP */ @@ -358,7 +358,7 @@ /* SIOint (8259 cascade) is special */ if (offset) { openpic_initirq(0, 8, offset, 1, 1); - openpic_mapirq(0, 1<>= 1) - mask |= (cpumask & 1) << cpu_hw_index[i]; + mask |= (cpumask & 1) << get_hard_smp_processor_id(i); return mask; } @@ -595,7 +595,7 @@ { #ifdef CONFIG_IRQ_ALL_CPUS int i; - u32 msk = 1 << cpu_hw_index[smp_processor_id()]; + u32 msk = 1 << hard_smp_processor_id(); #endif spin_lock(&openpic_setup_lock); diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pSeries_pci.c linuxppc64_2_4/arch/ppc64/kernel/pSeries_pci.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pSeries_pci.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pSeries_pci.c Sat Sep 29 13:40:48 2001 @@ -46,18 +46,7 @@ #include "open_pic.h" #include "pci.h" -/* Supporting macros *********************************************/ -#define TESTBIT(value,bits) ((value&bits) == bits) -#define TRUE 1 -#define FALSE 0 - -extern struct pci_controller* hose_head; -extern struct pci_controller** hose_tail; -extern struct Naca *naca; -extern struct pci_ops rtas_pci_ops; -extern struct pci_ops ibm_phb_pci_ops; extern struct device_node *allnodes; -extern unsigned long phb_tce_table_init(struct pci_controller *phb); /******************************************************************* * Forward declares of prototypes. @@ -65,33 +54,72 @@ unsigned long find_and_init_phbs(void); struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ; void pSeries_pcibios_fixup(void); -void pci_build_bar_resources(struct pci_dev* Pci_Dev, int BarCount); -void pci_build_rom_resources(struct pci_dev* Pci_Dev); -void pci_set_BARS(struct pci_dev* Pci_Dev); +static int rtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval); /********************************************************************************** * * pSeries I/O Operations to access the PCI configuration space. * **********************************************************************************/ -#define RTAS_PCI_READ_OP(size, type, nbytes) \ -int __chrp \ -rtas_read_config_##size(struct pci_dev *dev, int offset, type val) { \ - unsigned long ReturnValue, ConfigAddr; \ - int ReturnCode; \ - ConfigAddr = ((dev->bus->number&0x0000FF) << 16) | (dev->devfn << 8) | (offset & 0xff); \ - ReturnCode = call_rtas("read-pci-config", 2, 2, &ReturnValue, ConfigAddr, nbytes);\ - *val = ReturnValue; \ - return ReturnCode; \ -} -#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ -int __chrp \ -rtas_write_config_##size(struct pci_dev *dev, int offset, type val) { \ - unsigned long ConfigAddr; \ - int ReturnCode; \ - ConfigAddr = ((dev->bus->number&0x0000FF) << 16) | (dev->devfn << 8) | (offset & 0xff); \ - ReturnCode = call_rtas("write-pci-config", 3, 1, NULL, ConfigAddr, nbytes, (ulong)val); \ - return ReturnCode; \ +#define RTAS_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas_read_config_##size(struct device_node *dn, int offset, type val) { \ + unsigned long returnval = ~0L; \ + unsigned long buid; \ + unsigned int addr; \ + int ret; \ + \ + if (dn == NULL) { \ + ret = -2; \ + } else if (dn->status) { \ + ret = -1; \ + } else { \ + addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \ + buid = dn->phb->buid; \ + if (buid) { \ + ret = call_rtas("ibm,read-pci-config", 4, 2, &returnval, addr, buid >> 32, buid & 0xffffffff, nbytes); \ + if (ret == -4) \ + ret = rtas_fake_read(dn, offset, nbytes, &returnval); \ + } else { \ + ret = call_rtas("read-pci-config", 2, 2, &returnval, addr, nbytes); \ + } \ + } \ + *val = returnval; \ + return ret; \ +} \ +int __chrp \ +rtas_pci_read_config_##size(struct pci_dev *dev, int offset, type val) { \ + struct device_node *dn = pci_device_to_OF_node(dev); \ + int ret = rtas_read_config_##size(dn, offset, val); \ + /* udbg_printf("read bus=%x, devfn=%x, ret=%d phb=%lx, dn=%lx\n", dev->bus->number, dev->devfn, ret, dn ? dn->phb : 0, dn); */ \ + return ret ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; \ +} + +#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas_write_config_##size(struct device_node *dn, int offset, type val) { \ + unsigned long buid; \ + unsigned int addr; \ + int ret; \ + \ + if (dn == NULL) { \ + ret = -2; \ + } else if (dn->status) { \ + ret = -1; \ + } else { \ + buid = dn->phb->buid; \ + addr = (dn->busno << 16) | (dn->devfn << 8) | offset; \ + if (buid) { \ + ret = call_rtas("ibm,write-pci-config", 5, 1, NULL, addr, buid >> 32, buid & 0xffffffff, nbytes, (ulong) val); \ + } else { \ + ret = call_rtas("write-pci-config", 3, 1, NULL, addr, nbytes, (ulong)val); \ + } \ + } \ + return ret; \ +} \ +int __chrp \ +rtas_pci_write_config_##size(struct pci_dev *dev, int offset, type val) { \ + return rtas_write_config_##size(pci_device_to_OF_node(dev), offset, val); \ } RTAS_PCI_READ_OP(byte, u8 *, 1) @@ -102,73 +130,54 @@ RTAS_PCI_WRITE_OP(dword, u32, 4) struct pci_ops rtas_pci_ops = { - rtas_read_config_byte, - rtas_read_config_word, - rtas_read_config_dword, - rtas_write_config_byte, - rtas_write_config_word, - rtas_write_config_dword, - pci_read_bar_registers, - pci_read_irq_line + rtas_pci_read_config_byte, + rtas_pci_read_config_word, + rtas_pci_read_config_dword, + rtas_pci_write_config_byte, + rtas_pci_write_config_word, + rtas_pci_write_config_dword, }; -/********************************************************************************** - * - * pSeries I/O Operations to access the PCI configuration space. - * This is the support for Large Systems(>256 buses). - * - **********************************************************************************/ -#define RTAS64_PCI_READ_OP(size, type, nbytes) \ -int __chrp \ -rtas64_read_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - struct pci_controller *hose = dev->sysdata; \ - unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ - | (((dev->bus->number) & 0xff) << 16); \ - unsigned long ret = ~0UL; \ - int rval; \ - int buidhi = hose->buid >> 32; \ - int buidlo = hose->buid & 0xffffffff; \ - \ - rval = call_rtas("ibm,read-pci-config", 4, 2, &ret, \ - addr, buidhi, buidlo, nbytes); \ - *val = ret; \ - /* udbg_printf("%08x %08x SYM Read Config %d\n",addr, ret,rval); */ \ - return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ -} +/* + * Handle the case where rtas refuses to do a pci config read. + * This currently only happens with some PHBs in which case we totally fake + * out the values (and call it a speedwagaon -- something we could look up in the + * device tree). + */ +static int +rtas_fake_read(struct device_node *dn, int offset, int nbytes, unsigned long *returnval) +{ + char *device_type = (char *)get_property(dn, "device_type", 0); + u32 *class_code = (u32 *)get_property(dn, "class-code", 0); -#define RTAS64_PCI_WRITE_OP(size, type, nbytes) \ -int __chrp \ -rtas64_write_config_##size(struct pci_dev *dev, int offset, type val) \ -{ \ - struct pci_controller *hose = dev->sysdata; \ - unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ - | (((dev->bus->number) & 0xff) << 16); \ - int rval; \ - int buidhi = hose->buid >> 32; \ - int buidlo = hose->buid & 0xffffffff; \ - \ - rval = call_rtas("ibm,write-pci-config", 5, 1, NULL, \ - addr, buidhi, buidlo, nbytes, (ulong)val); \ - /* udbg_printf("%08x %08x SYM Write Config %d\n",addr, val,rval); */ \ - return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ -} + *returnval = ~0; /* float by default */ -RTAS64_PCI_READ_OP(byte, u8 *, 1) -RTAS64_PCI_READ_OP(word, u16 *, 2) -RTAS64_PCI_READ_OP(dword, u32 *, 4) -RTAS64_PCI_WRITE_OP(byte, u8, 1) -RTAS64_PCI_WRITE_OP(word, u16, 2) -RTAS64_PCI_WRITE_OP(dword, u32, 4) - -struct pci_ops rtas64_pci_ops = { - rtas64_read_config_byte, - rtas64_read_config_word, - rtas64_read_config_dword, - rtas64_write_config_byte, - rtas64_write_config_word, - rtas64_write_config_dword -}; + /* udbg_printf("rtas_fake_read dn=%p, offset=0x%02x, nbytes=%d, device_type=%s\n", dn, offset, nbytes, device_type ? device_type : ""); */ + if (class_code || (device_type && strcmp(device_type, "pci") != 0)) + return -3; /* Not a phb. We don't fake anything else (yet) */ + + if (nbytes == 1) { + if (offset == PCI_HEADER_TYPE) + *returnval = 0x80; /* multifunction */ + else if (offset == PCI_INTERRUPT_PIN || offset == PCI_INTERRUPT_LINE) + *returnval = 0; + } else if (nbytes == 2) { + if (offset == PCI_SUBSYSTEM_VENDOR_ID || offset == PCI_SUBSYSTEM_ID) + *returnval = 0; + else if (offset == PCI_COMMAND) + *returnval = PCI_COMMAND_PARITY|PCI_COMMAND_MASTER|PCI_COMMAND_MEMORY; + } else if (nbytes == 4) { + if (offset == PCI_VENDOR_ID) + *returnval = 0x1014 | (0x102 << 16); /* a phb */ + else if (offset == PCI_REVISION_ID) + *returnval = PCI_CLASS_BRIDGE_HOST << 16; /* revs are zero */ + else if ((offset >= PCI_BASE_ADDRESS_0 && offset <= PCI_BASE_ADDRESS_5) || offset == PCI_ROM_ADDRESS) + *returnval = 0; + } + + /* udbg_printf("rtas_fake_read returning value %lx\n", *returnval); */ + return 0; +} /****************************************************************** @@ -204,216 +213,6 @@ PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n",Pci_Dev->slot_name,Pci_Dev->irq); return 0; } -/****************************************************************** - * pci_set_BARS - * - * Sets the IOA BAR registers from the values from the register - * values found in the OpenFirmware node for the device. Or in - * the future, assigns space in phb and sets the values. - ******************************************************************/ -void -pci_set_BARS(struct pci_dev* Pci_Dev) { - int AddrIndex; - struct device_node *Device_Node = pci_device_to_OF_node(Pci_Dev); - - if(Device_Node == 0 || Device_Node->n_addrs == 0 || Device_Node->addrs == NULL) { - PPCDBG(PPCDBG_BUSWALK, "\tDevice Node was not found or no defined bars\n"); - return; - } - PPCDBG(PPCDBG_BUSWALK, "\tSet Bar Regs(%d)\n",Device_Node->n_addrs); - for(AddrIndex = 0; AddrIndex < Device_Node->n_addrs; ++AddrIndex) { - int PciReg = (Device_Node->addrs[AddrIndex].space & 0x000000FF); - u32 PciBar = Device_Node->addrs[AddrIndex].address; - int BarSze = Device_Node->addrs[AddrIndex].size; - - PPCDBG(PPCDBG_BUSWALK, "\tPciBar 0x%02X, 0x%08X, 0x%08X\n",PciReg,PciBar,BarSze); - pci_write_config_dword(Pci_Dev, PciReg,PciBar); - } -} -/****************************************************************** - * - * pci_build_bar_registers - * - * This function will build the resources based on the information - * that is extracted from the BAR register. The original BAR value - * is saved and restored. - * - * Note: This is pSeries brand specific code. - * In the future, this code will be setting the initial BAR - * value and handing out the memory space in the PHB. - ******************************************************************/ -void -pci_build_bar_resources(struct pci_dev* Pci_Dev, int Count) { - int BarRegister = PCI_BASE_ADDRESS_0; - int EndingBar = BarRegister + (Count*4); - int ResourceIndex = 0; - PPCDBG(PPCDBG_BUSWALK, "\npci_read_bar_registers %s\n",Pci_Dev->slot_name); - - /************************************************************** - * Read Bars until the ending bar has been read. - **************************************************************/ - for(BarRegister = PCI_BASE_ADDRESS_0;BarRegister <= EndingBar; BarRegister += 4) { - struct resource* Resource = &Pci_Dev->resource[ResourceIndex]; - u32 BarSaveArea, BarSizeBits, BarSize; - u64 BarStart; - /********************************************************** - * Save the original bar value and check for success. - **********************************************************/ - if((pci_read_config_dword( Pci_Dev, BarRegister, &BarSaveArea) != 0) || - (BarSaveArea == 0xFFFFFFFF )) { - BarRegister += 4; - continue; - } - /********************************************************** - * Write all ones to register to get the size of area. - **********************************************************/ - pci_write_config_dword(Pci_Dev, BarRegister, 0xFFFFFFFF); - pci_read_config_dword( Pci_Dev, BarRegister, &BarSizeBits); - pci_write_config_dword(Pci_Dev, BarRegister, BarSaveArea); - - /********************************************************** - * Error reading bar(all ones back) or unimplemented BAR(zero) - **********************************************************/ - if(( BarSizeBits == 0xFFFFFFFF ) || BarSizeBits == 0 ) { - BarRegister += 4; - continue; - } - Resource->name = Pci_Dev->name; - /********************************************************** - * Test and read Io Space, It is always 32 bits. - **********************************************************/ - if(TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_SPACE_IO) == TRUE) { - BarSize = (~(BarSizeBits&PCI_BASE_ADDRESS_IO_MASK)) +1; - Resource->flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; - Resource->start = BarSaveArea & PCI_BASE_ADDRESS_IO_MASK; - Resource->end = Resource->start + BarSize - 1; - ++ResourceIndex; - } - /********************************************************** - * Memory Space, could be 32 bits or 64 bits - **********************************************************/ - else { - Resource->flags = IORESOURCE_MEM; - if( TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_MEM_PREFETCH) == TRUE) { - Resource->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; - } - BarStart = BarSaveArea &PCI_BASE_ADDRESS_MEM_MASK; - BarSize = ~((BarSizeBits)&PCI_BASE_ADDRESS_MEM_MASK)+1; - /********************************************************** - * 64 bit register, read next register to get the high 32 - * bits. PCI Spec says to write both and THEN read. - **********************************************************/ - if( TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_MEM_TYPE_64) == TRUE) { - u64 High64Bits; - u32 BarHigh64Save; - Resource->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; - BarRegister += 4; /* Index to next Bar */ - pci_read_config_dword( Pci_Dev, BarRegister, &BarHigh64Save); - High64Bits = BarHigh64Save; - BarStart += (High64Bits<< 32); - pci_write_config_dword(Pci_Dev, BarRegister-4,0xFFFFFFFF); - pci_write_config_dword(Pci_Dev, BarRegister, 0xFFFFFFFF); - pci_read_config_dword( Pci_Dev, BarRegister-4,&BarSizeBits); - BarSize = ~((BarSizeBits)&PCI_BASE_ADDRESS_MEM_MASK)+1; - pci_read_config_dword( Pci_Dev, BarRegister, &BarSizeBits); - High64Bits = ~(BarSizeBits); - BarSize += (High64Bits << 32); - pci_write_config_dword(Pci_Dev, BarRegister-4,BarSaveArea); - pci_write_config_dword(Pci_Dev, BarRegister, BarHigh64Save); - ++ResourceIndex; /* Skip next resource */ - } - /********************************************************** - * Set resource fields with values. - **********************************************************/ - Resource->start = BarStart; - Resource->end = BarStart + BarSize - 1; - ++ResourceIndex; - } - } - PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(Pci_Dev) ); -} -/****************************************************************** - * - * Read the rom register - * - ******************************************************************/ -void -pci_build_rom_resources(struct pci_dev* Pci_Dev) { - u32 RomSaveArea, RomSizeBits; - u64 RomSize = 0; - struct resource* Resource = &Pci_Dev->resource[PCI_ROM_RESOURCE]; - PPCDBG(PPCDBG_BUSWALK, "\tpci_read_bar_Rom \n"); - - pci_read_config_dword( Pci_Dev, PCI_ROM_ADDRESS, &RomSaveArea); - pci_write_config_dword(Pci_Dev, PCI_ROM_ADDRESS, 0XFFFFFFFF); - pci_read_config_dword( Pci_Dev, PCI_ROM_ADDRESS, &RomSizeBits); - pci_write_config_dword(Pci_Dev, PCI_ROM_ADDRESS, RomSaveArea); - - if( (RomSizeBits&PCI_ROM_ADDRESS_ENABLE) == PCI_ROM_ADDRESS_ENABLE) { - RomSize = ~(RomSizeBits) & PCI_ROM_ADDRESS_MASK; - if(RomSize > 0) { - Resource->name = Pci_Dev->name; - Resource->flags = IORESOURCE_MEM | IORESOURCE_READONLY | IORESOURCE_PREFETCH; - Resource->start = RomSaveArea; - Resource->end = Resource->start + RomSize - 1; - } - } -} -/****************************************************************** - * - * pci_read_bar_registers - * - * This function is the hook from the independant code to the arch - * dependant code to set and read the BAR registers. - * - * Note: This is pSeries brand specific code. - * In the future, this code will be setting the initial BAR - * value and handing out the PBH memory space. - ******************************************************************/ -int -pci_read_bar_registers(struct pci_dev* Pci_Dev, int Count, int RomFlag) { - u16 Pci_Command_Register_Save = 0; - u16 Pci_Mask_Reg; - /************************************************************** - * If Io or Memory is enabled, disable while the BARs are being - * changed to avoid any side affects. - **************************************************************/ - if(( pci_read_config_word( Pci_Dev, PCI_COMMAND, &Pci_Command_Register_Save) != 0 ) || - (Pci_Command_Register_Save == (u16)0xFFFF) ) { - printk("PCI: Device %s Read Command Failed.\n",Pci_Dev->slot_name); - PPCDBG(PPCDBG_BUSWALK,"\tDevice %s Read Command Failed.\n",Pci_Dev->slot_name); - return -1; - } - Pci_Mask_Reg = Pci_Command_Register_Save &(PCI_COMMAND_IO|PCI_COMMAND_MEMORY); - if( Pci_Mask_Reg != 0) { - u16 ResetCmdReg = Pci_Command_Register_Save & (~Pci_Mask_Reg); - pci_write_config_word( Pci_Dev, PCI_COMMAND, ResetCmdReg); - } - else { - Pci_Command_Register_Save = 0; - } - /************************************************************** - * Do the base BARS. - **************************************************************/ - pci_build_bar_resources(Pci_Dev, Count); - - /************************************************************** - * Rom Flag on, read ROM BARS. - **************************************************************/ - if(RomFlag != 0) { - pci_build_rom_resources(Pci_Dev); - } - - /************************************************************** - * If the Command Register was modifed, restore it. - **************************************************************/ - if(Pci_Command_Register_Save != 0) { - pci_write_config_word( Pci_Dev, PCI_COMMAND, Pci_Command_Register_Save); - PPCDBG(PPCDBG_BUSWALK,"\tPCI_COMMAND Register Restored 0x%04X\n", - Pci_Command_Register_Save); - } - return 0; -} /****************************************************************** * Find all PHBs in the system and initialize a set of data @@ -432,7 +231,6 @@ struct resource *res; unsigned int memno, rlen, i, index; unsigned int *opprop; - PPCDBG(PPCDBG_PHBINIT, "find_and_init_phbs\n"); if(naca->interrupt_controller == IC_OPEN_PIC) { @@ -449,6 +247,11 @@ return(-1); } + if (find_type_devices("isa")) { + isa_io_limit = 0; /* allow all ISA ports down to zero. */ + PPCDBG(PPCDBG_PHBINIT, "\tFound an ISA bus.\n"); + } + index = 0; /****************************************************************** @@ -476,10 +279,10 @@ range_stride = this_addr_count + root_addr_size_words + this_addr_size_words; memno = 0; - phb->io_base_phys = 0; + phb->io_base_phys = 0; ranges = (unsigned int *) get_property(Pci_Node, "ranges", &rlen); - PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen); + PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen); for(i = 0; i < (rlen/sizeof(*ranges)); i+=range_stride) { /* Put the PCI addr part of the current element into a @@ -518,21 +321,26 @@ res = NULL; switch ((range.child_addr.a_hi >> 24) & 0x3) { case 1: /* I/O space */ + PPCDBG(PPCDBG_PHBINIT, "\tIO Space\n"); phb->io_base_phys = range.parent_addr; + phb->io_base_virt = ioremap(phb->io_base_phys, range.size); + if (!isa_io_base) + isa_io_base = (unsigned long)phb->io_base_virt; res = &phb->io_resource; + res->name = Pci_Node->full_name; res->flags = IORESOURCE_IO; - if (isa_io_base == 0) { - isa_io_base = (unsigned long) - ioremap(phb->io_base_phys, range.size); - PPCDBG(PPCDBG_PHBINIT, "\tisa_io_base = 0x%lx\n", isa_io_base); - } + res->start = ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo)); + res->start += (unsigned long)phb->io_base_virt - isa_io_base; + res->end = res->start + range.size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; phb->pci_io_offset = range.parent_addr - ((((unsigned long) range.child_addr.a_mid) << 32) | (range.child_addr.a_lo)); PPCDBG(PPCDBG_PHBINIT, "\tpci_io_offset = 0x%lx\n", phb->pci_io_offset); - break; case 2: /* mem space */ PPCDBG(PPCDBG_PHBINIT, "\tMem Space\n"); @@ -542,37 +350,32 @@ (range.child_addr.a_lo)); PPCDBG(PPCDBG_PHBINIT, "\tpci_mem_offset = 0x%lx\n", phb->pci_mem_offset); - res = &(phb->mem_resources[memno]); - res->flags = IORESOURCE_MEM; - ++memno; + if (memno < sizeof(phb->mem_resources)/sizeof(phb->mem_resources[0])) { + res = &(phb->mem_resources[memno]); + ++memno; + res->name = Pci_Node->full_name; + res->flags = IORESOURCE_MEM; + res->start = range.parent_addr; + res->end = range.parent_addr + range.size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } break; } - if (res) { - res->name = Pci_Node->full_name; - res->start = range.parent_addr; - res->end = range.parent_addr + range.size - 1; - res->parent = NULL; - res->sibling = NULL; - res->child = NULL; - } } PPCDBG(PPCDBG_PHBINIT, "\tphb->io_base_phys = 0x%lx\n", phb->io_base_phys); PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%lx\n", phb->pci_mem_offset); - if(naca->interrupt_controller == IC_OPEN_PIC) { - if(root_addr_size_words == 1) { - openpic_setup_ISU(index, opprop[index+1]); - } else { - openpic_setup_ISU(index, - ((unsigned long)opprop[(index+1)*2]) << 32 | - opprop[(index+1)*2+1]); - } - } - + if(naca->interrupt_controller == IC_OPEN_PIC) { + int addr = root_addr_size_words * (index + 2) - 1; + openpic_setup_ISU(index, opprop[addr]); + } index++; } + pci_devs_phb_init(); return 0; /*Success */ } @@ -587,7 +390,8 @@ struct pci_controller *phb; unsigned int *ui_ptr = NULL, len; struct reg_property64 reg_struct; - int *bus_range; + int *bus_range; + int *buid_vals; PPCDBG(PPCDBG_PHBINIT, "alloc_phb: %s\n", dev->full_name); PPCDBG(PPCDBG_PHBINIT, "\tdev = 0x%lx\n", dev); @@ -644,19 +448,25 @@ phb = pci_alloc_pci_controller("PHB SW",phb_type_speedwagon); if(phb == NULL) return NULL; - phb->cfg_addr = (volatile unsigned long *) - ioremap(reg_struct.address + 0x140, PAGE_SIZE); - phb->cfg_data = (char*)(phb->cfg_addr - 0x02); /* minus is correct */ - phb->phb_regs = (volatile unsigned long *) - ioremap(reg_struct.address, PAGE_SIZE); + if (_machine == _MACH_pSeries) { + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0x140, PAGE_SIZE); + phb->cfg_data = (char*)(phb->cfg_addr - 0x02); /* minus is correct */ + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address, PAGE_SIZE); + /* Speedwagon's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + PPCDBG(PPCDBG_PHBINIT, "\tmapping chip_regs from 0x%lx -> 0x%lx\n", + reg_struct.address & 0xfffff, phb->chip_regs); + } else { + phb->cfg_addr = NULL; + phb->cfg_data = NULL; + phb->phb_regs = NULL; + phb->chip_regs = NULL; + } phb->local_number = ((reg_struct.address >> 12) & 0xf) - 0x8; - - /* Speedwagon's register file is 1 MB in size. */ - phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), - 0x100000); - PPCDBG(PPCDBG_PHBINIT, "\tmapping chip_regs from 0x%lx -> 0x%lx\n", - reg_struct.address & 0xfffff, phb->chip_regs); /*************************************************************** * Trying to build a known just gets the code in trouble. ***************************************************************/ @@ -678,17 +488,34 @@ ***************************************************************/ phb->first_busno = bus_range[0]; phb->last_busno = bus_range[1]; - //phb->first_busno = (phb->global_number <<8) + bus_range[0]; - //phb->last_busno = (phb->global_number <<8) + bus_range[1]; phb->arch_data = dev; - if( Pci_Large_Bus_System == 0 ) phb->ops = &rtas_pci_ops; - else phb->ops = &rtas64_pci_ops; + phb->ops = &rtas_pci_ops; - /*************************************************************** - * Build tce table for phb - ***************************************************************/ - phb_tce_table_init(phb); + buid_vals = (int *) get_property(dev, "ibm,fw-phb-id", &len); + if (buid_vals == NULL || len < 2 * sizeof(int)) { + phb->buid = 0; + } else { + /* Big bus system. These systems start new bus numbers under + * each phb. Until pci domains are standard, we depend on a + * patch which makes bus numbers ints and we shift the phb + * number into the upper bits. + */ + struct pci_bus check; + if (sizeof(check.number) == 1 || sizeof(check.primary) == 1 || + sizeof(check.secondary) == 1 || sizeof(check.subordinate) == 1) { + udbg_printf("pSeries_pci: this system has large bus numbers and the kernel was not\n" + "built with the patch that fixes include/linux/pci.h struct pci_bus so\n" + "number, primary, secondary and subordinate are ints.\n"); + panic("pSeries_pci: this system has large bus numbers and the kernel was not\n" + "built with the patch that fixes include/linux/pci.h struct pci_bus so\n" + "number, primary, secondary and subordinate are ints.\n"); + } + phb->buid = (((unsigned long)buid_vals[0]) << 32UL) | + (((unsigned long)buid_vals[1]) & 0xffffffff); + phb->first_busno += (phb->global_number << 8); + phb->last_busno += (phb->global_number << 8); + } /* Dump PHB information for Debug */ PPCDBGCALL(PPCDBG_PHBINIT,dumpPci_Controller(phb) ); @@ -699,8 +526,7 @@ void fixup_resources(struct pci_dev *dev) { int i; - unsigned long size; - struct pci_controller* phb = (struct pci_controller *)dev->sysdata; + struct pci_controller* phb = PCI_GET_PHB_PTR(dev); PPCDBG(PPCDBG_PHBINIT, "fixup_resources:\n"); PPCDBG(PPCDBG_PHBINIT, "\tphb = 0x%016LX\n", phb); @@ -712,7 +538,7 @@ if(phb == NULL) return; - for (i = 0; i < 6; ++i) { + for (i = 0; i < DEVICE_COUNT_RESOURCE; ++i) { PPCDBG(PPCDBG_PHBINIT, "\tdevice %x.%x[%d] (flags %x) [%lx..%lx]\n", dev->bus->number, dev->devfn, i, dev->resource[i].flags, @@ -724,20 +550,20 @@ } if (dev->resource[i].flags & IORESOURCE_IO) { - dev->resource[i].start += phb->pci_io_offset; - dev->resource[i].end += phb->pci_io_offset; - size = dev->resource[i].end - dev->resource[i].start; - dev->resource[i].start = - ((unsigned long) ioremap(dev->resource[i].start, - size)) - isa_io_base; - dev->resource[i].end = dev->resource[i].start + size; - + unsigned long offset = (unsigned long) phb->io_base_virt - isa_io_base; + dev->resource[i].start += offset; + dev->resource[i].end += offset; PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx (%lx) .. %lx (%lx)]\n", - dev->resource[i].start, ___pa(dev->resource[i].start + isa_io_base), - dev->resource[i].end, ___pa(dev->resource[i].end + isa_io_base)); + dev->resource[i].start, 0/*___pa(dev->resource[i].start + isa_io_base)*/, + dev->resource[i].end, 0/* ___pa(dev->resource[i].end + isa_io_base)*/); } else if (dev->resource[i].flags & IORESOURCE_MEM) { - dev->resource[i].start += phb->pci_mem_offset; - dev->resource[i].end += phb->pci_mem_offset; + if (dev->resource[i].start == 0) { + /* Bogus. Probably an unused bridge. */ + dev->resource[i].end = 0; + } else { + dev->resource[i].start += phb->pci_mem_offset; + dev->resource[i].end += phb->pci_mem_offset; + } PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx..%lx]\n", dev->resource[i].start, dev->resource[i].end); @@ -760,6 +586,7 @@ pci_assign_all_busses = 0; pci_for_each_dev(dev) { + pci_read_irq_line(dev); PPCDBGCALL(PPCDBG_PHBINIT, dumpPci_Dev(dev) ); } @@ -787,89 +614,6 @@ return NULL; } -/*********************************************************************** - * - * scan_OF_childs_for_device - * - * The function is a helper function for the pci_device_to_OF_node. It - * walks down the passed node, looking for a node entry that matches the - * requested bus and device function. NOTE: If a bridge is found in the - * scan, it will recurse this the function to to scan that bridge looking - * for a match. If none found, the return will continue down the orginal - * tree. - * - * Return: - * Node matching the bus and devfn passed or NULL. - ***********************************************************************/ -static struct device_node* -scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) -{ - struct device_node* CurrentNode; /* Current node being scanned. */ - struct device_node* DeviceNode; /* Node of Device */ - u32* Register; /* Pointer to Register Array */ - u32* Class_Code; /* Pointer to ClassCode property */ - CurrentNode = node; - DeviceNode = NULL; - while(CurrentNode != NULL && DeviceNode == NULL) { - u32 IoaAddress; - u8 IoaBus, IoaDevFn; - - Register = (unsigned int *) get_property(CurrentNode,"reg", 0); - if(Register != NULL) { - /* 1st register entry is the Ioa Address = 00BBSS00 */ - IoaAddress = Register[0]; - IoaBus = (IoaAddress & 0x00FF0000) >> 16; - IoaDevFn = (IoaAddress & 0x0000FF00) >> 8; - if( (IoaBus == bus) && (IoaDevFn == dev_fn ) ) { - DeviceNode = CurrentNode; - return DeviceNode; - } - } - /*************************************************************** - * check for a bridge, if so scan the branch of the tree for a match. - ***************************************************************/ - Class_Code = (unsigned int*) get_property(CurrentNode,"class-code", 0); - if(Class_Code != NULL) { - u32 PciClassCode = ((*Class_Code)&0x00FFFF00)>>8; - - if(( PciClassCode == PCI_CLASS_BRIDGE_PCI) || - ( PciClassCode == PCI_CLASS_BRIDGE_CARDBUS) ) { - - PPCDBG(PPCDBG_BUSWALK,"\tScan OF behind bridge\n"); - DeviceNode = scan_OF_childs_for_device(CurrentNode->child, bus, dev_fn); - if(DeviceNode != NULL) { - return DeviceNode; - } - } - } - /*************************************************************** - * Try the next node. - ***************************************************************/ - CurrentNode = CurrentNode->sibling; - } - return NULL; -} -/*********************************************************************** - * pci_device_to_OF_node - * - * This function Finds the Open Firmware node for the passed in pci_dev. - * It starts at the Phb's node for the device and calls the - * scan_OF_childs_for_device to looking for the matching entry. - * - * Return: - * Node matching the device or NULL if it was not found. - ***********************************************************************/ -struct device_node* -pci_device_to_OF_node(struct pci_dev* Pci_Dev) -{ - struct pci_controller* Phb; - struct device_node* PhbNode; - struct device_node* DeviceNode; - Phb = PCI_GET_PHB_PTR(Pci_Dev); - PhbNode = (struct device_node *) Phb->arch_data; - DeviceNode = scan_OF_childs_for_device(PhbNode->child, PCI_GET_BUS_NUMBER(Pci_Dev), Pci_Dev->devfn); - return DeviceNode; -} /*********************************************************************** * find_floppy(void) * @@ -882,20 +626,15 @@ * and maybe that is okay. ***********************************************************************/ struct pci_dev* -find_floppy() { - struct device_node *FloppyParent, *FloppyNode; - struct pci_dev *floppy_dev = NULL; - int *Register; - - FloppyNode = find_type_devices("fdc"); - if(FloppyNode != NULL && FloppyNode->parent != NULL) { - FloppyParent = FloppyNode->parent; - Register = (unsigned int *)get_property(FloppyParent,"reg", 0); - if(Register != NULL) { - u8 IoaBus = (Register[0] & 0x00FF0000) >> 16; - u8 IoaDevFn = (Register[0] & 0x0000FF00) >> 8; - floppy_dev = pci_find_slot(IoaBus, IoaDevFn); - } +find_floppy(void) { + struct device_node *floppy_dn; + struct pci_dev *floppy_dev = NULL; + int *reg; + + floppy_dn = find_type_devices("fdc"); + if(floppy_dn && floppy_dn->parent) { + if ((reg = (unsigned int *)get_property(floppy_dn->parent,"reg", 0)) != NULL) + floppy_dev = pci_find_slot((reg[0] & 0x00ff0000) >> 16, (reg[0] & 0x0000ff00) >> 8); } PPCDBG(PPCDBG_BUSWALK,"\tFloppy pci_dev\n"); PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(floppy_dev) ); @@ -912,18 +651,21 @@ pSeries_pcibios_init(void) { PPCDBG(PPCDBG_PHBINIT, "\tppc64_pcibios_init Entry.\n"); - if(get_property(find_path_device("/rtas"),"read-pc-config",NULL) != NULL) { - PPCDBG(PPCDBG_PHBINIT, "\tFound: read-pc-config\n"); - } - else PPCDBG(PPCDBG_PHBINIT, "\tNOT Found: read-pc-config\n"); - - - if(get_property(find_path_device("/rtas"),"ibm,read-pc-config",NULL) != NULL) { - PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,read-pc-config\n"); - Pci_Set_IOA_Address = 1; - } if(get_property(find_path_device("/rtas"),"ibm,fw-phb-id",NULL) != NULL) { PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,fw-phb-id\n"); Pci_Large_Bus_System = 1; } +} + +/* + * This is called very early before the page table is setup. + */ +void +pSeries_pcibios_init_early(void) { + ppc_md.pcibios_read_config_byte = rtas_read_config_byte; + ppc_md.pcibios_read_config_word = rtas_read_config_word; + ppc_md.pcibios_read_config_dword = rtas_read_config_dword; + ppc_md.pcibios_write_config_byte = rtas_write_config_byte; + ppc_md.pcibios_write_config_word = rtas_write_config_word; + ppc_md.pcibios_write_config_dword = rtas_write_config_dword; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pacaData.c linuxppc64_2_4/arch/ppc64/kernel/pacaData.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pacaData.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pacaData.c Thu Sep 13 11:54:36 2001 @@ -31,49 +31,35 @@ * processor (not thread). */ #define PACAINITDATA(number,start,lpq,asrr,asrv) \ -{ (struct ItLpPaca *)(((char *)(&xPaca[number]))+offsetof(struct Paca, xLpPaca)), \ - (struct ItLpRegSave *)(((char *)(&xPaca[number]))+offsetof(struct Paca, xRegSav)), \ - 0, /* Current */ \ - 0, /* R21 Save */ \ - 0, /* R22 Save */ \ - 0, /* Kernel stack addr save */ \ - (number), /* Paca Index */ \ - 0, /* HW Proc Number */ \ - 0, /* CCR Save */ \ - 0, /* MSR Save */ \ - 0, /* LR Save */ \ - 0, /* Pointer to thread */ \ - {(asrr), /* Real pointer to segment table */ \ - (asrv), /* Virt pointer to segment table */ \ - REGION_COUNT-1}, /* Round robin index */ \ - {0,0,0,0,0,0,0,0},/* Segment cache */ \ - 0, /* Kernel TOC address */ \ - 0, /* R1 Save */ \ - (lpq), /* &xItLpQueue, */ \ - {0,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, SPIN_LOCK_UNLOCKED, 0}, /*xRtas */ \ - (start), /* Processor start */ \ - {0,0,0}, /* Resv */ \ - 0, /* Default Decrementer */ \ - 0, /* next_jiffy_update_tb */ \ - {0,0,0,0,0,0,0,0,0,0,0,0}, /* Resv */ \ - { /* LpPaca */ \ - xDesc: 0xd397d781, /* "LpPa" */ \ - xSize: sizeof(struct ItLpPaca), /* */ \ - xFPRegsInUse: 1, \ - xDynProcStatus: 2, \ - xEndOfQuantum: 0xffffffffffffffff /* */ \ - }, \ - { /* LpRegSave */ \ - 0xd397d9e2, /* "LpRS" */ \ - sizeof(struct ItLpRegSave) /* */ \ - }, \ - 0, /* pvr */ \ - 0, /* pgd_cache */ \ - 0, /* pmd_cache */ \ - 0, /* pte_cache */ \ - 0, /* pgtable_cache_sz */ \ - 0, /* prof_multiplier */ \ - 0 /* prof_counter */ \ +{ \ + xLpPacaPtr: &xPaca[number].xLpPaca, \ + xLpRegSavePtr: &xPaca[number].xRegSav, \ + xPacaIndex: (number), /* Paca Index */ \ + default_decr: 0x00ff0000, /* Initial Decr */ \ + xStab_data: { \ + real: (asrr), /* Real pointer to segment table */ \ + virt: (asrv), /* Virt pointer to segment table */ \ + next_round_robin: 1 /* Round robin index */ \ + }, \ + lpQueuePtr: (lpq), /* &xItLpQueue, */ \ + xRtas: { \ + lock: SPIN_LOCK_UNLOCKED \ + }, \ + xProcStart: (start), /* Processor start */ \ + xLpPaca: { \ + xDesc: 0xd397d781, /* "LpPa" */ \ + xSize: sizeof(struct ItLpPaca), \ + xFPRegsInUse: 1, \ + xDynProcStatus: 2, \ + xDecrVal: 0x00ff0000, \ + xEndOfQuantum: 0xffffffffffffffff \ + }, \ + xRegSav: { \ + xDesc: 0xd397d9e2, /* "LpRS" */ \ + xSize: sizeof(struct ItLpRegSave) \ + }, \ + exception_sp: \ + &xPaca[number].exception_stack[N_EXC_STACK*EXC_FRAME_SIZE], \ } struct Paca xPaca[maxPacas] __page_aligned = { diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pci.c linuxppc64_2_4/arch/ppc64/kernel/pci.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pci.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pci.c Wed Oct 3 11:04:13 2001 @@ -34,6 +34,17 @@ #include "pci.h" +/* isa_io_limit -- only I/O ports above this addr are recognized by inb/outb. + * We fake failure below this limit without trashing the system. + * This is zero when an ISA bus exists, else PAGE_SIZE. + */ +unsigned long isa_io_limit = PAGE_SIZE; +/* isa_io_base -- the base address from which io bars are offsets. + * This is the lowest I/O base address (so bar values are always positive), + * and it *must* be the start of ISA space if an ISA bus exists because + * ISA drivers use hard coded offsets. If no ISA bus exists a dummy + * page is mapped and isa_io_limit prevents access to it. + */ unsigned long isa_io_base = 0; unsigned long isa_mem_base = 0; unsigned long pci_dram_offset = 0; @@ -41,10 +52,14 @@ /****************************************************************** * Forward declare of prototypes ******************************************************************/ -void pcibios_fixup_resources(struct pci_dev* dev); -void fixup_resources(struct pci_dev* dev); /* In brand pci code */ +static void pcibios_fixup_resources(struct pci_dev* dev); +static void fixup_broken_pcnet32(struct pci_dev* dev); +void fixup_resources(struct pci_dev* dev); struct pci_dev *find_floppy(void); +void iSeries_pcibios_init(void); +void pSeries_pcibios_init(void); + extern struct Naca *naca; @@ -53,6 +68,19 @@ struct pci_controller* hose_head; struct pci_controller** hose_tail = &hose_head; +/******************************************************************* + * Counters and control flags. + *******************************************************************/ +long Pci_Io_Read_Count = 0; +long Pci_Io_Write_Count = 0; +long Pci_Cfg_Read_Count = 0; +long Pci_Cfg_Write_Count= 0; +long Pci_Error_Count = 0; + +int Pci_Retry_Max = 7; +int Pci_Error_Flag = 0; +int Pci_Trace_Flag = 0; + /****************************************************************** * ******************************************************************/ @@ -67,10 +95,20 @@ struct pci_dev *ppc64_floppy_dev = NULL; struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_TRIDENT, PCI_ANY_ID, fixup_broken_pcnet32 }, { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, { 0 } }; +static void fixup_broken_pcnet32(struct pci_dev* dev) +{ + if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) { + dev->vendor = PCI_VENDOR_ID_AMD; + pci_write_config_word(dev, PCI_VENDOR_ID, PCI_VENDOR_ID_AMD); + pci_name_device(dev); + } +} + void pcibios_fixup_pbus_ranges(struct pci_bus *pbus, struct pbus_set_ranges_data *pranges) { @@ -83,7 +121,7 @@ { u32 new, check; int reg; - struct pci_controller* hose = dev->sysdata; + struct pci_controller* hose = PCI_GET_PHB_PTR(dev); new = res->start; if (hose && res->flags & IORESOURCE_MEM) @@ -186,22 +224,29 @@ { struct list_head *ln; struct pci_bus *bus; - struct pci_dev *dev; - int idx; - struct resource *r, *pr; + int i; + struct resource *res, *pr; /* Depth-First Search on bus tree */ for (ln=bus_list->next; ln != bus_list; ln=ln->next) { bus = pci_bus_b(ln); - if ((dev = bus->self)) { - for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { - r = &dev->resource[idx]; - if (!r->start) - continue; - pr = pci_find_parent_resource(dev, r); - if (!pr || request_resource(pr, r) < 0) - printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); - } + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL || !res->flags) + continue; + if (bus->parent == NULL) + pr = (res->flags & IORESOURCE_IO)? + &ioport_resource: &iomem_resource; + else + pr = pci_find_parent_resource(bus->self, res); + + if (pr == res) + continue; /* transparent bus or undefined */ + if (pr && request_resource(pr, res) == 0) + continue; + printk(KERN_ERR "PCI: Cannot allocate resource region " + "%d of PCI bridge %x\n", i, bus->number); + printk(KERN_ERR "PCI: resource is %lx..%lx (%lx), parent %p\n", + res->start, res->end, res->flags, pr); } pcibios_allocate_bus_resources(&bus->children); } @@ -388,7 +433,9 @@ int next_busno; #ifndef CONFIG_PPC_ISERIES - pSeries_pcibios_init(); + pSeries_pcibios_init(); +#else + iSeries_pcibios_init(); #endif printk("PCI: Probing PCI hardware\n"); @@ -398,14 +445,13 @@ /* Scan all of the recorded PCI controllers. */ for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { hose->last_busno = 0xff; - bus = pci_scan_bus(hose->first_busno, hose->ops, hose); + bus = pci_scan_bus(hose->first_busno, hose->ops, hose->arch_data); hose->bus = bus; hose->last_busno = bus->subordinate; if (pci_assign_all_busses || next_busno <= hose->last_busno) next_busno = hose->last_busno+1; } pci_bus_count = next_busno; - /* Call machine dependant fixup */ if (ppc_md.pcibios_fixup) { @@ -415,24 +461,18 @@ /* Generic fixups */ pcibios_generic_fixup(); +#ifndef CONFIG_PPC_ISERIES /* Allocate and assign resources */ pcibios_allocate_bus_resources(&pci_root_buses); - if(naca->io_subsystem == IOS_OPEN_PIC) { - pcibios_allocate_resources(0); // DRENG Condor removed. This will have to be reviewed!!! - } - if(naca->io_subsystem == IOS_OPEN_PIC) { - pcibios_allocate_resources(1); // DRENG Condor removed. This will have to be reviewed!!! - } + pcibios_allocate_resources(0); + pcibios_allocate_resources(1); pcibios_assign_resources(); - /* - * Set up TCE tables for each PHB. - */ - for (hose = hose_head; hose; hose = hose->next) { - create_pci_bus_tce_table(hose->global_number, - (unsigned long)hose); - } + pci_fix_bus_sysdata(); + create_tce_tables(); + PPCDBG(PPCDBG_BUSWALK,"pSeries create_tce_tables()\n"); +#endif ppc64_floppy_dev = find_floppy(); printk("PCI: Probing PCI hardware done\n"); @@ -454,8 +494,55 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) { - pci_read_bridge_bases(bus); - + struct pci_controller *phb = PCI_GET_PHB_PTR(bus); + struct resource *res; + unsigned long io_offset; + int i; + +#ifndef CONFIG_PPC_ISERIES + io_offset = (unsigned long)phb->io_base_virt - isa_io_base; + + if (bus->parent == NULL) { + /* This is a host bridge - fill in its resources */ + phb->bus = bus; + bus->resource[0] = res = &phb->io_resource; + if (!res->flags) + BUG(); /* No I/O resource for this PHB? */ + + for (i = 0; i < 3; ++i) { + res = &phb->mem_resources[i]; + if (!res->flags) { + if (i == 0) + BUG(); /* No memory resource for this PHB? */ + } + bus->resource[i+1] = res; + } + } else { + /* This is a subordinate bridge */ + pci_read_bridge_bases(bus); + + for (i = 0; i < 4; ++i) { + if ((res = bus->resource[i]) == NULL) + continue; + if (!res->flags) + continue; + if (res == pci_find_parent_resource(bus->self, res)) { + /* Transparent resource -- don't try to "fix" it. */ + continue; + } + if (res->flags & IORESOURCE_IO) { + res->start += io_offset; + res->end += io_offset; + } else if (phb->pci_mem_offset + && (res->flags & IORESOURCE_MEM)) { + if (res->start < phb->pci_mem_offset) { + res->start += phb->pci_mem_offset; + res->end += phb->pci_mem_offset; + } + } + } + } +#endif if ( ppc_md.pcibios_fixup_bus ) ppc_md.pcibios_fixup_bus(bus); } @@ -471,6 +558,7 @@ int idx; struct resource *r; + PPCDBG(PPCDBG_BUSWALK,"PCI: "__FUNCTION__" for device %s \n",dev->slot_name); if (ppc_md.pcibios_enable_device_hook) if (ppc_md.pcibios_enable_device_hook(dev, 0)) return -EINVAL; @@ -546,7 +634,7 @@ */ int pci_controller_num(struct pci_dev *dev) { - struct pci_controller *hose = (struct pci_controller *) dev->sysdata; + struct pci_controller *hose = PCI_GET_PHB_PTR(dev); return hose->global_number; } @@ -572,7 +660,7 @@ __pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state) { - struct pci_controller *hose = (struct pci_controller *) dev->sysdata; + struct pci_controller *hose = PCI_GET_PHB_PTR(dev); unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long io_offset = 0; int i, res_bit; @@ -794,11 +882,12 @@ * Dump Device information for Debug *****************************************************/ void dumpPci_Dev(struct pci_dev* Pci_Dev) { - int i; - udbg_printf("\tpci_dev = 0x%016LX \n",Pci_Dev); - if( Pci_Dev == NULL ) return; - udbg_printf("\tname = %s\n",Pci_Dev->name); - udbg_printf("\tpci_dev phb = 0x%016LX \n",PCI_GET_PHB_PTR(Pci_Dev) ); + int i; + udbg_printf("\tpci_dev* = 0x%pb\n",Pci_Dev); + if( Pci_Dev == NULL ) return; + udbg_printf("\tname = %s \n",Pci_Dev->name); + udbg_printf("\tbus* = 0x%p\n",Pci_Dev->bus); + udbg_printf("\tsysdata* = 0x%p\n",Pci_Dev->sysdata); udbg_printf("\tDevice = 0x%4X%02X:%02X.%02X 0x%04X:%04X\n", PCI_GET_PHB_NUMBER(Pci_Dev), PCI_GET_BUS_NUMBER(Pci_Dev), diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pci.h linuxppc64_2_4/arch/ppc64/kernel/pci.h --- linux-2.4.9-ac10/arch/ppc64/kernel/pci.h Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pci.h Fri Sep 21 09:24:50 2001 @@ -9,8 +9,6 @@ #ifndef __PPC_KERNEL_PCI_H__ #define __PPC_KERNEL_PCI_H__ -/* Include these to prevent strange compile warnings if user forgets - * to include them in their .c file. */ #include #include @@ -29,27 +27,59 @@ extern struct pci_controller* pci_alloc_pci_controller(char *model, enum phb_types controller_type); extern struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node); +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; + /******************************************************************* * Platform functions that are brand specific implementation. *******************************************************************/ extern unsigned long find_and_init_phbs(void); -extern int pci_read_irq_line(struct pci_dev* PciDev); -extern int pci_read_bar_registers(struct pci_dev* PciDev, int Count, int RomFlag); - extern void fixup_resources(struct pci_dev *dev); extern void ppc64_pcibios_init(void); extern int pci_reset_device(struct pci_dev*); extern int pci_get_location(struct pci_dev*, char*, int); -extern struct pci_dev* find_floppy(void); extern struct pci_dev *ppc64_floppy_dev; + +/******************************************************************* + * PCI device_node operations + *******************************************************************/ +struct device_node; +typedef void *(*traverse_func)(struct device_node *me, void *data); +void *traverse_pci_devices(struct device_node *start, traverse_func pre, traverse_func post, void *data); +void *traverse_all_pci_devices(traverse_func pre); + +void pci_devs_phb_init(void); +void pci_fix_bus_sysdata(void); +struct device_node *fetch_dev_dn(struct pci_dev *dev); + +void iSeries_pcibios_init_early(void); +void pSeries_pcibios_init_early(void); +void pSeries_pcibios_init(void); + +/* Get a device_node from a pci_dev. This code must be fast except in the case + * where the sysdata is incorrect and needs to be fixed up (hopefully just once) + */ +static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev) +{ + struct device_node *dn = (struct device_node *)(dev->sysdata); + if (dn->devfn == dev->devfn && dn->busno == dev->bus->number) + return dn; /* fast path. sysdata is good */ + else + return fetch_dev_dn(dev); +} +/* Use this macro after the PCI bus walk for max performance when it + * is known that sysdata is correct. + */ +#define PCI_GET_DN(dev) ((struct device_node *)((dev)->sysdata)) + + /******************************************************************* * Platform configuration flags.. (Live in pci.c) *******************************************************************/ extern int Pci_Large_Bus_System; /* System has > 256 buses */ -extern int Pci_Set_IOA_Address; /* Set IOA BARs from OF */ extern int Pci_Manage_Phb_Space; /* Manage Phb Space for IOAs*/ /******************************************************************* @@ -58,9 +88,9 @@ * PCI_GET_PHB_NUMBER(struct pci_dev*) returns the Phb number. * PCI_GET_BUS_NUMBER(struct pci_dev*) returns the bus number. *******************************************************************/ -#define PCI_GET_PHB_PTR(PCIDEV) ((struct pci_controller*)##PCIDEV##->sysdata) -#define PCI_GET_PHB_NUMBER(PCIDEV) ((##PCIDEV##->bus->number&0x00FFFF00)>>8) -#define PCI_GET_BUS_NUMBER(PCIDEV) ( ##PCIDEV##->bus->number&0x0000FF) +#define PCI_GET_PHB_PTR(dev) (((struct device_node *)(dev)->sysdata)->phb) +#define PCI_GET_PHB_NUMBER(dev) (((dev)->bus->number&0x00FFFF00)>>8) +#define PCI_GET_BUS_NUMBER(dev) ((dev)->bus->number&0x0000FF) /******************************************************************* * Pci Flight Recorder support. diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pci_dma.c linuxppc64_2_4/arch/ppc64/kernel/pci_dma.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pci_dma.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pci_dma.c Wed Oct 3 12:41:53 2001 @@ -39,7 +39,11 @@ #include #include -#define DEBUG_TCE 1 +#include + +#include "pci.h" + +// #define DEBUG_TCE 1 /* Initialize so this guy does not end up in the BSS section. * Only used to pass OF initialization data set in prom.c into the main @@ -47,47 +51,61 @@ */ extern struct _of_tce_table of_tce_table[]; -struct TceTable virtBusTceTable; /* Tce table for virtual bus */ +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; + +struct TceTable virtBusVethTceTable; /* Tce table for virtual ethernet */ +struct TceTable virtBusVioTceTable; /* Tce table for virtual I/O */ + +struct device_node iSeries_veth_dev_node = { tce_table: &virtBusVethTceTable }; +struct device_node iSeries_vio_dev_node = { tce_table: &virtBusVioTceTable }; + +struct pci_dev iSeries_veth_dev_st = { sysdata: &iSeries_veth_dev_node }; +struct pci_dev iSeries_vio_dev_st = { sysdata: &iSeries_vio_dev_node }; + +struct pci_dev * iSeries_veth_dev = &iSeries_veth_dev_st; +struct pci_dev * iSeries_vio_dev = &iSeries_vio_dev_st; struct TceTable * tceTables[256]; /* Tce tables for 256 busses * Bus 255 is the virtual bus * zero indicates no bus defined */ - /* allocates a contiguous range of tces (power-of-2 size) */ -static long alloc_tce_range( struct TceTable *, +/* allocates a contiguous range of tces (power-of-2 size) */ +static inline long alloc_tce_range(struct TceTable *, unsigned order ); - /* allocates a contiguous range of tces (power-of-2 size) - * assumes lock already held - */ -static long alloc_tce_range_nolock( struct TceTable *, - unsigned order ); - /* frees a contiguous range of tces (power-of-2 size) */ -static void free_tce_range( struct TceTable *, - long tcenum, - unsigned order ); - /* frees a contiguous rnage of tces (power-of-2 size) - * assumes lock already held - */ -static void free_tce_range_nolock( struct TceTable *, - long tcenum, - unsigned order ); - /* allocates a range of tces and sets them to the pages */ -static dma_addr_t get_tces( struct TceTable *, - unsigned order, - void *page, - unsigned numPages, - int tceType, - int direction ); -static void free_tces( struct TceTable *, - dma_addr_t tce, - unsigned order, - unsigned numPages ); -static long test_tce_range( struct TceTable *, + +/* allocates a contiguous range of tces (power-of-2 size) + * assumes lock already held + */ +static long alloc_tce_range_nolock(struct TceTable *, + unsigned order ); + +/* frees a contiguous range of tces (power-of-2 size) */ +static inline void free_tce_range(struct TceTable *, long tcenum, unsigned order ); -static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, - dma_addr_t dma_addr, unsigned long numTces ); +/* frees a contiguous rnage of tces (power-of-2 size) + * assumes lock already held + */ +void free_tce_range_nolock(struct TceTable *, + long tcenum, + unsigned order ); + +/* allocates a range of tces and sets them to the pages */ +static inline dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int direction ); + +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg(struct scatterlist *sg, int nents, + dma_addr_t dma_addr, + unsigned long numTces ); static unsigned long num_tces_sg( struct scatterlist *sg, int nents ); @@ -96,93 +114,107 @@ struct scatterlist *sg, int nents, unsigned numTces, - int tceType, int direction ); -static void getTceTableParms( struct pci_controller *phb, - struct TceTable *tce_table_parms ); - +static void getTceTableParmsPSeries( struct pci_controller *phb, + struct device_node *dn, + struct TceTable *tce_table_parms ); -static unsigned long setTce( unsigned long base, - unsigned long tce_num, - unsigned long tce_data); - -static long resv_tce_range_top_level( struct TceTable *tbl, - unsigned long dma_addr, - unsigned size ); +void create_pci_bus_tce_table( unsigned long token ); u8 iSeries_Get_Bus( struct pci_dev * dv ) { return 0; } -/* - * Given a pci device, return which tce table is assigned to it. - */ -unsigned long get_tce_table_index( struct pci_dev *dev) { - unsigned long index = - ((struct pci_controller *)dev->sysdata)->global_number; - PPCDBG(PPCDBG_TCE, "get_tce_table_index:\n"); - PPCDBG(PPCDBG_TCE, "\tdev = 0x%lx, index = 0x%lx\n", dev, index); - return(index); +static inline struct TceTable *get_tce_table(struct pci_dev *dev) { + + if ( ( _machine == _MACH_iSeries ) && ( dev->bus ) ) + return tceTables[dev->bus->number]; + + return PCI_GET_DN(dev)->tce_table; } -static unsigned __inline__ count_leading_zeros32( unsigned long x ) +static unsigned long __inline__ count_leading_zeros64( unsigned long x ) { - unsigned lz; - asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); + unsigned long lz; + asm("cntlzd %0,%1" : "=r"(lz) : "r"(x)); return lz; } -static void __inline__ build_tce( struct TceTable * tbl, long tcenum, - unsigned long uaddr, int tceType, int direction ) +static void tce_build_iSeries(struct TceTable *tbl, long tcenum, + unsigned long uaddr, int direction ) { u64 setTceRc; union Tce tce; PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx, tbl = 0x%lx, index=%lx\n", + tcenum, tbl, tbl->index); + tce.wholeTce = 0; tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + /* If for virtual bus */ - if ( tceType == TCE_VB ) { + if ( tbl->tceType == TCE_VB ) { tce.tceBits.valid = 1; tce.tceBits.allIo = 1; if ( direction != PCI_DMA_TODEVICE ) tce.tceBits.readWrite = 1; - } - /* If for PCI bus */ - else { + } else { + /* If for PCI bus */ tce.tceBits.readWrite = 1; // Read allowed if ( direction != PCI_DMA_TODEVICE ) tce.tceBits.pciWrite = 1; } -#ifdef CONFIG_PPC_ISERIES - setTceRc = HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); -#else - setTceRc = setTce( (u64)tbl->base, (u64)tcenum, tce.wholeTce ); -#endif + setTceRc = HvCallXm_setTce((u64)tbl->index, + (u64)tcenum, + tce.wholeTce ); - if ( setTceRc ) { - PPCDBG(PPCDBG_TCE, "build_tce: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", - (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + if(setTceRc) { + PPCDBG(PPCDBG_TCE, "setTce failed. rc=%ld\n", setTceRc); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + PPCDBG(PPCDBG_TCE, "\ttce num = 0x%lx\n", (u64)tcenum); + PPCDBG(PPCDBG_TCE, "\ttce val = 0x%lx\n", tce.wholeTce ); } - } +static void tce_build_pSeries(struct TceTable *tbl, long tcenum, + unsigned long uaddr, int direction ) +{ + union Tce tce; + union Tce *tce_addr; + + PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx, tbl = 0x%lx, index=%lx\n", + tcenum, tbl, tbl->index); + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) tce.tceBits.pciWrite = 1; + + tce_addr = ((union Tce *)tbl->base) + tcenum; + *tce_addr = (union Tce)tce.wholeTce; + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); +} /* * Build a TceTable structure. This contains a multi-level bit map which * is used to manage allocation of the tce space. */ -struct TceTable * build_tce_table( struct TceTable * tbl ) +static struct TceTable *build_tce_table( struct TceTable * tbl ) { unsigned long bits, bytes, totalBytes; unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; unsigned i, k, m; unsigned char * pos, * p, b; + PPCDBG(PPCDBG_TCEINIT, "build_tce_table: tbl = 0x%lx\n", tbl); spin_lock_init( &(tbl->lock) ); tbl->mlbm.maxLevel = 0; @@ -194,14 +226,14 @@ bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); for ( i=0; i> m; - PPCDBG(PPCDBG_TCE, "build_tce_table: level %d last bit %x\n", i, 0x80>>m ); + PPCDBG(PPCDBG_TCEINIT, "build_tce_table: level %d last bit %x\n", i, 0x80>>m ); } } else @@ -232,7 +264,6 @@ pos += numBytes[i]; tbl->mlbm.level[i].numBits = numBits[i]; tbl->mlbm.level[i].numBytes = numBytes[i]; - } /* For the highest level, turn on all the bits */ @@ -240,14 +271,14 @@ i = tbl->mlbm.maxLevel; p = tbl->mlbm.level[i].map; m = numBits[i]; - PPCDBG(PPCDBG_TCE, "build_tce_table: highest level (%d) has all bits set\n", i); + PPCDBG(PPCDBG_TCEINIT, "build_tce_table: highest level (%d) has all bits set\n", i); for (k=0; k= 8 ) { /* handle full bytes */ *p++ = 0xff; m -= 8; } - else { + else if(m>0) { /* handle the last partial byte */ b = 0x80; *p = 0; @@ -256,15 +287,15 @@ b >>= 1; --m; } + } else { + break; } } - return tbl; - } -static long alloc_tce_range( struct TceTable *tbl, unsigned order ) +static inline long alloc_tce_range( struct TceTable *tbl, unsigned order ) { long retval; unsigned long flags; @@ -281,50 +312,50 @@ return retval; } - - static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) { unsigned long numBits, numBytes; unsigned long i, bit, block, mask; long tcenum; - unsigned char * map; + u64 * map; /* If the order (power of 2 size) requested is larger than our * biggest, indicate failure */ - if ( order > tbl->mlbm.maxLevel ) { - PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: invalid order requested, order = %d\n", - order ); + if(order >= NUM_TCE_LEVELS) { + PPCDBG(PPCDBG_TCE, + "alloc_tce_range_nolock: invalid order: %d\n", order ); return -1; } numBits = tbl->mlbm.level[order].numBits; numBytes = tbl->mlbm.level[order].numBytes; - map = tbl->mlbm.level[order].map; + map = (u64 *)tbl->mlbm.level[order].map; /* Initialize return value to -1 (failure) */ tcenum = -1; /* Loop through the bytes of the bitmap */ - for (i=0; i> bit); - *map &= mask; + mask = 0x1UL << (63 - bit); + *map &= ~mask; + /* compute the index into our tce table for * the first tce in the block */ PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); tcenum = block << order; - break; + return tcenum; } ++map; } @@ -343,7 +374,7 @@ * size bigger. If one of those is found, return the second * half of the block to freespace and keep the first half */ - if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { + if((tcenum == -1) && (order < (NUM_TCE_LEVELS - 1))) { tcenum = alloc_tce_range_nolock( tbl, order+1 ); if ( tcenum != -1 ) { free_tce_range_nolock( tbl, tcenum+(1< tbl->mlbm.maxLevel ) { - PPCDBG(PPCDBG_TCE, "free_tce_range: order too large, order = %d, tcenum = %d\n", order, tcenum ); + + if (order >= NUM_TCE_LEVELS) { + PPCDBG(PPCDBG_TCE, + "free_tce_range: invalid order: %d, tcenum = %d\n", + order, tcenum ); return; } - + block = tcenum >> order; + +#ifdef DEBUG_TCE if ( tcenum != (block << order ) ) { - PPCDBG(PPCDBG_TCE, "free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); + PPCDBG(PPCDBG_TCE, + "free_tce_range: tcenum %lx misaligned for order %x\n", + tcenum, order ); return; } + + if ( block >= tbl->mlbm.level[order].numBits ) { - PPCDBG(PPCDBG_TCE, "free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); + PPCDBG(PPCDBG_TCE, + "free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", + tcenum, order, tbl->mlbm.level[order].numBits ); return; } -#ifdef DEBUG_TCE + + if ( test_tce_range( tbl, tcenum, order ) ) { - PPCDBG(PPCDBG_TCE, "free_tce_range: freeing range not completely allocated.\n"); - PPCDBG(PPCDBG_TCE, "free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + PPCDBG(PPCDBG_TCE, + "free_tce_range: freeing range not allocated.\n"); + PPCDBG(PPCDBG_TCE, + "\tTceTable %p, tcenum %lx, order %x\n", + tbl, tcenum, order ); } #endif + map = tbl->mlbm.level[order].map; byte = block / 8; bit = block % 8; mask = 0x80 >> bit; bytep = map + byte; + #ifdef DEBUG_TCE - PPCDBG(PPCDBG_TCE, "free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); + PPCDBG(PPCDBG_TCE, + "free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n", + block, byte, bit, order); if ( *bytep & mask ) - PPCDBG(PPCDBG_TCE, "free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + PPCDBG(PPCDBG_TCE, + "free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", + tbl, tcenum, order ); #endif + *bytep |= mask; /* If there is a higher level in the bit map than this we may be @@ -415,11 +469,11 @@ * If this is the highest level we can't buddy up * If this level has an odd number of bits and * we are freeing the last block we can't buddy up + * Don't buddy up if it's in the first 1/4 of the level */ - if ( ( order < tbl->mlbm.maxLevel ) && - ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || - ( block < tbl->mlbm.level[order].numBits-1 ) ) ) { - + if (( block > (tbl->mlbm.level[order].numBits/4) ) && + (( block < tbl->mlbm.level[order].numBits-1 ) || + ( 0 == ( tbl->mlbm.level[order].numBits & 1)))) { /* See if we can buddy up the block we just freed */ bit &= 6; /* get to the first of the buddy bits */ mask = 0xc0 >> bit; /* build two bit mask */ @@ -430,13 +484,15 @@ block = ( byte * 8 ) + bit; /* block of first of buddies */ tcenum = block << order; /* free the buddied block */ - PPCDBG(PPCDBG_TCE, "free_tce_range: buddying up block %ld and block %ld\n", block, block+1); + PPCDBG(PPCDBG_TCE, + "free_tce_range: buddying blocks %ld & %ld\n", + block, block+1); free_tce_range_nolock( tbl, tcenum, order+1 ); } } } -static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) { unsigned long block; unsigned byte, bit, mask, b; @@ -468,7 +524,7 @@ return retval; } -static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) +static inline dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int direction ) { long tcenum; unsigned long uaddr; @@ -485,7 +541,7 @@ retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ /* Setup a tce for each page */ for (i=0; istartOffset; if ( tcenum > maxTcenum ) { - PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", - tcenum, maxTcenum ); - PPCDBG(PPCDBG_TCE, "free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); - PPCDBG(PPCDBG_TCE, "free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); - PPCDBG(PPCDBG_TCE, "free_tces: size %lu\n", (unsigned long)tbl->size ); - PPCDBG(PPCDBG_TCE, "free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); - PPCDBG(PPCDBG_TCE, "free_tces: index %lu\n", (unsigned long)tbl->index ); + PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum\n"); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx\n", tcenum); + PPCDBG(PPCDBG_TCE, "\tmaxTcenum = 0x%lx\n", maxTcenum); + PPCDBG(PPCDBG_TCE, "\tTCE Table = 0x%lx\n", (u64)tbl); + PPCDBG(PPCDBG_TCE, "\tbus# = 0x%lx\n", + (u64)tbl->busNumber ); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%lx\n", (u64)tbl->size); + PPCDBG(PPCDBG_TCE, "\tstartOff = 0x%lx\n", + (u64)tbl->startOffset ); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); return; } @@ -522,22 +582,68 @@ for (i=0; iindex, (u64)tcenum, tce.wholeTce ); -#else - setTceRc = setTce( (u64)tbl->base, (u64)tcenum, tce.wholeTce ); -#endif + setTceRc = HvCallXm_setTce((u64)tbl->index, + (u64)tcenum, + tce.wholeTce ); if ( setTceRc ) { - PPCDBG(PPCDBG_TCE, "free_tces: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", - (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + PPCDBG(PPCDBG_TCE, "tce_free: setTce failed\n"); + PPCDBG(PPCDBG_TCE, "\trc = 0x%lx\n", setTceRc); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", + (u64)tbl->index); + PPCDBG(PPCDBG_TCE, "\ttce num = 0x%lx\n", (u64)tcenum); + PPCDBG(PPCDBG_TCE, "\ttce val = 0x%lx\n", + tce.wholeTce ); } ++tcenum; } free_tce_range( tbl, freeTce, order ); +} + +static void tce_free_pSeries(struct TceTable *tbl, dma_addr_t dma_addr, + unsigned order, unsigned numPages) +{ + long tcenum, freeTce, maxTcenum; + unsigned i; + union Tce tce; + union Tce *tce_addr; + + maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + // tcenum -= tbl->startOffset; + + freeTce = tcenum - tbl->startOffset; + + if ( freeTce > maxTcenum ) { + PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum\n"); + PPCDBG(PPCDBG_TCE, "\ttcenum = 0x%lx\n", tcenum); + PPCDBG(PPCDBG_TCE, "\tmaxTcenum = 0x%lx\n", maxTcenum); + PPCDBG(PPCDBG_TCE, "\tTCE Table = 0x%lx\n", (u64)tbl); + PPCDBG(PPCDBG_TCE, "\tbus# = 0x%lx\n", + (u64)tbl->busNumber ); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%lx\n", (u64)tbl->size); + PPCDBG(PPCDBG_TCE, "\tstartOff = 0x%lx\n", + (u64)tbl->startOffset ); + PPCDBG(PPCDBG_TCE, "\tindex = 0x%lx\n", (u64)tbl->index); + return; + } + + for (i=0; ibase) + tcenum; + *tce_addr = (union Tce)tce.wholeTce; + ++tcenum; + } + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); + + free_tce_range( tbl, freeTce, order ); } void __init create_virtual_bus_tce_table(void) @@ -552,178 +658,232 @@ absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); HvCallXm_getTceTableParms( absParmsPtr ); - virtBusTceTable.size = virtBusTceTableParms.size; - virtBusTceTable.busNumber = virtBusTceTableParms.busNumber; - virtBusTceTable.startOffset = virtBusTceTableParms.startOffset; - virtBusTceTable.index = virtBusTceTableParms.index; + virtBusVethTceTable.size = virtBusTceTableParms.size / 2; + virtBusVethTceTable.busNumber = virtBusTceTableParms.busNumber; + virtBusVethTceTable.startOffset = virtBusTceTableParms.startOffset; + virtBusVethTceTable.index = virtBusTceTableParms.index; + virtBusVethTceTable.tceType = TCE_VB; + + virtBusVioTceTable.size = virtBusTceTableParms.size - virtBusVethTceTable.size; + virtBusVioTceTable.busNumber = virtBusTceTableParms.busNumber; + virtBusVioTceTable.startOffset = virtBusTceTableParms.startOffset + + virtBusVethTceTable.size * (PAGE_SIZE/sizeof(union Tce)); + virtBusVioTceTable.index = virtBusTceTableParms.index; + virtBusVioTceTable.tceType = TCE_VB; - t = build_tce_table( &virtBusTceTable ); + t = build_tce_table( &virtBusVethTceTable ); if ( t ) { tceTables[255] = t; - PPCDBG(PPCDBG_TCE, "Virtual Bus TCE table built successfully.\n"); - PPCDBG(PPCDBG_TCE, " TCE table size = %ld entries\n", + printk( "Virtual Bus VETH TCE table built successfully.\n"); + printk( " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk( " TCE table token = %d\n", + (unsigned)t->index ); + printk( " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + printk( "Virtual Bus VETH TCE table failed.\n"); + + t = build_tce_table( &virtBusVioTceTable ); + if ( t ) { + printk( "Virtual Bus VIO TCE table built successfully.\n"); + printk( " TCE table size = %ld entries\n", (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); - PPCDBG(PPCDBG_TCE, " TCE table token = %d\n", + printk( " TCE table token = %d\n", (unsigned)t->index ); - PPCDBG(PPCDBG_TCE, " TCE table start entry = 0x%lx\n", + printk( " TCE table start entry = 0x%lx\n", (unsigned long)t->startOffset ); } else - PPCDBG(PPCDBG_TCE, "Virtual Bus TCE table failed.\n"); + printk( "Virtual Bus VIO TCE table failed.\n"); } -void create_pci_bus_tce_table( unsigned long busNumber, - unsigned long token ) +void create_tce_tables_for_buses(struct list_head *bus_list) { - struct TceTable * t; + struct pci_controller* phb; + struct device_node *dn, *first_dn; + int num_slots, num_slots_ilog2; + int first_phb = 1; + + for (phb=hose_head;phb;phb=phb->next) { + first_dn = ((struct device_node *)phb->arch_data)->child; + /* Carve 2GB into the largest dma_window_size possible */ + for (dn = first_dn, num_slots = 0; dn != NULL; dn = dn->sibling) + num_slots++; + num_slots_ilog2 = __ilog2(num_slots); + if ((1<dma_window_size = 1 << (22 - num_slots_ilog2); + /* Reserve 16MB of DMA space on the first PHB. + * We should probably be more careful and use firmware props. + * In reality this space is remapped, not lost. But we don't + * want to get that smart to handle it -- too much work. + */ + phb->dma_window_base_cur = first_phb ? (1 << 12) : 0; + first_phb = 0; + for (dn = first_dn, num_slots = 0; dn != NULL; dn = dn->sibling) { + create_pci_bus_tce_table((unsigned long)dn); + } + } +} + +void create_tce_tables(void) { + struct pci_dev *dev; + struct device_node *dn, *mydn; + + create_tce_tables_for_buses(&pci_root_buses); + + /* Now copy the tce_table ptr from the bus devices down to every + * pci device_node. This means get_tce_table() won't need to search + * up the device tree to find it. + */ + pci_for_each_dev(dev) { + mydn = dn = PCI_GET_DN(dev); + while (dn && dn->tce_table == NULL) + dn = dn->parent; + if (dn) { + mydn->tce_table = dn->tce_table; + } + } +} + +/* + * iSeries token = busNumber + * pSeries token = pci_controller* + */ +void create_pci_bus_tce_table( unsigned long token ) { + struct TceTable * builtTceTable; struct TceTable * newTceTable; struct TceTableManagerCB pciBusTceTableParms; -#ifdef CONFIG_PPC_ISERIES u64 parmsPtr; -#endif - struct pci_controller *phb = (struct pci_controller *)token; PPCDBG(PPCDBG_TCE, "Entering create_pci_bus_tce_table.\n"); - PPCDBG(PPCDBG_TCE, "\tbusNumber = 0x%lx, token = 0x%lx.\n", - busNumber, token); - if ( busNumber > 254 ) { - PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); - PPCDBG(PPCDBG_TCE, " Invalid bus number %u\n", busNumber ); - return; - } + PPCDBG(PPCDBG_TCE, "\ttoken = 0x%lx\n", token); newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); - PPCDBG(PPCDBG_TCE, "\tnewTceTable = 0x%lx\n", newTceTable); - - pciBusTceTableParms.busNumber = busNumber; - pciBusTceTableParms.virtualBusFlag = 0; -#ifdef CONFIG_PPC_ISERIES - parmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + if(_machine == _MACH_iSeries) { + if ( token > 254 ) { + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); + PPCDBG(PPCDBG_TCE, " Invalid bus number %u\n", token ); + return; + } - /* - * Call HV with the architected data structure to get TCE table info. - * Put the returned data into the Linux representation of the TCE - * table data. - */ - HvCallXm_getTceTableParms( parmsPtr ); - newTceTable->size = pciBusTceTableParms.size; - newTceTable->busNumber = pciBusTceTableParms.busNumber; - newTceTable->startOffset = pciBusTceTableParms.startOffset; - newTceTable->index = pciBusTceTableParms.index; -#else - getTceTableParms( phb, newTceTable ); -#endif - - t = build_tce_table( newTceTable ); - if ( t ) { - tceTables[busNumber] = t; - PPCDBG(PPCDBG_TCE, "PCI Bus TCE table built successfully.\n"); - PPCDBG(PPCDBG_TCE, " TCE table size = %ld entries\n", - (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); - PPCDBG(PPCDBG_TCE, " TCE table token = %d\n", - (unsigned)t->index ); - PPCDBG(PPCDBG_TCE, " TCE table start entry = 0x%lx\n", - (unsigned long)t->startOffset ); + pciBusTceTableParms.busNumber = token; + pciBusTceTableParms.virtualBusFlag = 0; + parmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + + /* + * Call HV with the architected data structure to get TCE table + * info. Put the returned data into the Linux representation + * of the TCE table data. + */ + HvCallXm_getTceTableParms( parmsPtr ); + + newTceTable->size = pciBusTceTableParms.size; + newTceTable->busNumber = pciBusTceTableParms.busNumber; + newTceTable->startOffset = pciBusTceTableParms.startOffset; + newTceTable->index = pciBusTceTableParms.index; + + builtTceTable = build_tce_table( newTceTable ); + builtTceTable->tceType = TCE_PCI; + tceTables[token] = builtTceTable; + return; + } else { + struct device_node *dn; + struct pci_controller *phb; + + dn = (struct device_node *)token; + phb = dn->phb; + getTceTableParmsPSeries(phb, dn, newTceTable); + builtTceTable = build_tce_table( newTceTable ); + dn->tce_table = builtTceTable; } - else { + + if(builtTceTable == NULL ) { kfree( newTceTable ); PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); return; } - -#ifndef CONFIG_PPC_ISERIES - /* Do not allow DMA's to the 1st 16MB of PCI space in order - * to account for the I/O hole and aliases. - * - * DRENG: this only really needs to be done on PHB0 & should be - * validated against dma-ranges. This is all somewhat compilcated - * by some OF oddities: on a 260, there is no dma-ranges, but there - * is a io-hole. On an F80, there are dma-ranges, but no I/O hole. - * Just seems easier to simply map out the first 16MB in all cases. - * - * The RTAS case here ends up being redundant as it falls into - * the 16MB already mapped out. - */ - resv_tce_range_top_level(newTceTable, 0, 4096); - resv_tce_range_top_level(newTceTable, rtas.base, - (rtas.size) >> PAGE_SHIFT); - -#if 1 - /* Initialize the table to have a one-to-one mapping over RTAS */ - - /* DRENG strictly speaking, the RPA says this should be done. - * I do not see how it can really be a valid thing to require however. - */ - { - unsigned long tce_entry, *tce_entryp, i; - - tce_entryp = (unsigned long *)newTceTable->base; - tce_entryp += (rtas.base >> PAGE_SHIFT); - tce_entry = (rtas.base & PAGE_MASK) | 0x3; - - for(i = 0; i < (rtas.size >> PAGE_SHIFT); i++) { - *tce_entryp = tce_entry; - - tce_entryp++; - tce_entry += (1UL << PAGE_SHIFT); - } - } -#endif -#endif } -static void getTceTableParms( struct pci_controller *phb, - struct TceTable *newTceTable ) { -#if 0 - struct pci_dev *dev; - struct device_node *np; -#endif +static void getTceTableParmsPSeries(struct pci_controller *phb, + struct device_node *dn, + struct TceTable *newTceTable ) { phandle node; unsigned long i; node = ((struct device_node *)(phb->arch_data))->node; - PPCDBG(PPCDBG_TCE, "getTceTableParms: start\n"); - PPCDBG(PPCDBG_TCE, "\tof_tce_table = 0x%lx\n", of_tce_table); - PPCDBG(PPCDBG_TCE, "\tphb = 0x%lx\n", phb); - PPCDBG(PPCDBG_TCE, "\tnewTceTable = 0x%lx\n", newTceTable); + PPCDBG(PPCDBG_TCEINIT, "getTceTableParms: start\n"); + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table = 0x%lx\n", of_tce_table); + PPCDBG(PPCDBG_TCEINIT, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCEINIT, "\tdn = 0x%lx\n", dn); + PPCDBG(PPCDBG_TCEINIT, "\tdn->name = %s\n", dn->name); + PPCDBG(PPCDBG_TCEINIT, "\tdn->full_name= %s\n", dn->full_name); + PPCDBG(PPCDBG_TCEINIT, "\tnewTceTable = 0x%lx\n", newTceTable); + PPCDBG(PPCDBG_TCEINIT, "\tdma_window_size = 0x%lx\n", phb->dma_window_size); i = 0; while(of_tce_table[i].node) { - PPCDBG(PPCDBG_TCE, "\ttce_table[%d].node = 0x%lx\n", + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].node = 0x%lx\n", i, of_tce_table[i].node); - PPCDBG(PPCDBG_TCE, "\ttce_table[%d].base = 0x%lx\n", - i, of_tce_table[i].base); - PPCDBG(PPCDBG_TCE, "\ttce_table[%d].size = 0x%lx\n", - i, of_tce_table[i].size >> PAGE_SHIFT); - PPCDBG(PPCDBG_TCE, "\tphb->arch_data->node = 0x%lx\n", node); + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].base = 0x%lx\n", + i, of_tce_table[i].base); + PPCDBG(PPCDBG_TCEINIT, "\tof_tce_table[%d].size = 0x%lx\n", + i, of_tce_table[i].size >> PAGE_SHIFT); + PPCDBG(PPCDBG_TCEINIT, "\tphb->arch_data->node = 0x%lx\n", + node); if(of_tce_table[i].node == node) { memset((void *)of_tce_table[i].base, 0, of_tce_table[i].size); newTceTable->busNumber = phb->bus->number; - newTceTable->size = - (of_tce_table[i].size) >> PAGE_SHIFT; - newTceTable->startOffset = 0; - newTceTable->base = - of_tce_table[i].base; + + /* Units of tce entries. */ + newTceTable->startOffset = phb->dma_window_base_cur; + + /* Adjust the current table offset to the next */ + /* region. Measured in TCE entries. Force an */ + /* alignment to the size alloted per IOA. This */ + /* makes it easier to remove the 1st 16MB. */ + phb->dma_window_base_cur += (phb->dma_window_size>>3); + phb->dma_window_base_cur &= + ~((phb->dma_window_size>>3)-1); + + /* Set the tce table size - measured in units */ + /* of pages of tce table. */ + newTceTable->size = ((phb->dma_window_base_cur - + newTceTable->startOffset) << 3) + >> PAGE_SHIFT; + + /* Test if we are going over 2GB of DMA space. */ + if(phb->dma_window_base_cur > (1 << 19)) { + udbg_printf("Unexpected number of IOAs under this PHB"); + panic("Unexpected number of IOAs under this PHB"); + } + + newTceTable->base = of_tce_table[i].base; newTceTable->index = 0; + + PPCDBG(PPCDBG_TCEINIT, + "\tnewTceTable->base = 0x%lx\n", + newTceTable->base); + PPCDBG(PPCDBG_TCEINIT, + "\tnewTceTable->startOffset = 0x%lx" + "(# tce entries)\n", + newTceTable->startOffset); + PPCDBG(PPCDBG_TCEINIT, + "\tnewTceTable->size = 0x%lx" + "(# pages of tce table)\n", + newTceTable->size); } i++; } - -#if 0 - pci_for_each_dev(dev) { - np = pci_device_to_OF_node(dev); - node = np->node; - bus_number = dev->bus->number; - create_pci_bus_tce_table( unsigned long bus_number ); - node = hose->arch_data->node; - } -#endif } + /* Allocates a contiguous real buffer and creates TCEs over it. * Returns the virtual address of the buffer and sets dma_handle * to the dma address (tce) of the first page. @@ -733,9 +893,8 @@ { struct TceTable * tbl; void *ret = NULL; - unsigned order, nPages, bus; + unsigned order, nPages; dma_addr_t tce; - int tceType; PPCDBG(PPCDBG_TCE, "pci_alloc_consistent:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx\n", hwdev); @@ -746,26 +905,8 @@ order = get_order(size); nPages = 1 << order; - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) { - bus = 255; - tceType = TCE_VB; - } - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = get_tce_table_index( hwdev ); - // bus = ISERIES_GET_BUS( hwdev ); - tceType = TCE_PCI; -#else - BUG(); - return NULL; -#endif /* CONFIG_PCI */ - } - - tbl = tceTables[bus]; + tbl = get_tce_table(hwdev); + if ( tbl ) { /* Alloc enough pages (and possibly more) */ ret = (void *)__get_free_pages( GFP_ATOMIC, order ); @@ -773,8 +914,7 @@ /* Page allocation succeeded */ memset(ret, 0, nPages << PAGE_SHIFT); /* Set up tces to cover the allocated range */ - tce = get_tces( tbl, order, ret, nPages, tceType, - PCI_DMA_BIDIRECTIONAL ); + tce = get_tces( tbl, order, ret, nPages, PCI_DMA_BIDIRECTIONAL ); if ( tce == NO_TCE ) { PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tces failed\n" ); free_pages( (unsigned long)ret, order ); @@ -785,9 +925,9 @@ *dma_handle = tce; } } - else - PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); + else PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); } + else PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tce_table failed for 0x%016lx\n", hwdev); PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle); PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: return = 0x%16.16lx\n", ret); @@ -798,7 +938,7 @@ void *vaddr, dma_addr_t dma_handle) { struct TceTable * tbl; - unsigned order, nPages, bus; + unsigned order, nPages; PPCDBG(PPCDBG_TCE, "pci_free_consistent:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, dma_handle, vaddr); @@ -811,31 +951,10 @@ PPCDBG(PPCDBG_TCE, "pci_free_consistent: order=%d, size=%d, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) - bus = 255; - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = get_tce_table_index( hwdev ); - // bus = ISERIES_GET_BUS( hwdev ); -#else - BUG(); - return; -#endif /* CONFIG_PCI */ - } - - if ( bus > 255 ) { - PPCDBG(PPCDBG_TCE, "pci_free_consistent: invalid bus # %d\n", bus ); - PPCDBG(PPCDBG_TCE, "pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); - } - - tbl = tceTables[bus]; + tbl = get_tce_table(hwdev); if ( tbl ) { - free_tces( tbl, dma_handle, order, nPages ); + ppc_md.tce_free(tbl, dma_handle, order, nPages); free_pages( (unsigned long)vaddr, order ); } } @@ -846,51 +965,28 @@ * need not be page aligned, the dma_addr_t returned will point to the same * byte within the page as vaddr. */ -dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) +dma_addr_t pci_map_single(struct pci_dev *hwdev, void *vaddr, + size_t size, int direction ) { struct TceTable * tbl; - dma_addr_t dma_handle; + dma_addr_t dma_handle = NO_TCE; unsigned long uaddr; - unsigned order, nPages, bus; - int tceType; + unsigned order, nPages; PPCDBG(PPCDBG_TCE, "pci_map_single:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, direction, vaddr); if ( direction == PCI_DMA_NONE ) BUG(); - dma_handle = NO_TCE; - uaddr = (unsigned long)vaddr; nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); order = get_order( nPages & PAGE_MASK ); nPages >>= PAGE_SHIFT; - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) { - bus = 255; - tceType = TCE_VB; - } - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = get_tce_table_index( hwdev ); - // bus = ISERIES_GET_BUS( hwdev ); - tceType = TCE_PCI; -#else - BUG(); - return NO_TCE; -#endif /* CONFIG_PCI */ - - } - - tbl = tceTables[bus]; + tbl = get_tce_table(hwdev); if ( tbl ) { - dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, - direction ); + dma_handle = get_tces( tbl, order, vaddr, nPages, direction ); dma_handle |= ( uaddr & ~PAGE_MASK ); } @@ -900,7 +996,7 @@ void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) { struct TceTable * tbl; - unsigned order, nPages, bus; + unsigned order, nPages; PPCDBG(PPCDBG_TCE, "pci_unmap_single:\n"); PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, dma_handle = 0x%16.16lx\n", hwdev, size, direction, dma_handle); @@ -915,31 +1011,10 @@ PPCDBG(PPCDBG_TCE, "pci_unmap_single: order=%d, size=%d, nPages=%d, dma_handle=%016lx\n", order, size, nPages, (unsigned long)dma_handle ); - /* If no pci_dev then use virtual bus */ - if (hwdev == NULL ) - bus = 255; - else { -#ifdef CONFIG_PCI - /* Get the iSeries bus # to use as an index - * into the TCE table array - */ - bus = get_tce_table_index( hwdev ); - // bus = ISERIES_GET_BUS( hwdev ); -#else - BUG(); - return; -#endif /* CONFIG_PCI */ - } - - if ( bus > 255 ) { - PPCDBG(PPCDBG_TCE, "pci_unmap_single: invalid bus # %d\n", bus ); - PPCDBG(PPCDBG_TCE, "pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); - } - - tbl = tceTables[bus]; + tbl = get_tce_table(hwdev); if ( tbl ) - free_tces( tbl, dma_handle, order, nPages ); + ppc_md.tce_free(tbl, dma_handle, order, nPages); } @@ -1077,7 +1152,7 @@ * return the number of TCEs created */ static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, - int nents, unsigned numTces, int tceType, int direction ) + int nents, unsigned numTces, int direction ) { unsigned order, i, j; unsigned long startPage, endPage, prevEndPage, numPages, uaddr; @@ -1113,8 +1188,7 @@ } for (i=0; i 255 ) { - PPCDBG(PPCDBG_TCE, "pci_unmap_sg: invalid bus # %d\n", bus ); - PPCDBG(PPCDBG_TCE, "pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); - } - - - tbl = tceTables[bus]; + tbl = get_tce_table(hwdev); if ( tbl ) - free_tces( tbl, dma_start_page, order, numTces ); - -} - -static unsigned long setTce( unsigned long base, - unsigned long tce_num, - unsigned long tce_data) { - union Tce *tce_addr; - - PPCDBG(PPCDBG_TCE, "setTce:\n"); - PPCDBG(PPCDBG_TCE, "\tbase = 0x%lx\n", base); - PPCDBG(PPCDBG_TCE, "\ttce_num = 0x%lx\n", tce_num); - PPCDBG(PPCDBG_TCE, "\ttce_data = 0x%lx\n", tce_data); - - tce_addr = ((union Tce *)base) + tce_num; - - PPCDBG(PPCDBG_TCE, "\ttce_addr = 0x%lx\n", tce_addr); + ppc_md.tce_free( tbl, dma_start_page, order, numTces ); - *tce_addr = (union Tce)tce_data; - - /* Make sure the update is visible to hardware. */ - __asm__ __volatile__ ("sync" : : : "memory"); - - return(0); } +/* + * phb_tce_table_init + * + * Function: Display TCE config registers. Could be easily changed + * to initialize the hardware to use TCEs. + */ unsigned long phb_tce_table_init(struct pci_controller *phb) { unsigned int r, cfg_rw, i; unsigned long r64; @@ -1320,64 +1335,19 @@ } PPCDBG(PPCDBG_TCEINIT, "phb_tce_table_init: done\n"); - // create_pci_bus_tce_table(phb->number, (unsigned long)phb); -} - -/* - * Reserve a range of TCE table entries. - * - * Inputs: - * tbl TCE Table. - * dma_addr PCI Base Address, must be aligned to 2^(NUM_TCE_LEVELS-1). - * size Size to reserve in pages. Value is rounded up to a multiple - * of (2^(NUM_TCE_LEVELS-1)); ie the min size of the allocation - * is 2^9 * 4096 = 2MB. - * - * This should only be called after the table has been initialized, but - * before it has been used. - */ -static long resv_tce_range_top_level( struct TceTable *tbl, - unsigned long dma_addr, - unsigned size ) -{ - unsigned long i, segments, block; - long tcenum, maxTcenum; - unsigned byte, bit, mask; - unsigned char *map, *bytep; - - PPCDBG(PPCDBG_TCEINIT, "resv_tce_range_top: start\n"); - PPCDBG(PPCDBG_TCEINIT, "\tdma_addr=0x%lx, size=0x%lx\n", - dma_addr, size); - maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1; - tcenum = (dma_addr >> PAGE_SHIFT) & - (~((1UL << (NUM_TCE_LEVELS - 1)) - 1)); - tcenum -= tbl->startOffset; - - PPCDBG(PPCDBG_TCEINIT, "\ttcenum=0x%lx, maxTcenum=0x%lx\n", - tcenum, maxTcenum); - if ( tcenum > maxTcenum ) { - return(-1); - } - - segments = (size + (1UL << (NUM_TCE_LEVELS - 1)) - 1) >> - (NUM_TCE_LEVELS - 1); - map = tbl->mlbm.level[NUM_TCE_LEVELS-1].map; - block = tcenum >> (NUM_TCE_LEVELS -1); - - PPCDBG(PPCDBG_TCEINIT, "\tsegments = 0x%lx, map = 0x%lx\n", - segments, map); - - for(i = 0; i < segments; i++) { - byte = block / 8; - bit = block % 8; - mask = 0x80 >> bit; - bytep = map + byte; - - *bytep = *bytep & (~mask); + return(0); +} - block++; - } +/* These are called very early. */ +void tce_init_pSeries(void) +{ + ppc_md.tce_build = tce_build_pSeries; + ppc_md.tce_free = tce_free_pSeries; +} - return(0); +void tce_init_iSeries(void) +{ + ppc_md.tce_build = tce_build_iSeries; + ppc_md.tce_free = tce_free_iSeries; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pci_dn.c linuxppc64_2_4/arch/ppc64/kernel/pci_dn.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pci_dn.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pci_dn.c Fri Sep 21 09:31:31 2001 @@ -0,0 +1,366 @@ +/* + * pci_dn.c + * + * Copyright (C) 2001 Todd Inglett, IBM Corporation + * + * PCI manipulation via device_nodes. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +/* Traverse_func that inits the PCI fields of the device node. + * NOTE: this *must* be done before read/write config to the device. + */ +static void * __init +update_dn_pci_info(struct device_node *dn, void *data) +{ + struct pci_controller *phb = (struct pci_controller *)data; + u32 *regs; + char *device_type = get_property(dn, "device_type", 0); + + dn->phb = phb; + if (device_type && strcmp(device_type, "pci") == 0 && get_property(dn, "class-code", 0) == 0) { + /* special case for PHB's. Sigh. */ + regs = (u32 *)get_property(dn, "bus-range", 0); + dn->busno = regs[0]; + dn->devfn = 0; /* assumption */ + } else { + regs = (u32 *)get_property(dn, "reg", 0); + if (regs) { + /* First register entry is addr (00BBSS00) */ + dn->busno = (regs[0] >> 16) & 0xff; + dn->devfn = (regs[0] >> 8) & 0xff; + } + } + return NULL; +} + +/* + * Hit all the BARs of all the devices with values from OF. + * This is unnecessary on most systems, but also harmless. + */ +static void * __init +write_OF_bars(struct device_node *dn, void *data) +{ + int i; + u32 oldbar, newbar, newbartest; + u8 config_offset; + char *name = get_property(dn, "name", 0); + char *device_type = get_property(dn, "device_type", 0); + char devname[128]; + sprintf(devname, "%04x:%02x.%x %s (%s)", dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn), name ? name : "", device_type ? device_type : ""); + + if (device_type && strcmp(device_type, "pci") == 0 && + get_property(dn, "class-code", 0) == 0) + return NULL; /* This is probably a phb. Skip it. */ + + if (dn->n_addrs == 0) + return NULL; /* This is normal for some adapters or bridges */ + + if (dn->addrs == NULL) { + /* This shouldn't happen. */ + printk(KERN_WARNING "write_OF_bars %s: device has %d BARs, but no addrs recorded\n", devname, dn->n_addrs); + return NULL; + } + +#ifndef CONFIG_PPC_ISERIES + for (i = 0; i < dn->n_addrs; i++) { + newbar = dn->addrs[i].address; + config_offset = dn->addrs[i].space & 0xff; + if (ppc_md.pcibios_read_config_dword(dn, config_offset, &oldbar) != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING "write_OF_bars %s: read BAR%d failed\n", devname, i); + continue; + } + /* Need to update this BAR. */ + if (ppc_md.pcibios_write_config_dword(dn, config_offset, newbar) != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING "write_OF_bars %s: write BAR%d with 0x%08x failed (old was 0x%08x)\n", devname, i, newbar, oldbar); + continue; + } + /* sanity check */ + if (ppc_md.pcibios_read_config_dword(dn, config_offset, &newbartest) != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING "write_OF_bars %s: sanity test read BAR%d failed?\n", devname, i); + continue; + } + if ((newbar & PCI_BASE_ADDRESS_MEM_MASK) != (newbartest & PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_WARNING "write_OF_bars %s: oops...BAR%d read back as 0x%08x%s!\n", devname, i, newbartest, (oldbar & PCI_BASE_ADDRESS_MEM_MASK) == (newbartest & PCI_BASE_ADDRESS_MEM_MASK) ? " (original value)" : ""); + continue; + } + } +#endif + return NULL; +} + +#if 0 +/* Traverse_func that starts the BIST (self test) */ +static void * __init +startBIST(struct device_node *dn, void *data) +{ + struct pci_controller *phb = (struct pci_controller *)data; + u8 bist; + + char *name = get_property(dn, "name", 0); + udbg_printf("startBIST: %s phb=%p, device=%p\n", name ? name : "", phb, dn); + + if (ppc_md.pcibios_read_config_byte(dn, PCI_BIST, &bist) == PCIBIOS_SUCCESSFUL) { + if (bist & PCI_BIST_CAPABLE) { + udbg_printf(" -> is BIST capable!\n", phb, dn); + /* Start bist here */ + } + } + return NULL; +} +#endif + + +/****************************************************************** + * Traverse a device tree stopping each PCI device in the tree. + * This is done depth first. As each node is processed, a "pre" + * function is called, the children are processed recursively, and + * then a "post" function is called. + * + * The "pre" and "post" funcs return a value. If non-zero + * is returned from the "pre" func, the traversal stops and this + * value is returned. The return value from "post" is not used. + * This return value is useful when using traverse as + * a method of finding a device. + * + * NOTE: we do not run the funcs for devices that do not appear to + * be PCI except for the start node which we assume (this is good + * because the start node is often a phb which may be missing PCI + * properties). + * We use the class-code as an indicator. If we run into + * one of these nodes we also assume its siblings are non-pci for + * performance. + * + ******************************************************************/ +void *traverse_pci_devices(struct device_node *start, traverse_func pre, traverse_func post, void *data) +{ + struct device_node *dn, *nextdn; + void *ret; + + if (pre && (ret = pre(start, data)) != NULL) + return ret; + for (dn = start->child; dn; dn = nextdn) { + nextdn = NULL; + if (get_property(dn, "class-code", 0)) { + if (pre && (ret = pre(dn, data)) != NULL) + return ret; + if (dn->child) { + /* Depth first...do children */ + nextdn = dn->child; + } else if (dn->sibling) { + /* ok, try next sibling instead. */ + nextdn = dn->sibling; + } else { + /* no more children or siblings...call "post" */ + if (post) + post(dn, data); + } + } + if (!nextdn) { + /* Walk up to next valid sibling. */ + do { + dn = dn->parent; + if (dn == start) + return NULL; + } while (dn->sibling == NULL); + nextdn = dn->sibling; + } + } + return NULL; +} + +/* Same as traverse_pci_devices except this does it for all phbs. + */ +void *traverse_all_pci_devices(traverse_func pre) +{ + struct pci_controller* phb; + void *ret; + for (phb=hose_head;phb;phb=phb->next) + if ((ret = traverse_pci_devices((struct device_node *)phb->arch_data, pre, NULL, phb)) != NULL) + return ret; + return NULL; +} + + +/* Traversal func that looks for a value. + * If found, the device_node is returned (thus terminating the traversal). + */ +static void * +is_devfn_node(struct device_node *dn, void *data) +{ + int busno = ((unsigned long)data >> 8) & 0xff; + int devfn = ((unsigned long)data) & 0xff; + return (devfn == dn->devfn && busno == dn->busno) ? dn : NULL; +} + +/* Same as is_devfn_node except ignore the "fn" part of the "devfn". + */ +static void * +is_devfn_sub_node(struct device_node *dn, void *data) +{ + int busno = ((unsigned long)data >> 8) & 0xff; + int devfn = ((unsigned long)data) & 0xf8; + return (devfn == (dn->devfn & 0xf8) && busno == dn->busno) ? dn : NULL; +} + + +/* This is the "slow" path for looking up a device_node from a + * pci_dev. It will hunt for the device under it's parent's + * phb and then update sysdata for a future fastpath. + * + * It may also do fixups on the actual device since this happens + * on the first read/write. + * + * Note that it also must deal with devices that don't exist. + * In this case it may probe for real hardware ("just in case") + * and add a device_node to the device tree if necessary. + * + */ +struct device_node *fetch_dev_dn(struct pci_dev *dev) +{ + struct device_node *orig_dn = (struct device_node *)dev->sysdata; + struct pci_controller *phb = orig_dn->phb; /* assume same phb as orig_dn */ + struct device_node *phb_dn; + struct device_node *dn; + unsigned long searchval = (dev->bus->number << 8) | dev->devfn; + + phb_dn = (struct device_node *)(phb->arch_data); + dn = (struct device_node *)traverse_pci_devices(phb_dn, is_devfn_node, NULL, (void *)searchval); + if (dn) { + dev->sysdata = dn; + /* ToDo: call some device init hook here */ + } else { + /* Now it is very possible that we can't find the device because it is + * not the zero'th device of a mutifunction device and we don't have + * permission to read the zero'th device. If this is the case, Linux + * would ordinarily skip all the other functions. + */ + if ((searchval & 0x7) == 0) { + struct device_node *thisdevdn; + /* Ok, we are looking for fn == 0. Let's check for other functions. */ + thisdevdn = (struct device_node *)traverse_pci_devices(phb_dn, is_devfn_sub_node, NULL, (void *)searchval); + if (thisdevdn) { + /* Ah ha! There does exist a sub function. Now this isn't an exact + * match for searchval, but in order to get Linux to believe the sub + * functions exist we will need to manufacture a fake device_node + * for this zero'th function. To keept this simple for now we only + * handle pci bridges and we just hand back the found node which + * isn't correct, but Linux won't care. + */ + char *device_type = (char *)get_property(thisdevdn, "device_type", 0); + if (device_type && strcmp(device_type, "pci") == 0) { + /* udbg_printf("fetch_dev_dn: substitute a pci bridge node\n"); */ + return thisdevdn; + } + } + } + /* ToDo: device not found...probe for it anyway with a fake dn? + struct device_node fake_dn; + memset(&fake_dn, 0, sizeof(fake_dn)); + fake_dn.phb = phb; + fake_dn.busno = dev->bus->number; + fake_dn.devfn = dev->devfn; + ... now do ppc_md.pcibios_read_config_dword(&fake_dn.....) + ... if ok, alloc a real device_node and dn = real_dn; + */ + } + return dn; +} + + +/****************************************************************** + * Actually initialize the phbs. + * The buswalk on this phb has not happened yet. + ******************************************************************/ +void __init +pci_devs_phb_init(void) +{ + /* This must be done first so the device nodes have valid pci info! */ + traverse_all_pci_devices(update_dn_pci_info); + + /* Hack for regatta which does not init the bars correctly */ + traverse_all_pci_devices(write_OF_bars); +#if 0 + traverse_all_pci_devices(startBIST); + mdelay(5000); + traverse_all_pci_devices(checkBIST); +#endif +} + + +static void __init +pci_fixup_bus_sysdata_list(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct pci_controller *phb; + int newnum; + + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if (bus->self) { + bus->sysdata = bus->self->sysdata; + /* Also fixup the bus number on large bus systems to + * include the PHB# in the next byte + */ + phb = PCI_GET_DN(bus)->phb; + if (phb && phb->buid) { + newnum = (phb->global_number << 8) | bus->number; + bus->number = newnum; + sprintf(bus->name, "PCI Bus #%x", bus->number); + } + } + pci_fixup_bus_sysdata_list(&bus->children); + } +} + + +/****************************************************************** + * Fixup the bus->sysdata ptrs to point to the bus' device_node. + * This is done late in pcibios_init(). We do this mostly for + * sanity, but pci_dma.c uses these at DMA time so they must be + * correct. + * To do this we recurse down the bus hierarchy. Note that PHB's + * have bus->self == NULL, but fortunately bus->sysdata is already + * correct in this case. + ******************************************************************/ +void __init +pci_fix_bus_sysdata(void) +{ + pci_fixup_bus_sysdata_list(&pci_root_buses); +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pmac_nvram.c linuxppc64_2_4/arch/ppc64/kernel/pmac_nvram.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pmac_nvram.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pmac_nvram.c Tue Sep 25 13:52:29 2001 @@ -37,8 +37,6 @@ static int core99_bank = 0; sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; -extern int pmac_newworld; - #define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ #define CORE99_SIGNATURE 0x5a @@ -183,42 +181,6 @@ return 0; } -static void -lookup_partitions(void) -{ - u8 buffer[17]; - int i, offset; - struct chrp_header* hdr; - - if (pmac_newworld) { - nvram_partitions[pmac_nvram_OF] = -1; - nvram_partitions[pmac_nvram_XPRAM] = -1; - nvram_partitions[pmac_nvram_NR] = -1; - hdr = (struct chrp_header *)buffer; - - offset = 0; - do { - for (i=0;i<16;i++) - buffer[i] = nvram_read_byte(offset+i); - if (!strcmp(hdr->name, "common")) - nvram_partitions[pmac_nvram_OF] = offset + 0x10; - if (!strcmp(hdr->name, "APL,MacOS75")) { - nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10; - nvram_partitions[pmac_nvram_NR] = offset + 0x110; - } - offset += (hdr->len * 0x10); - } while(offset < NVRAM_SIZE); - } else { - nvram_partitions[pmac_nvram_OF] = 0x1800; - nvram_partitions[pmac_nvram_XPRAM] = 0x1300; - nvram_partitions[pmac_nvram_NR] = 0x1400; - } -#ifdef DEBUG - printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); - printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); - printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); -#endif -} __init void pmac_nvram_init(void) @@ -260,7 +222,7 @@ #endif for (i=0; iaddrs[0].address, dp->addrs[0].size); nvram_mult = 1; } else if (nvram_naddrs == 1) { diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/pmc.c linuxppc64_2_4/arch/ppc64/kernel/pmc.c --- linux-2.4.9-ac10/arch/ppc64/kernel/pmc.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc.c Tue Sep 25 13:52:29 2001 @@ -102,7 +102,6 @@ int n; unsigned long htab_primary_overflows, htab_capacity_castouts; unsigned long htab_read_to_write_faults; - unsigned long i; htab_primary_overflows = htab_capacity_castouts = 0; htab_read_to_write_faults = n = 0; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ppc-stub.c linuxppc64_2_4/arch/ppc64/kernel/ppc-stub.c --- linux-2.4.9-ac10/arch/ppc64/kernel/ppc-stub.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc-stub.c Fri Sep 14 22:14:50 2001 @@ -737,11 +737,3 @@ return 1; } - -/* I don't know why other platforms don't need this. The function for - * the 8xx is found in arch/ppc/8xx_io/uart.c. -- Dan - */ -void -kgdb_map_scc(void) -{ -} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ppc_asm.h linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h --- linux-2.4.9-ac10/arch/ppc64/kernel/ppc_asm.h Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h Wed Sep 26 03:16:27 2001 @@ -44,34 +44,6 @@ #define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) #define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) -#define SYNC \ - sync; \ - isync - -/* iSeries LPAR uses an event queue to signal I/O events - This code checks the queue */ - -#define CHECKLPQUEUE(ra,rb,rc) \ - mfspr ra,SPRG3; /* Get PACA address */\ - ld ra,PACALPQUEUE(ra); /* Get LpQueue address */\ - cmpi 0,ra,0; /* Does LpQueue exist? */\ - beq 99f; /* No? - then skip rest */\ - ld rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\ - lbz rb,LPEVENTFLAGS(rb); /* Get valid bit */\ - lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\ - andi. ra,rb,0x0080; /* Isolate valid bit */\ - or. ra,ra,rc; /* 0 == no pending events */\ -99: - -#define CHECKDECR(ra,rb) \ - mfspr rb,SPRG3; /* Get Paca address */\ - lbz ra,PACALPPACA+LPPACADECRINT(rb); /* Get DECR int flag */\ - cmpi 0,ra,0; /* DECR occurred in hypervisor ? */\ - beq 99f; /* If not, skip rest */\ - xor ra,ra,ra; \ - stb ra,PACALPPACA+LPPACADECRINT(rb); /* Clear DECR int flag */\ -99: - #define CHECKANYINT(ra,rb) \ mfspr rb,SPRG3; /* Get Paca address */\ ld ra,PACALPPACA+LPPACAANYINT(rb); /* Get pending interrupt flags */\ diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/proc_pcifr.c linuxppc64_2_4/arch/ppc64/kernel/proc_pcifr.c --- linux-2.4.9-ac10/arch/ppc64/kernel/proc_pcifr.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/proc_pcifr.c Thu Sep 27 14:08:16 2001 @@ -28,10 +28,12 @@ #include #include -//#include -//#include +#include +#include +#include #include +#include #include "pci.h" void pci_Fr_TestCode(void); @@ -39,6 +41,14 @@ static spinlock_t proc_pcifr_lock; struct flightRecorder* PciFr = NULL; +extern long Pci_Interrupt_Count; +extern long Pci_Event_Count; +extern long Pci_Io_Read_Count; +extern long Pci_Io_Write_Count; +extern long Pci_Cfg_Read_Count; +extern long Pci_Cfg_Write_Count; +extern long Pci_Error_Count; + /************************************************************************/ /* Forward declares. */ /************************************************************************/ @@ -46,45 +56,121 @@ int proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); int proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data); +static struct proc_dir_entry *pciDev_proc_root = NULL; +int proc_pciDev_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pciDev_write_proc(struct file *file, const char *buffer, unsigned long count, void *data); + /************************************************************************/ /* Create entry ../proc/ppc64/pcifr */ /************************************************************************/ void proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root) { - if (proc_ppc64_root == NULL) return; + if (proc_ppc64_root == NULL) return; - /* Read = User,Group,Other, Write User */ - printk("PCI: Creating ../proc/ppc64/pcifr \n"); - spin_lock(&proc_pcifr_lock); - pciFr_proc_root = create_proc_entry("pcifr", S_IFREG | S_IRUGO | S_IWUSR, proc_ppc64_root); - spin_unlock(&proc_pcifr_lock); - - if (pciFr_proc_root == NULL) return; - - pciFr_proc_root->nlink = 1; - pciFr_proc_root->data = (void *)0; - pciFr_proc_root->read_proc = proc_pciFr_read_proc; - pciFr_proc_root->write_proc = proc_pciFr_write_proc; + /* Read = User,Group,Other, Write User */ + printk("PCI: Creating ../proc/ppc64/pcifr \n"); + spin_lock(&proc_pcifr_lock); + pciFr_proc_root = create_proc_entry("pcifr", S_IFREG | S_IRUGO | S_IWUSR, proc_ppc64_root); + spin_unlock(&proc_pcifr_lock); + + if (pciFr_proc_root == NULL) return; + + pciFr_proc_root->nlink = 1; + pciFr_proc_root->data = (void *)0; + pciFr_proc_root->read_proc = proc_pciFr_read_proc; + pciFr_proc_root->write_proc = proc_pciFr_write_proc; PciFr = alloc_Flight_Recorder(NULL,"PciFr", 4096); - - pci_Fr_TestCode(); + printk("PCI: Creating ../proc/ppc64/pci \n"); + spin_lock(&proc_pcifr_lock); + pciDev_proc_root = create_proc_entry("pci", S_IFREG | S_IRUGO | S_IWUSR, proc_ppc64_root); + spin_unlock(&proc_pcifr_lock); + + if (pciDev_proc_root == NULL) return; + + pciDev_proc_root->nlink = 1; + pciDev_proc_root->data = (void *)0; + pciDev_proc_root->read_proc = proc_pciDev_read_proc; + pciDev_proc_root->write_proc = proc_pciDev_write_proc; } /*******************************************************************************/ /* Get called when client reads the ../proc/ppc64/pcifr. */ /*******************************************************************************/ int proc_pciFr_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { - int BufferLen; - printk("PCI: Dump Flight Recorder!\n"); - BufferLen = fr_Dump(PciFr, page, count); - *eof = BufferLen; - return BufferLen; + int BufferLen; + BufferLen = fr_Dump(PciFr, page, count); + *eof = BufferLen; + return BufferLen; } /*******************************************************************************/ /* Gets called when client writes to ../proc/ppc64/pcifr */ /*******************************************************************************/ int proc_pciFr_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { - pci_Fr_TestCode(); + return count; +} + +/*******************************************************************************/ +/* Get called when client reads the ../proc/ppc64/pcifr. */ +/*******************************************************************************/ +int proc_pciDev_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { + int DeviceCount = 0; + int LineLen = 0; /* Size of new Data */ + struct pci_dev* PciDev; /* Device pointer */ + struct net_device* dev; /* net_device pointer */ + +#ifdef CONFIG_PPC_ISERIES + LineLen += sprintf(page+LineLen,"Pci Events.... %8ld ",Pci_Interrupt_Count); + LineLen += sprintf(page+LineLen,"Pci Interrupts %8ld\n",Pci_Event_Count); +#endif + LineLen += sprintf(page+LineLen,"Pci I/O Reads. %8ld ",Pci_Io_Read_Count); + LineLen += sprintf(page+LineLen,"Pci I/O Writes %8ld\n",Pci_Io_Write_Count); + + LineLen += sprintf(page+LineLen,"Pci Cfg Reads. %8ld ",Pci_Cfg_Read_Count); + LineLen += sprintf(page+LineLen,"Pci Cfg Writes %8ld\n",Pci_Cfg_Write_Count); + + LineLen += sprintf(page+LineLen,"Pci I/O Errors %8ld\n",Pci_Error_Count); + LineLen += sprintf(page+LineLen,"\n"); + +#ifdef CONFIG_PPC_ISERIES + /***************************************************************************/ + /* List the devices */ + /***************************************************************************/ + pci_for_each_dev(PciDev) { + if( PciDev->sysdata != NULL ) { + ++DeviceCount; + + LineLen += sprintf(page+LineLen,"%3d. ",DeviceCount); + LineLen += iSeries_Device_Information(PciDev,page+LineLen,count-LineLen); + LineLen += sprintf(page+LineLen,"\n"); + + /* look for the net devices out */ + for (dev = dev_base; dev != NULL; dev = dev->next) { + if (dev->base_addr == PciDev->resource[0].start ) { /* Yep, a net_device */ + LineLen += sprintf(page+LineLen, " - Net device: %s\n", dev->name); + } /* if */ + } /* for */ + } + else { + LineLen += sprintf(page+LineLen,"No Device Node for %p\n",PciDev); + } + /*********************************************************************/ + /* Run out of room in system buffer. */ + /*********************************************************************/ + if(LineLen+80 >= count) { /* Room for another line? No. bail out */ + LineLen +=sprintf(page+LineLen,"/proc/ppc64/pci file full!\n"); + break; + } + } +#else + LineLen += sprintf(page+LineLen,"List of Pci I/O devices.\n"); +#endif + *eof = LineLen; + return LineLen; +} +/*******************************************************************************/ +/* Gets called when client writes to ../proc/ppc64/pcifr */ +/*******************************************************************************/ +int proc_pciDev_write_proc(struct file *file, const char *buffer, unsigned long count, void *data) { return count; } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/proc_pmc.c linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c --- linux-2.4.9-ac10/arch/ppc64/kernel/proc_pmc.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c Wed Sep 26 11:20:49 2001 @@ -29,7 +29,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include @@ -38,7 +42,7 @@ #include /* pci Flight Recorder AHT */ -extern proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root); +extern void proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root); static int proc_pmc_control_mode = 0; @@ -66,6 +70,8 @@ int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_get_titanTod( char *page, char **start, off_t off, int count, int *eof, void *data); + int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); @@ -120,8 +126,8 @@ if(ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; - ent->read_proc = proc_ppc64_pmc_stab_read; - ent->write_proc = proc_ppc64_pmc_stab_read; + ent->read_proc = (void *)proc_ppc64_pmc_stab_read; + ent->write_proc = (void *)proc_ppc64_pmc_stab_read; } ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, @@ -129,8 +135,8 @@ if(ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; - ent->read_proc = proc_ppc64_pmc_htab_read; - ent->write_proc = proc_ppc64_pmc_htab_read; + ent->read_proc = (void *)proc_ppc64_pmc_htab_read; + ent->write_proc = (void *)proc_ppc64_pmc_htab_read; } } @@ -139,8 +145,8 @@ if(ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_system_root; - ent->read_proc = proc_ppc64_pmc_stab_read; - ent->write_proc = proc_ppc64_pmc_stab_read; + ent->read_proc = (void *)proc_ppc64_pmc_stab_read; + ent->write_proc = (void *)proc_ppc64_pmc_stab_read; } ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, @@ -148,8 +154,8 @@ if(ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_system_root; - ent->read_proc = proc_ppc64_pmc_htab_read; - ent->write_proc = proc_ppc64_pmc_htab_read; + ent->read_proc = (void *)proc_ppc64_pmc_htab_read; + ent->write_proc = (void *)proc_ppc64_pmc_htab_read; } /* Create directories for the hardware counters. */ @@ -159,8 +165,8 @@ if(ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; - ent->read_proc = proc_ppc64_pmc_hw_read; - ent->write_proc = proc_ppc64_pmc_hw_read; + ent->read_proc = (void *)proc_ppc64_pmc_hw_read; + ent->write_proc = (void *)proc_ppc64_pmc_hw_read; } } @@ -169,8 +175,8 @@ if(ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_system_root; - ent->read_proc = proc_ppc64_pmc_hw_read; - ent->write_proc = proc_ppc64_pmc_hw_read; + ent->read_proc = (void *)proc_ppc64_pmc_hw_read; + ent->write_proc = (void *)proc_ppc64_pmc_hw_read; } } @@ -204,8 +210,7 @@ int proc_ppc64_pmc_read(char *page, char **start, off_t off, int count, int *eof, char *buffer) { - int buffer_size, n, i; - unsigned long flags; + int buffer_size, n; if (count < 0) return 0; @@ -315,7 +320,14 @@ ent->data = (void *)0; ent->read_proc = proc_get_lpevents; ent->write_proc = proc_reset_lpevents; - + + ent = create_proc_entry("titanTod", S_IFREG|S_IRUGO, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_get_titanTod; + ent->write_proc = NULL; + pmc_proc_root = proc_mkdir("pmc", iSeries_proc); if (!pmc_proc_root) return; @@ -368,6 +380,12 @@ lpEventTypes[i], (unsigned long)xItLpQueue.xLpIntCountByType[i] ); } + len += sprintf( page+len, "\n events processed by processor:\n" ); + for (i=0; iprocessorCount; ++i) { + len += sprintf( page+len, " CPU%02d %10u\n", + i, xPaca[i].lpEvent_count ); + } + return pmc_calc_metrics( page, start, off, count, eof, len ); } @@ -375,6 +393,55 @@ int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) { return count; +} + +extern unsigned long procFreqHz; +static unsigned long startTitan = 0; +static unsigned long startTb = 0; + + +int proc_get_titanTod +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long tb0, titan_tod; + + tb0 = get_tb(); + titan_tod = HvCallXm_loadTod(); + + len += sprintf( page+len, "Titan\n" ); + len += sprintf( page+len, " time base = %016lx\n", tb0 ); + len += sprintf( page+len, " titan tod = %016lx\n", titan_tod ); + len += sprintf( page+len, " xProcFreq = %016x\n", xIoHriProcessorVpd[0].xProcFreq ); + len += sprintf( page+len, " procFreqHz = %016lx\n", procFreqHz ); + len += sprintf( page+len, " xTimeBaseFreq = %016x\n", xIoHriProcessorVpd[0].xTimeBaseFreq ); + len += sprintf( page+len, " tb_ticks_per_jiffy = %lu\n", tb_ticks_per_jiffy ); + len += sprintf( page+len, " tb_ticks_per_usec = %lu\n", tb_ticks_per_usec ); + + if ( startTitan ) { + unsigned long titan_usec = (titan_tod - startTitan) >> 12; + unsigned long tb_ticks = (tb0 - startTb); + unsigned long titan_jiffies = titan_usec / (1000000/HZ); + unsigned long titan_jiff_usec = titan_jiffies * (1000000/HZ); + unsigned long titan_jiff_rem_usec = titan_usec - titan_jiff_usec; + unsigned long tb_jiffies = tb_ticks / tb_ticks_per_jiffy; + unsigned long tb_jiff_ticks = tb_jiffies * tb_ticks_per_jiffy; + unsigned long tb_jiff_rem_ticks = tb_ticks - tb_jiff_ticks; + unsigned long tb_jiff_rem_usec = tb_jiff_rem_ticks / tb_ticks_per_usec; + unsigned long new_tb_ticks_per_jiffy = (tb_ticks * (1000000/HZ))/titan_usec; + + len += sprintf( page+len, " titan elapsed = %lu uSec\n", titan_usec); + len += sprintf( page+len, " tb elapsed = %lu ticks\n", tb_ticks); + len += sprintf( page+len, " titan jiffies = %lu.%04lu \n", titan_jiffies, titan_jiff_rem_usec ); + len += sprintf( page+len, " tb jiffies = %lu.%04lu\n", tb_jiffies, tb_jiff_rem_usec ); + len += sprintf( page+len, " new tb_ticks_per_jiffy = %lu\n", new_tb_ticks_per_jiffy ); + + } + + startTitan = titan_tod; + startTb = tb0; + + return pmc_calc_metrics( page, start, off, count, eof, len ); } int proc_pmc_get_control diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/process.c linuxppc64_2_4/arch/ppc64/kernel/process.c --- linux-2.4.9-ac10/arch/ppc64/kernel/process.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/process.c Mon Oct 1 11:14:49 2001 @@ -43,6 +43,7 @@ #include #include #include +#include #include int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); @@ -265,14 +266,14 @@ } if ( __get_user(r, &(regs->gpr[i])) ) - goto out; + return; + printk("%016lX ", r); if ((i % 4) == 3) { printk("\n"); } } -out: } void exit_thread(void) @@ -509,81 +510,52 @@ return error; } - -unsigned long find_hpte(unsigned long); - -static __inline__ void set_pp_bit(unsigned long pp, HPTE *addr) -{ - unsigned long old; - unsigned long *p = (unsigned long *)(&(addr->dw1)); - - __asm__ __volatile__( -"1: ldarx %0,0,%3\n\ - rldimi %0,%2,0,62\n\ - stdcx. %0,0,%3\n\ - bne 1b" - : "=&r" (old), "=m" (*p) - : "r" (pp), "r" (p), "m" (*p) - : "cc"); -} - - -static void updateStackHptePP( unsigned long newpp, unsigned long ea ) -{ - unsigned long vsid,va,vpn; - long slot; - - - vsid = get_kernel_vsid( ea ); - va = ( vsid << 28 ) | ( ea & 0x0fffffff ); - vpn = va >> PAGE_SHIFT; - - slot = find_hpte( vpn ); - - - if ( _machine == _MACH_iSeries ) { - HvCallHpt_setPp( slot, newpp ); - } - else { - - HPTE * hptep = htab_data.htab + slot; - - set_pp_bit(newpp , hptep ); - - /* Ensure it is out of the tlb too */ - _tlbie( va ); - - - /* Ensure it is visible before validating */ - __asm__ __volatile__ ("eieio" : : : "memory"); - - - __asm__ __volatile__ ("ptesync" : : : "memory"); - - - } -} - - struct task_struct * alloc_task_struct(void) { - struct task_struct * new_task_ptr; - - new_task_ptr = ((struct task_struct *) __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE))); + struct task_struct * new_task_ptr; - /* Set the second page to be write protected - prevent kernel stack overflow */ - updateStackHptePP( PP_RXRX , (unsigned long)new_task_ptr + PAGE_SIZE ); + new_task_ptr = ((struct task_struct *) + __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE))); - return new_task_ptr; + return new_task_ptr; } void free_task_struct(struct task_struct * task_ptr) { - if (atomic_read(&(virt_to_page(task_ptr)->count)) == 1) { - updateStackHptePP( PP_RWXX , (unsigned long)task_ptr + PAGE_SIZE ); - } - free_pages((unsigned long)(task_ptr), get_order(THREAD_SIZE)); + free_pages((unsigned long)(task_ptr), get_order(THREAD_SIZE)); } +void initialize_paca_hardware_interrupt_stack(void) { + extern struct Naca *naca; + + int i; + unsigned long stack; + unsigned long end_of_stack =0; + + for (i=1; i < naca->processorCount; i++) { + /* Carve out storage for the hardware interrupt stack */ + stack = __get_free_pages(GFP_KERNEL, get_order(8*PAGE_SIZE)); + + if ( !stack ) { + printk("ERROR, cannot find space for hardware stack.\n"); + panic(" no hardware stack "); + } + + + /* Store the stack value in the PACA for the processor */ + xPaca[i].xHrdIntStack = stack + (8*PAGE_SIZE) - STACK_FRAME_OVERHEAD; + xPaca[i].xHrdIntCount = 0; + + } + + + for (i=0; i < naca->processorCount; i++) { + /* set page at the top of stack to be protected - prevent overflow */ + end_of_stack = xPaca[i].xHrdIntStack - (8*PAGE_SIZE - STACK_FRAME_OVERHEAD); + updateBoltedHptePP(PP_RXRX,end_of_stack); + + } + +} void print_backtrace(unsigned long *sp) @@ -605,88 +577,6 @@ printk("\n"); } -#if 0 -/* - * Low level print for debugging - Cort - */ -int __init ll_printk(const char *fmt, ...) -{ - va_list args; - char buf[256]; - int i; - - va_start(args, fmt); - i=vsprintf(buf,fmt,args); - ll_puts(buf); - va_end(args); - return i; -} - -int lines = 24, cols = 80; -int orig_x = 0, orig_y = 0; - -void puthex(unsigned long val) -{ - unsigned char buf[10]; - int i; - for (i = 7; i >= 0; i--) - { - buf[i] = "0123456789ABCDEF"[val & 0x0F]; - val >>= 4; - } - buf[8] = '\0'; - prom_print(buf); -} - -void __init ll_puts(const char *s) -{ - int x,y; - char *vidmem = (char *)/*(_ISA_MEM_BASE + 0xB8000) */0xD00B8000; - char c; - extern int mem_init_done; - - if ( mem_init_done ) /* assume this means we can printk */ - { - printk(s); - return; - } - - /* - * can't ll_puts on chrp without openfirmware yet. - * vidmem just needs to be setup for it. - * -- Cort - */ - if ( _machine != _MACH_prep ) - return; - x = orig_x; - y = orig_y; - - while ( ( c = *s++ ) != '\0' ) { - if ( c == '\n' ) { - x = 0; - if ( ++y >= lines ) { - /*scroll();*/ - /*y--;*/ - y = 0; - } - } else { - vidmem [ ( x + cols * y ) * 2 ] = c; - if ( ++x >= cols ) { - x = 0; - if ( ++y >= lines ) { - /*scroll();*/ - /*y--;*/ - y = 0; - } - } - } - } - - orig_x = x; - orig_y = y; -} -#endif - /* * These bracket the sleeping functions.. */ @@ -705,8 +595,8 @@ sp = p->thread.ksp; do { sp = *(unsigned long *)sp; - if (sp < (stack_page + sizeof(struct task_struct)) || - sp >= (stack_page + (4 * PAGE_SIZE))) + if (sp < (stack_page + (2 * PAGE_SIZE)) || + sp >= (stack_page + (THREAD_SIZE * PAGE_SIZE))) return 0; if (count > 0) { ip = *(unsigned long *)(sp + 16); @@ -730,8 +620,8 @@ sp = p->thread.ksp; do { sp = *(unsigned long *)sp; - if (sp < (stack_page + sizeof(struct task_struct)) || - sp >= (stack_page + (4 * PAGE_SIZE))) + if (sp < (stack_page + (2 * PAGE_SIZE)) || + sp >= (stack_page + (THREAD_SIZE * PAGE_SIZE))) break; if (count > 0) { ip = *(unsigned long *)(sp + 16); diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/prom.c linuxppc64_2_4/arch/ppc64/kernel/prom.c --- linux-2.4.9-ac10/arch/ppc64/kernel/prom.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/prom.c Sat Sep 29 13:39:52 2001 @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef DEBUG_YABOOT #define call_yaboot(FUNC,...) \ @@ -66,12 +67,15 @@ #include #include #include "open_pic.h" +#include #include #ifdef CONFIG_FB #include #endif +extern char _end[]; + /* * prom_init() is called very early on, before the kernel text * and data have been mapped to KERNELBASE. At this point the code @@ -134,9 +138,7 @@ static unsigned long interpret_pci_props(struct device_node *, unsigned long, int, int); -static interpret_func interpret_dbdma_props; static interpret_func interpret_isa_props; -static interpret_func interpret_macio_props; static interpret_func interpret_root_props; #ifndef FB_MAX /* avoid pulling in all of the fb stuff */ @@ -152,13 +154,13 @@ 0, /* disp_node */ {0,0,0,{0},NULL}, /* args */ 0, /* version */ - 32 /* encode_phys_size */ + 32, /* encode_phys_size */ + 0 /* bi_rec pointer */ #ifdef DEBUG_YABOOT ,NULL /* yaboot */ #endif }; -int cpu_hw_index[NR_CPUS] = {0}; char *prom_display_paths[FB_MAX] __initdata = { 0, }; unsigned int prom_num_displays = 0; @@ -166,6 +168,7 @@ extern struct rtas_t rtas; extern unsigned long klimit; +extern unsigned long embedded_sysmap_end; extern struct Naca *naca; extern struct lmb lmb; #ifdef CONFIG_MSCHUNKS @@ -178,10 +181,6 @@ char *bootpath = 0; char *bootdevice = 0; -/* Set for a newworld machine */ -int use_of_interrupt_tree = 0; -int pmac_newworld = 0; - struct device_node *allnodes = 0; static unsigned long call_prom(const char *service, int nargs, int nret, ...); @@ -194,6 +193,8 @@ static unsigned long finish_node_interrupts(struct device_node *, unsigned long); static unsigned long check_display(unsigned long); static int prom_next_node(phandle *); +static void prom_bi_rec_init(unsigned long); +static struct device_node *find_phandle(phandle); #ifdef CONFIG_MSCHUNKS static unsigned long prom_initialize_mschunks(unsigned long); @@ -216,8 +217,7 @@ * mode when we do. We switch back to 64b mode upon return. */ -__init -static unsigned long +static unsigned long __init call_prom(const char *service, int nargs, int nret, ...) { int i; @@ -244,8 +244,7 @@ } -__init -static void +static void __init prom_exit() { unsigned long offset = reloc_offset(); @@ -256,8 +255,7 @@ ; } -__init -void +void __init prom_enter(void) { unsigned long offset = reloc_offset(); @@ -266,8 +264,7 @@ } -__init -void +void __init prom_print(const char *msg) { const char *p, *q; @@ -323,13 +320,14 @@ unsigned long num_cpus = 0; unsigned long offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); - struct lmb *_lmb = PTRRELOC(&lmb); struct Naca *_naca = RELOC(naca); #ifdef DEBUG_PROM prom_print(RELOC("prom_initialize_naca: start...\n")); #endif + _naca->pftSize = 0; /* ilog2 of htab size. computed below. */ + for (node = 0; prom_next_node(&node); ) { type[0] = 0; call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), @@ -429,6 +427,25 @@ _naca->physicalMemorySize = lmb_phys_mem_size(); + if (RELOC(_machine) == _MACH_pSeries) { + unsigned long rnd_mem_size, pteg_count; + + /* round mem_size up to next power of 2 */ + rnd_mem_size = 1UL << __ilog2(_naca->physicalMemorySize); + if (rnd_mem_size < _naca->physicalMemorySize) + rnd_mem_size <<= 1; + + /* # pages / 2 */ + pteg_count = (rnd_mem_size >> (12 + 1)); + + _naca->pftSize = __ilog2(pteg_count << 7); + } + + if (_naca->pftSize == 0) { + prom_print(RELOC("prom: failed to compute pftSize!\n")); + PROM_BUG(); + } + /* * Hardcode to GP size. I am not sure where to get this info * in general, as there does not appear to be a slb-size OF @@ -445,6 +462,10 @@ prom_print_hex(_naca->physicalMemorySize); prom_print_nl(); + prom_print(RELOC("naca->pftSize = 0x")); + prom_print_hex(_naca->pftSize); + prom_print_nl(); + prom_print(RELOC("naca->dCacheL1LineSize = 0x")); prom_print_hex(_naca->dCacheL1LineSize); prom_print_nl(); @@ -477,6 +498,10 @@ prom_print_hex(_naca->interrupt_controller); prom_print_nl(); + prom_print(RELOC("_machine = 0x")); + prom_print_hex(RELOC(_machine)); + prom_print_nl(); + prom_print(RELOC("prom_initialize_naca: end...\n")); #endif @@ -484,8 +509,7 @@ } -__init -static unsigned long +static unsigned long __init prom_initialize_lmb(unsigned long mem) { phandle node; @@ -496,16 +520,18 @@ unsigned long mem_size, lmb_base, lmb_size; unsigned long num_regs, bytes_per_reg = (_prom->encode_phys_size*2)/8; +#ifdef CONFIG_MSCHUNKS #if 1 - /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ - unsigned long io_base = 3UL<<30; - unsigned long io_size = 1UL<<30; + /* Fix me: 630 3G-4G IO hack here... -Peter (PPPBBB) */ + unsigned long io_base = 3UL<<30; + unsigned long io_size = 1UL<<30; unsigned long have_630 = 1; /* assume we have a 630 */ #else unsigned long io_base = ; unsigned long io_size = ; #endif +#endif lmb_init(); @@ -529,6 +555,7 @@ lmb_size = reg.addr64[i].size; } +#ifdef CONFIG_MSCHUNKS if ( lmb_addrs_overlap(lmb_base,lmb_size, io_base,io_size) ) { /* If we really have dram here, then we don't @@ -536,6 +563,7 @@ */ have_630 = 0; } +#endif if ( lmb_add(lmb_base, lmb_size) < 0 ) prom_print(RELOC("Too many LMB's, discarding this one...\n")); else @@ -544,8 +572,10 @@ } +#ifdef CONFIG_MSCHUNKS if ( have_630 && lmb_addrs_overlap(0,mem_size,io_base,io_size) ) lmb_add_io(io_base, io_size); +#endif lmb_analyze(); @@ -557,8 +587,7 @@ } -__init -static unsigned long +static unsigned long __init prom_instantiate_rtas(unsigned long mem) { unsigned long offset = reloc_offset(); @@ -593,22 +622,6 @@ prom_print(RELOC(" at 0x")); prom_print_hex(_rtas->base); - _rtas->event_scan.log = (struct rtas_error_log *)(mem + offset); - call_prom(RELOC("getprop"), - 4, 1, prom_rtas, - RELOC("rtas-error-log-max"), - &getprop_rval, - sizeof(getprop_rval)); - _rtas->event_scan.log_size = getprop_rval; - mem += getprop_rval; - - call_prom(RELOC("getprop"), - 4, 1, prom_rtas, - RELOC("rtas-event-scan-rate"), - &getprop_rval, - sizeof(getprop_rval)); - _rtas->event_scan.rate = getprop_rval; - prom_rtas = (ihandle)call_prom(RELOC("open"), 1, 1, RELOC("/rtas")); prom_print(RELOC("...")); @@ -628,24 +641,15 @@ } #ifdef DEBUG_PROM - prom_print(RELOC("rtas->base = 0x")); + prom_print(RELOC("rtas->base = 0x")); prom_print_hex(_rtas->base); prom_print_nl(); - prom_print(RELOC("rtas->entry = 0x")); + prom_print(RELOC("rtas->entry = 0x")); prom_print_hex(_rtas->entry); prom_print_nl(); - prom_print(RELOC("rtas->size = 0x")); + prom_print(RELOC("rtas->size = 0x")); prom_print_hex(_rtas->size); prom_print_nl(); - prom_print(RELOC("rtas->event_scan.rate = 0x")); - prom_print_hex(_rtas->event_scan.rate); - prom_print_nl(); - prom_print(RELOC("rtas->event_scan.log = 0x")); - prom_print_hex((unsigned long)_rtas->event_scan.log); - prom_print_nl(); - prom_print(RELOC("rtas->event_scan.log_size = 0x")); - prom_print_hex(_rtas->event_scan.log_size); - prom_print_nl(); #endif } #ifdef DEBUG_PROM @@ -894,8 +898,9 @@ extern unsigned long __secondary_hold_acknowledge; unsigned long *spinloop = __v2a(&__secondary_hold_spinloop); unsigned long *acknowledge = __v2a(&__secondary_hold_acknowledge); - unsigned long secondary_hold = __v2a(*PTRRELOC((unsigned long *)__secondary_hold)); + unsigned long secondary_hold = (unsigned long)__v2a(*PTRRELOC((unsigned long *)__secondary_hold)); struct Naca *_naca = RELOC(naca); + struct Paca *_xPaca = PTRRELOC(&xPaca[0]); struct prom_t *_prom = PTRRELOC(&prom); /* Initially, we must have one active CPU. */ @@ -983,7 +988,7 @@ prom_print_hex(reg); prom_print_nl(); #endif - RELOC(cpu_hw_index)[cpuid] = reg; + _xPaca[cpuid].xHwProcNum = reg; prom_print(RELOC("starting cpu ")); prom_print(path); @@ -1048,8 +1053,7 @@ * handling exceptions and the MMU hash table for us. */ -__init -unsigned long +unsigned long __init prom_init(unsigned long r3, unsigned long r4, unsigned long pp, unsigned long r6, unsigned long r7, yaboot_debug_t *yaboot) { @@ -1063,10 +1067,18 @@ unsigned long phys; u32 getprop_rval; struct Naca *_naca = RELOC(naca); + struct Paca *_xPaca = PTRRELOC(&xPaca[0]); struct prom_t *_prom = PTRRELOC(&prom); + /* Default machine type. */ + RELOC(_machine) = _MACH_pSeries; + /* Reset klimit to take into account the embedded system map */ + if (RELOC(embedded_sysmap_end)) + RELOC(klimit) = KERNELBASE + PAGE_ALIGN(RELOC(embedded_sysmap_end)); + /* Get a handle to the prom entry point before anything else */ _prom->entry = pp; + _prom->bi_recs = (struct bi_record *)r6; #ifdef DEBUG_YABOOT call_yaboot(yaboot->dummy,offset>>32,offset&0xffffffff); @@ -1181,7 +1193,7 @@ cpu_pkg, RELOC("reg"), &getprop_rval, sizeof(getprop_rval)); _prom->cpu = (int)(unsigned long)getprop_rval; - cpu_hw_index[0] = _prom->cpu; + _xPaca[0].xHwProcNum = _prom->cpu; #ifdef DEBUG_PROM prom_print(RELOC("Booting CPU hw index = 0x")); @@ -1206,6 +1218,8 @@ mem = prom_initialize_lmb(mem); + prom_bi_rec_init(mem); + mem = prom_instantiate_rtas(mem); /* Initialize some system info into the Naca early... */ @@ -1229,7 +1243,8 @@ lmb_reserve(0, __pa(RELOC(klimit))); - prom_initialize_tce_table(); + if(RELOC(_machine) == _MACH_pSeries) + prom_initialize_tce_table(); if ((long) call_prom(RELOC("getprop"), 4, 1, _prom->chosen, @@ -1288,8 +1303,7 @@ * So we check whether we will need to open the display, * and if so, open it now. */ -__init -static unsigned long +static unsigned long __init check_display(unsigned long mem) { phandle node; @@ -1341,7 +1355,7 @@ prom_print(RELOC("... ok\n")); if (_prom->disp_node == 0) - _prom->disp_node = (ihandle)node; + _prom->disp_node = (ihandle)(unsigned long)node; /* Setup a useable color table when the appropriate * method is available. Should update this to set-colors */ @@ -1378,8 +1392,7 @@ return DOUBLEWORD_ALIGN(mem); } -__init -static int +static int __init prom_next_node(phandle *nodep) { phandle node; @@ -1401,8 +1414,7 @@ /* * Make a copy of the device tree from the PROM. */ -__init -static unsigned long +static unsigned long __init copy_device_tree(unsigned long mem_start) { phandle root; @@ -1504,25 +1516,11 @@ * It traverses the device tree and fills in the name, type, * {n_}addrs and {n_}intrs fields of each node. */ -__init -void +void __init finish_device_tree(void) { unsigned long mem = klimit; - /* All newworld machines now use the interrupt tree */ - struct device_node *np = allnodes; - - while(np) { - if (get_property(np, "interrupt-parent", 0)) { - pmac_newworld = 1; // This is true on the RS6000-260 - break; - } - np = np->allnext; - } - if (pmac_newworld) use_of_interrupt_tree = 1; - udbg_printf("use_of_interrupt_tree = 0x%lx\n", use_of_interrupt_tree); - mem = finish_node(allnodes, mem, NULL, 0, 0); dev_tree_size = mem - (unsigned long) allnodes; @@ -1534,9 +1532,7 @@ rtas.dev = find_devices("rtas"); } - -__init -static unsigned long +static unsigned long __init finish_node(struct device_node *np, unsigned long mem_start, interpret_func *ifunc, int naddrc, int nsizec) { @@ -1550,9 +1546,7 @@ if (ifunc != NULL) { mem_start = ifunc(np, mem_start, naddrc, nsizec); } - if (use_of_interrupt_tree) { - mem_start = finish_node_interrupts(np, mem_start); - } + mem_start = finish_node_interrupts(np, mem_start); /* Look for #address-cells and #size-cells properties. */ ip = (int *) get_property(np, "#address-cells", 0); @@ -1575,10 +1569,6 @@ ifunc = NULL; else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) ifunc = interpret_pci_props; - else if (!strcmp(np->type, "dbdma")) - ifunc = interpret_dbdma_props; - else if (!strcmp(np->type, "mac-io")) - ifunc = interpret_macio_props; else if (!strcmp(np->type, "isa")) ifunc = interpret_isa_props; @@ -1600,7 +1590,7 @@ { /* Finish this node */ unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; - phandle *parent; + phandle *parent, map_parent; struct device_node *node, *parent_node; int l, isize, ipsize, asize, map_size, regpsize; @@ -1630,20 +1620,12 @@ */ if (get_property(node, "interrupt-controller", &l)) { int i,j; - int cvt_irq; - /* XXX on chrp, offset interrupt numbers for the - 8259 by 0, those for the openpic by 16 */ - // DRENG Condor -- this info is not actually known yet ... - cvt_irq = _machine == _MACH_chrp - && get_property(node, "interrupt-parent", NULL) == 0; np->intrs = (struct interrupt_info *) mem_start; np->n_intrs = ipsize / isize; mem_start += np->n_intrs * sizeof(struct interrupt_info); for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = *interrupts++; - if (cvt_irq) - np->intrs[i].line = openpic_to_irq(np->intrs[i].line); + np->intrs[i].line = openpic_to_irq(*interrupts++); np->intrs[i].sense = 1; if (isize > 1) np->intrs[i].sense = *interrupts++; @@ -1662,7 +1644,7 @@ else map = NULL; if (map && l) { - int i, found, temp_isize; + int i, found, temp_isize, temp_asize; map_size = l>>2; map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); asizep = (unsigned int *)get_property(node, "#address-cells", &l); @@ -1687,26 +1669,33 @@ map++; map_size--; } - parent = *((phandle *)(map)); + map_parent = *((phandle *)map); map+=1; map_size-=1; - parent_node = find_phandle(parent); + parent_node = find_phandle(map_parent); temp_isize = isize; + temp_asize = 0; if (parent_node) { isizep = (unsigned int *)get_property(parent_node, "#interrupt-cells", &l); if (isizep) temp_isize = *isizep; + asizep = (unsigned int *)get_property(parent_node, "#address-cells", &l); + if (asizep && l == sizeof(unsigned int)) + temp_asize = *asizep; } if (!found) { - map += temp_isize; - map_size-=temp_isize; + map += temp_isize + temp_asize; + map_size -= temp_isize + temp_asize; } } if (found) { + /* Mapped to a new parent. Use the reg and interrupts specified in + * the map as the new search parameters. Then search from the parent. + */ node = parent_node; - reg = NULL; - regpsize = 0; - interrupts = (unsigned int *)map; - ipsize = temp_isize*1; + reg = map; + regpsize = temp_asize; + interrupts = map + temp_asize; + ipsize = temp_isize; continue; } } @@ -1735,8 +1724,9 @@ ip = (int *) get_property(np, "#address-cells", 0); if (ip != NULL) return *ip; - } while(np->parent); - return 0; + } while (np->parent); + /* No #address-cells property for the root node, default to 1 */ + return 1; } int @@ -1749,19 +1739,18 @@ ip = (int *) get_property(np, "#size-cells", 0); if (ip != NULL) return *ip; - } while(np->parent); - return 0; + } while (np->parent); + /* No #size-cells property for the root node, default to 1 */ + return 1; } -__init -static unsigned long +static unsigned long __init interpret_pci_props(struct device_node *np, unsigned long mem_start, int naddrc, int nsizec) { struct address_range *adr; struct pci_reg_property *pci_addrs; - int i, l, *ip, ml; - struct pci_intr_map *imp; + int i, l; pci_addrs = (struct pci_reg_property *) get_property(np, "assigned-addresses", &l); @@ -1769,7 +1758,6 @@ i = 0; adr = (struct address_range *) mem_start; while ((l -= sizeof(struct pci_reg_property)) >= 0) { - /* XXX assumes PCI addresses mapped 1-1 to physical */ adr[i].space = pci_addrs[i].addr.a_hi; adr[i].address = pci_addrs[i].addr.a_lo; adr[i].size = pci_addrs[i].size_lo; @@ -1779,195 +1767,16 @@ np->n_addrs = i; mem_start += i * sizeof(struct address_range); } - - if (use_of_interrupt_tree) - return mem_start; - - /* - * If the pci host bridge has an interrupt-map property, - * look for our node in it. - */ - if (np->parent != 0 && pci_addrs != 0 - && (imp = (struct pci_intr_map *) - get_property(np->parent, "interrupt-map", &ml)) != 0 - && (ip = (int *) get_property(np, "interrupts", &l)) != 0) { - unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00; - unsigned int cell_size; - struct device_node* np2; - /* This is hackish, but is only used for BootX booting */ - cell_size = sizeof(struct pci_intr_map); - np2 = np->parent; - while(np2) { - if (device_is_compatible(np2, "uni-north")) { - cell_size += 4; - break; - } - np2 = np2->parent; - } - np->n_intrs = 0; - np->intrs = (struct interrupt_info *) mem_start; - for (i = 0; (ml -= cell_size) >= 0; ++i) { - if (imp->addr.a_hi == devfn) { - np->intrs[np->n_intrs].line = imp->intr; - np->intrs[np->n_intrs].sense = 1; /* FIXME */ - ++np->n_intrs; - } - imp = (struct pci_intr_map *)(((unsigned long)imp) - + cell_size); - } - if (np->n_intrs == 0) - np->intrs = 0; - mem_start += np->n_intrs * sizeof(struct interrupt_info); - return mem_start; - } - - ip = (int *) get_property(np, "AAPL,interrupts", &l); - if (ip == 0) - ip = (int *) get_property(np, "interrupts", &l); - if (ip != 0) { - np->intrs = (struct interrupt_info *) mem_start; - np->n_intrs = l / sizeof(int); - mem_start += np->n_intrs * sizeof(struct interrupt_info); - for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = *ip++; - np->intrs[i].sense = 1; - } - } - return mem_start; } -__init -static unsigned long -interpret_dbdma_props(struct device_node *np, unsigned long mem_start, - int naddrc, int nsizec) -{ - struct reg_property *rp; - struct address_range *adr; - unsigned long base_address; - int i, l, *ip; - struct device_node *db; - - base_address = 0; - for (db = np->parent; db != NULL; db = db->parent) { - if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { - base_address = db->addrs[0].address; - break; - } - } - - rp = (struct reg_property *) get_property(np, "reg", &l); - if (rp != 0 && l >= sizeof(struct reg_property)) { - i = 0; - adr = (struct address_range *) mem_start; - while ((l -= sizeof(struct reg_property)) >= 0) { - adr[i].space = 0; - adr[i].address = rp[i].address + base_address; - adr[i].size = rp[i].size; - ++i; - } - np->addrs = adr; - np->n_addrs = i; - mem_start += i * sizeof(struct address_range); - } - - if (use_of_interrupt_tree) - return mem_start; - - ip = (int *) get_property(np, "AAPL,interrupts", &l); - if (ip == 0) - ip = (int *) get_property(np, "interrupts", &l); - if (ip != 0) { - np->intrs = (struct interrupt_info *) mem_start; - np->n_intrs = l / sizeof(int); - mem_start += np->n_intrs * sizeof(struct interrupt_info); - for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = *ip++; - np->intrs[i].sense = 1; - } - } - - return mem_start; -} - -__init -static unsigned long -interpret_macio_props(struct device_node *np, unsigned long mem_start, - int naddrc, int nsizec) -{ - struct reg_property *rp; - struct address_range *adr; - unsigned long base_address; - int i, l, keylargo, *ip; - struct device_node *db; - - base_address = 0; - for (db = np->parent; db != NULL; db = db->parent) { - if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { - base_address = db->addrs[0].address; - keylargo = device_is_compatible(db, "Keylargo"); - break; - } - } - - rp = (struct reg_property *) get_property(np, "reg", &l); - if (rp != 0 && l >= sizeof(struct reg_property)) { - i = 0; - adr = (struct address_range *) mem_start; - while ((l -= sizeof(struct reg_property)) >= 0) { - adr[i].space = 0; - adr[i].address = rp[i].address + base_address; - adr[i].size = rp[i].size; - ++i; - } - np->addrs = adr; - np->n_addrs = i; - mem_start += i * sizeof(struct address_range); - } - - if (use_of_interrupt_tree) - return mem_start; - - ip = (int *) get_property(np, "interrupts", &l); - if (ip == 0) - ip = (int *) get_property(np, "AAPL,interrupts", &l); - if (ip != 0) { - np->intrs = (struct interrupt_info *) mem_start; - if (_machine == _MACH_Pmac) { - /* for the iMac */ - np->n_intrs = l / sizeof(int); - /* Hack for BootX on Core99 */ - if (keylargo) - np->n_intrs = np->n_intrs/2; - for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = *ip++; - if (keylargo) - np->intrs[i].sense = *ip++; - else - np->intrs[i].sense = 1; - } - } else { - /* CHRP machines */ - np->n_intrs = l / (2 * sizeof(int)); - for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = openpic_to_irq(*ip++); - np->intrs[i].sense = *ip++; - } - } - mem_start += np->n_intrs * sizeof(struct interrupt_info); - } - - return mem_start; -} - -__init -static unsigned long +static unsigned long __init interpret_isa_props(struct device_node *np, unsigned long mem_start, int naddrc, int nsizec) { struct isa_reg_property *rp; struct address_range *adr; - int i, l, *ip; + int i, l; rp = (struct isa_reg_property *) get_property(np, "reg", &l); if (rp != 0 && l >= sizeof(struct isa_reg_property)) { @@ -1985,30 +1794,15 @@ mem_start += i * sizeof(struct address_range); } - if (use_of_interrupt_tree) - return mem_start; - - ip = (int *) get_property(np, "interrupts", &l); - if (ip != 0) { - np->intrs = (struct interrupt_info *) mem_start; - np->n_intrs = l / (2 * sizeof(int)); - mem_start += np->n_intrs * sizeof(struct interrupt_info); - for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = *ip++; - np->intrs[i].sense = *ip++; - } - } - return mem_start; } -__init -static unsigned long +static unsigned long __init interpret_root_props(struct device_node *np, unsigned long mem_start, int naddrc, int nsizec) { struct address_range *adr; - int i, l, *ip; + int i, l; unsigned int *rp; int rpsize = (naddrc + nsizec) * sizeof(unsigned int); @@ -2028,22 +1822,6 @@ mem_start += i * sizeof(struct address_range); } - if (use_of_interrupt_tree) - return mem_start; - - ip = (int *) get_property(np, "AAPL,interrupts", &l); - if (ip == 0) - ip = (int *) get_property(np, "interrupts", &l); - if (ip != 0) { - np->intrs = (struct interrupt_info *) mem_start; - np->n_intrs = l / sizeof(int); - mem_start += np->n_intrs * sizeof(struct interrupt_info); - for (i = 0; i < np->n_intrs; ++i) { - np->intrs[i].line = *ip++; - np->intrs[i].sense = 1; - } - } - return mem_start; } @@ -2059,8 +1837,6 @@ /* default to level-triggered */ memset(senses, 1, max - off); - if (!use_of_interrupt_tree) - return; for (np = allnodes; np != 0; np = np->allnext) { for (j = 0; j < np->n_intrs; j++) { @@ -2074,7 +1850,6 @@ /* * Construct and return a list of the device_nodes with a given name. */ -__openfirmware struct device_node * find_devices(const char *name) { @@ -2094,7 +1869,6 @@ /* * Construct and return a list of the device_nodes with a given type. */ -__openfirmware struct device_node * find_type_devices(const char *type) { @@ -2111,35 +1885,10 @@ return head; } -/* Finds a device node given its PCI bus number, device number - * and function number - */ -__openfirmware -struct device_node * -find_pci_device_OFnode(unsigned char bus, unsigned char dev_fn) -{ - struct device_node* np; - unsigned int *reg; - int l; - - for (np = allnodes; np != 0; np = np->allnext) { - if (np->parent == NULL || np->parent->type == NULL - || strcmp(np->parent->type, "pci") != 0) - continue; - reg = (unsigned int *) get_property(np, "reg", &l); - if (reg == 0 || l < sizeof(struct reg_property)) - continue; - if (((reg[0] >> 8) & 0xff) == dev_fn && ((reg[0] >> 16) & 0xff) == bus) - break; - } - return np; -} - /* * Returns all nodes linked together */ -__openfirmware -struct device_node * +struct device_node * __openfirmware find_all_nodes(void) { struct device_node *head, **prevp, *np; @@ -2156,7 +1905,6 @@ /* Checks if the given "compat" string matches one of the strings in * the device's "compatible" property */ -__openfirmware int device_is_compatible(struct device_node *device, const char *compat) { @@ -2182,7 +1930,6 @@ * Indicates whether the root node has a given value in its * compatible property. */ -__openfirmware int machine_is_compatible(const char *compat) { @@ -2198,7 +1945,6 @@ * Construct and return a list of the device_nodes with a given type * and compatible property. */ -__openfirmware struct device_node * find_compatible_devices(const char *type, const char *compat) { @@ -2221,7 +1967,6 @@ /* * Find the device_node with a given full_name. */ -__openfirmware struct device_node * find_path_device(const char *path) { @@ -2236,8 +1981,7 @@ /* * Find the device_node with a given phandle. */ -__openfirmware -struct device_node * +static struct device_node * __init find_phandle(phandle ph) { struct device_node *np; @@ -2252,7 +1996,6 @@ * Find a property with a given name for a given node * and return the value. */ -__openfirmware unsigned char * get_property(struct device_node *np, const char *name, int *lenp) { @@ -2270,8 +2013,7 @@ /* * Add a property to a node */ -__openfirmware -void +void __openfirmware prom_add_property(struct device_node* np, struct property* prop) { struct property **next = &np->properties; @@ -2283,8 +2025,7 @@ } #if 0 -__openfirmware -void +void __openfirmware print_properties(struct device_node *np) { struct property *pp; @@ -2336,8 +2077,7 @@ #endif -__init -void +void __init abort() { #ifdef CONFIG_XMON @@ -2345,6 +2085,35 @@ #endif for (;;) prom_exit(); +} + + +static void +prom_bi_rec_init(unsigned long mem) +{ + unsigned long offset = reloc_offset(); + struct prom_t *_prom = PTRRELOC(&prom); + struct bi_record *rec; + + if ( _prom->bi_recs == NULL) + return; + + for (rec=_prom->bi_recs; rec->tag != BI_LAST ;rec=bi_rec_next(rec) ) + { + switch (rec->tag) + { +#ifdef CONFIG_BLK_DEV_INITRD + case BI_INITRD: + lmb_reserve(rec->data[0], rec->data[1]); + break; +#endif /* CONFIG_BLK_DEV_INITRD */ + } + } + /* The next use of this field will be after relocation + * is enabled, so convert this physical address into a + * virtual address. + */ + _prom->bi_recs = __va(_prom->bi_recs); } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ptrace.c linuxppc64_2_4/arch/ppc64/kernel/ptrace.c --- linux-2.4.9-ac10/arch/ppc64/kernel/ptrace.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ptrace.c Thu Sep 13 11:54:36 2001 @@ -57,7 +57,7 @@ static inline int put_reg(struct task_struct *task, int regno, unsigned long data) { - if (regno <= PT_MQ) { + if (regno < PT_SOFTE) { if (regno == PT_MSR) data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ptrace32.c linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c --- linux-2.4.9-ac10/arch/ppc64/kernel/ptrace32.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c Thu Sep 13 11:54:36 2001 @@ -57,7 +57,7 @@ */ static inline int put_reg(struct task_struct *task, int regno, unsigned long data) { - if (regno <= PT_MQ) + if (regno < PT_SOFTE) { if (regno == PT_MSR) data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); @@ -319,7 +319,7 @@ if ((addr & 3) || numReg > PT_FPSCR) break; /* Insure it is a register we let them change */ - if ((numReg == PT_ORIG_R3) || ((numReg > PT_MQ) && (numReg < PT_FPR0))) + if ((numReg == PT_ORIG_R3) || ((numReg > PT_CCR) && (numReg < PT_FPR0))) break; if (numReg >= PT_FPR0) diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/ras.c linuxppc64_2_4/arch/ppc64/kernel/ras.c --- linux-2.4.9-ac10/arch/ppc64/kernel/ras.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ras.c Wed Sep 26 13:41:52 2001 @@ -0,0 +1,166 @@ + +/* + * pmc.c + * Copyright (C) 2001 Dave Engebretsen IBM Corporation + * + * 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 + */ + +/* Change Activity: + * 2001/09/21 : engebret : Created with minimal EPOW and HW exception support. + * End Change Activity + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs); +static void ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs); +void init_ras_IRQ(void); + +/* #define DEBUG */ + +/* + * Initialize handlers for the set of interrupts caused by hardware errors + * and power system events. + */ +void init_ras_IRQ(void) { + struct device_node *np; + unsigned int *ireg, len, i; + + if((np = find_path_device("/event-sources/internal-errors")) && + (ireg = (unsigned int *)get_property(np, "open-pic-interrupt", + &len))) { + for(i=0; i<(len / sizeof(*ireg)); i++) { + request_irq((*(ireg)) + NUM_8259_INTERRUPTS, + &ras_error_interrupt, 0, + "RAS_ERROR", NULL); + ireg++; + } + } + + if((np = find_path_device("/event-sources/epow-events")) && + (ireg = (unsigned int *)get_property(np, "open-pic-interrupt", + &len))) { + for(i=0; i<(len / sizeof(*ireg)); i++) { + request_irq((*(ireg)) + NUM_8259_INTERRUPTS, + &ras_epow_interrupt, 0, + "RAS_EPOW", NULL); + ireg++; + } + } +} + +/* + * Handle power subsystem events (EPOW). + * + * Presently we just log the event has occured. This should be fixed + * to examine the type of power failure and take appropriate action where + * the time horizon permits something useful to be done. + */ +static void +ras_epow_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct rtas_error_log log_entry; + unsigned int size = sizeof(log_entry); + long status = 0xdeadbeef; + + status = call_rtas("check-exception", 6, 1, NULL, + 0x500, irq, + EPOW_WARNING | POWERMGM_EVENTS, + 1, /* Time Critical */ + __pa(&log_entry), size); + + udbg_printf("EPOW <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + printk(KERN_WARNING + "EPOW <0x%lx 0x%lx>\n",*((unsigned long *)&log_entry), status); +} + +/* + * Handle hardware error interrupts. + * + * RTAS check-exception is called to collect data on the exception. If + * the error is deemed recoverable, we log a warning and return. + * For nonrecoverable errors, an error is logged and we stop all processing + * as quickly as possible in order to prevent propagation of the failure. + */ +static void +ras_error_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct rtas_error_log log_entry; + unsigned int size = sizeof(log_entry); + long status = 0xdeadbeef; + + status = call_rtas("check-exception", 6, 1, NULL, + 0x500, irq, + INTERNAL_ERROR, + 1, /* Time Critical */ + __pa(&log_entry), size); + + if((status != 1) && + (log_entry.severity >= SEVERITY_ERROR_SYNC)) { + udbg_printf("HW Error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + printk(KERN_EMERG + "Error: Fatal hardware error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + +#ifndef DEBUG + /* Don't actually power off when debugging so we can test + * without actually failing while injecting errors. + */ + ppc_md.power_off(); +#endif + } else { + udbg_printf("Recoverable HW Error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + printk(KERN_WARNING + "Warning: Recoverable hardware error <0x%lx 0x%lx>\n", + *((unsigned long *)&log_entry), status); + + return; + } +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/rtas-eventscan.c linuxppc64_2_4/arch/ppc64/kernel/rtas-eventscan.c --- linux-2.4.9-ac10/arch/ppc64/kernel/rtas-eventscan.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas-eventscan.c Wed Dec 31 18:00:00 1969 @@ -1,217 +0,0 @@ -/* - * arch/ppc64/kernel/rtas-eventscan.c - * - * Copyright (c) 2000 Tilmann Bitterberg - * (tilmann@bitterberg.de) - * - * Error processing of errors found by rtas even-scan routine - * which is done with every heartbeat. (chrp_setup.c) - */ - -#include -#include - -#include /* for ppc_md */ -#include -#include - -static long rtas_handle_error_log(struct rtas_error_log *, long, char *); -static const char *rtas_errorlog_check_type (struct rtas_error_log *); - -/* ****************************************************************** */ - -#define rtas_errorlog_check_severity(x) \ - (_errlog_severity[x->severity]) -#define rtas_errorlog_check_target(x) \ - (_errlog_target[x->target]) -#define rtas_errorlog_check_initiator(x) \ - (_errlog_initiator[x->initiator]) -#define rtas_errorlog_check_extended(x) \ - (_errlog_extended[x->extended]) -#define rtas_errorlog_disect_extended(x) \ - do { /* implement me */ } while(0) - -/* ****************************************************************** */ -char rtas_event_buffer[NR_CPUS][1024]; - -long -rtas_event_scan(void) -{ - struct rtas_error_log *log = rtas.event_scan.log; - unsigned long size = rtas.event_scan.log_size; - long cnt = 0; - char *buf; - - buf = rtas_event_buffer[smp_processor_id()]; - - for(;;) { - long status = 1; - call_rtas( "event-scan", 4, 1, &status, EVENT_SCAN_ALL_EVENTS, - 0, __pa(log), size); - - if ( status == 1 ) /* No errors/events found */ - break; - - /* New error log returned */ - cnt += 1; - if (rtas_handle_error_log(log, status, buf) != 0) - printk("%s", buf); - - if (status == -1) { - /* Hardware error */ - panic("RTAS: event-scan reported hardware error\n"); - return 0; - } - } - ppc_md.heartbeat_count = ppc_md.heartbeat_reset; - return cnt; -} - - -/* ****************************************************************** */ -/* - * EVENT-SCAN - * The whole stuff below here doesn't take any action when it found - * an error, it just prints as much information as possible and - * then its up to the user to decide what to do. - * - * Returns 0 if no errors were found - * Returns 1 if there may be more errors - */ -static long -rtas_handle_error_log(struct rtas_error_log *log, long status, char *buffer) -{ -const char *_errlog_severity[] = { -#ifdef VERBOSE_ERRORS - "No Error\n\t\ -Should require no further information", - "Event\n\t\ -This is not really an error, it is an event. I use events\n\t\ -to communicate with RTAS back and forth.", - "Warning\n\t\ -Indicates a non-state-losing error, either fully recovered\n\t\ -by RTAS or not needing recovery. Ignore it.", - "Error sync\n\t\ -May only be fatal to a certain program or thread. Recovery\n\t\ -and continuation is possible, if I only had a handler for\n\t\ -this. Less serious", - "Error\n\t\ -Less serious, but still causing a loss of data and state.\n\t\ -I can't tell you exactly what to do, You have to decide\n\t\ -with help from the target and initiator field, what kind\n\t\ -of further actions may take place.", - "Fatal\n\t\ -Represent a permanent hardware failure and I believe this\n\t\ -affects my overall performance and behaviour. I would not\n\t\ -attempt to continue normal operation." -#else - "No Error", - "Event", - "Warning", - "Error sync", - "Error", - "Fatal" -#endif /* VERBOSE_ERRORS */ -}; - -const char *_errlog_extended[] = { -#ifdef VERBOSE_ERRORS - "Not present\n\t\ -Sad, the RTAS call didn't return an extended error log.", - "Present\n\t\ -The extended log is present and hopefully it contains a lot of\n\t\ -useful information, which leads to the solution of the problem." -#else - "Not present", - "Present" -#endif /* VERBOSE_ERRORS */ -}; - -const char *_errlog_initiator[] = { - "Unknown or not applicable", - "CPU", - "PCI", - "ISA", - "Memory", - "Power management" -}; - -const char *_errlog_target[] = { - "Unknown or not applicable", - "CPU", - "PCI", - "ISA", - "Memory", - "Power management" -}; - int n = 0; - - if (status == -1) { - n += sprintf ( buffer+n, KERN_WARNING "event-scan: " - "Unable to get errors. Do you a " - "favor and throw this box away\n"); - return n; - } - n += sprintf ( buffer+n, KERN_INFO "event-scan: " - "Error Log version %u\n", log->version); - - switch (log->disposition) { - case DISP_FULLY_RECOVERED: - /* there was an error, but everything is fine now */ - break; - case DISP_NOT_RECOVERED: - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "We have a really serious problem!\n"); - case DISP_LIMITED_RECOVERY: - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "Error classification\n"); - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "Severity : %s\n", - rtas_errorlog_check_severity (log)); - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "Initiator : %s\n", - rtas_errorlog_check_initiator (log)); - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "Target : %s\n", - rtas_errorlog_check_target (log)); - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "Type : %s\n", - rtas_errorlog_check_type (log)); - n += sprintf (buffer+n, KERN_WARNING "event-scan: " - "Ext. log : %s\n", - rtas_errorlog_check_extended (log)); - if (log->extended) - rtas_errorlog_disect_extended (log); - break; - default: - /* nothing */ - break; - } - return n; -} - -/* ****************************************************************** */ - -static const char * -rtas_errorlog_check_type (struct rtas_error_log *log) -{ - const char *_errlog_type[] = { - "unknown type", - "too many tries failed", - "TCE error", - "RTAS device failed", - "target timed out", - "parity error on data", /* 5 */ - "parity error on address", - "parity error on external cache", - "access to invalid address", - "uncorrectable ECC error", - "corrected ECC error" /* 10 */ - }; - if (log->type == TYPE_EPOW) - return "EPOW"; - if (log->type >= TYPE_PMGM_POWER_SW_ON) - return "PowerMGM Event (not handled right now)"; - return _errlog_type[log->type]; -} - diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/rtas-proc.c linuxppc64_2_4/arch/ppc64/kernel/rtas-proc.c --- linux-2.4.9-ac10/arch/ppc64/kernel/rtas-proc.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas-proc.c Tue Sep 25 13:52:29 2001 @@ -200,7 +200,7 @@ struct proc_dir_entry *entry; rtas_node = find_devices("rtas"); - if ((rtas_node == 0) || (_machine != _MACH_chrp)) { + if ((rtas_node == 0) || (_machine == _MACH_iSeries)) { return; } @@ -384,7 +384,7 @@ return -EINVAL; /* May not be enough */ - buffer = kmalloc(buffer, MAX_LINELENGTH*MAX_SENSORS); + buffer = kmalloc((unsigned long)buffer, MAX_LINELENGTH*MAX_SENSORS); if (!buffer) return -ENOMEM; diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/rtas.c linuxppc64_2_4/arch/ppc64/kernel/rtas.c --- linux-2.4.9-ac10/arch/ppc64/kernel/rtas.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas.c Wed Sep 26 03:19:30 2001 @@ -25,7 +25,6 @@ #include #include - /* * prom_init() is called very early on, before the kernel text * and data have been mapped to KERNELBASE. At this point the code @@ -47,8 +46,9 @@ * are put in the data segment. */ - -struct rtas_t rtas = { 0, 0, 0, SPIN_LOCK_UNLOCKED, NULL, {0, 0, NULL}}; +struct rtas_t rtas = { + lock: SPIN_LOCK_UNLOCKED +}; extern unsigned long reloc_offset(void); @@ -102,7 +102,6 @@ enter_rtas((void *)__pa((unsigned long)rtas)); } - #if 0 #define DEBUG_RTAS #endif @@ -184,11 +183,10 @@ return (ulong)((nret > 0) ? rtas_args->rets[0] : 0); } - void __chrp rtas_restart(char *cmd) { - printk("RTAS system-reboot returned %d\n", + printk("RTAS system-reboot returned %ld\n", call_rtas("system-reboot", 0, 1, NULL)); for (;;); } @@ -197,7 +195,7 @@ rtas_power_off(void) { /* allow power on only with power button press */ - printk("RTAS power-off returned %d\n", + printk("RTAS power-off returned %ld\n", call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); for (;;); } @@ -207,5 +205,3 @@ { rtas_power_off(); } - - diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/rtasd.c linuxppc64_2_4/arch/ppc64/kernel/rtasd.c --- linux-2.4.9-ac10/arch/ppc64/kernel/rtasd.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/rtasd.c Thu Sep 27 07:22:18 2001 @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2001 Anton Blanchard , IBM + * + * 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. + * + * Communication to userspace based on kernel/printk.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if 0 +#define DEBUG(A...) printk(KERN_ERR A) +#else +#define DEBUG(A...) +#endif + +static spinlock_t rtas_log_lock = SPIN_LOCK_UNLOCKED; + +DECLARE_WAIT_QUEUE_HEAD(rtas_log_wait); + +#define LOG_NUMBER 64 /* must be a power of two */ +#define LOG_NUMBER_MASK (LOG_NUMBER-1) + +static char *rtas_log_buf; +static unsigned long rtas_log_start; +static unsigned long rtas_log_size; + +static int surveillance_requested; +static unsigned int rtas_event_scan_rate; +static unsigned int rtas_error_log_max; + +#define EVENT_SCAN_ALL_EVENTS 0xf0000000 +#define SURVEILLANCE_TOKEN 9000 +#define SURVEILLANCE_TIMEOUT 1 +#define SURVEILLANCE_SCANRATE 1 + +/* + * Since we use 32 bit RTAS, the physical address of this must be below + * 4G or else bad things happen. Allocate this in the kernel data and + * make it big enough. + */ +#define RTAS_ERROR_LOG_MAX 1024 +static unsigned char logdata[RTAS_ERROR_LOG_MAX]; + +static int rtas_log_open(struct inode * inode, struct file * file) +{ + return 0; +} + +static int rtas_log_release(struct inode * inode, struct file * file) +{ + return 0; +} + +static ssize_t rtas_log_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int error; + char *tmp; + unsigned long offset; + + if (!buf || count < rtas_error_log_max) + return -EINVAL; + + count = rtas_error_log_max; + + error = verify_area(VERIFY_WRITE, buf, count); + if (error) + return -EINVAL; + + tmp = kmalloc(rtas_error_log_max, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + + error = wait_event_interruptible(rtas_log_wait, rtas_log_size); + if (error) + goto out; + + spin_lock(&rtas_log_lock); + offset = rtas_error_log_max * (rtas_log_start & LOG_NUMBER_MASK); + memcpy(tmp, &rtas_log_buf[offset], count); + rtas_log_start += 1; + rtas_log_size -= 1; + spin_unlock(&rtas_log_lock); + + copy_to_user(buf, tmp, count); + error = count; + +out: + kfree(tmp); + return error; +} + +static unsigned int rtas_log_poll(struct file *file, poll_table * wait) +{ + poll_wait(file, &rtas_log_wait, wait); + if (rtas_log_size) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations proc_rtas_log_operations = { + read: rtas_log_read, + poll: rtas_log_poll, + open: rtas_log_open, + release: rtas_log_release, +}; + +static void log_rtas(char *buf) +{ + unsigned long offset; + + DEBUG("logging rtas event\n"); + + spin_lock(&rtas_log_lock); + + offset = rtas_error_log_max * + ((rtas_log_start+rtas_log_size) & LOG_NUMBER_MASK); + + memcpy(&rtas_log_buf[offset], buf, rtas_error_log_max); + + if (rtas_log_size < LOG_NUMBER) + rtas_log_size += 1; + else + rtas_log_start += 1; + + spin_unlock(&rtas_log_lock); + wake_up_interruptible(&rtas_log_wait); +} + +static int enable_surveillance(void) +{ + int error; + + error = call_rtas("set-indicator", 3, 1, NULL, SURVEILLANCE_TOKEN, + 0, SURVEILLANCE_TIMEOUT); + + if (error) { + printk(KERN_ERR "rtasd: could not enable surveillance\n"); + return -1; + } + + rtas_event_scan_rate = SURVEILLANCE_SCANRATE; + + return 0; +} + +static int get_eventscan_parms(void) +{ + struct device_node *node; + int *ip; + + node = find_path_device("/rtas"); + + ip = (int *)get_property(node, "rtas-event-scan-rate", NULL); + if (ip == NULL) { + printk(KERN_ERR "rtasd: no rtas-event-scan-rate\n"); + return -1; + } + rtas_event_scan_rate = *ip; + DEBUG("rtas-event-scan-rate %d\n", rtas_event_scan_rate); + + ip = (int *)get_property(node, "rtas-error-log-max", NULL); + if (ip == NULL) { + printk(KERN_ERR "rtasd: no rtas-error-log-max\n"); + return -1; + } + rtas_error_log_max = *ip; + DEBUG("rtas-error-log-max %d\n", rtas_error_log_max); + + if (rtas_error_log_max > RTAS_ERROR_LOG_MAX) { + printk(KERN_ERR "rtasd: truncated error log from %d to %d bytes\n", rtas_error_log_max, RTAS_ERROR_LOG_MAX); + rtas_error_log_max = RTAS_ERROR_LOG_MAX; + } + + return 0; +} + +extern long sys_sched_get_priority_max(int policy); + +static int rtasd(void *unused) +{ + int cpu = 0; + int error; + int first_pass = 1; + + if (get_eventscan_parms() == -1) + goto error; + + rtas_log_buf = vmalloc(rtas_error_log_max*LOG_NUMBER); + if (!rtas_log_buf) { + printk(KERN_ERR "rtasd: no memory\n"); + goto error; + } + + DEBUG("will sleep for %d jiffies\n", (HZ*60/rtas_event_scan_rate) / 2); + + daemonize(); + sigfillset(¤t->blocked); + sprintf(current->comm, "rtasd"); + + /* Rusty unreal time task */ + current->policy = SCHED_FIFO; + current->nice = sys_sched_get_priority_max(SCHED_FIFO) + 1; + + cpu = 0; + current->cpus_allowed = 1UL << cpu_logical_map(cpu); + schedule(); + + while(1) { + do { + memset(logdata, 0, rtas_error_log_max); + error = call_rtas("event-scan", 4, 1, NULL, + EVENT_SCAN_ALL_EVENTS, 0, + __pa(logdata), rtas_error_log_max); + if (error == -1) { + printk(KERN_ERR "event-scan failed\n"); + break; + } + + if (error == 0) + log_rtas(logdata); + + } while(error == 0); + + DEBUG("watchdog scheduled on cpu %d\n", smp_processor_id()); + + cpu++; + if (cpu >= smp_num_cpus) { + + if (first_pass && surveillance_requested) { + DEBUG("enabling surveillance\n"); + if (enable_surveillance()) + goto error_vfree; + DEBUG("surveillance enabled\n"); + } + + first_pass = 0; + cpu = 0; + } + + current->cpus_allowed = 1UL << cpu_logical_map(cpu); + + /* Check all cpus for pending events before sleeping*/ + if (first_pass) { + schedule(); + } else { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((HZ*60/rtas_event_scan_rate) / 2); + } + } + +error_vfree: + vfree(rtas_log_buf); +error: + /* Should delete proc entries */ + return -EINVAL; +} + +static void __init rtas_init(void) +{ + struct proc_dir_entry *rtas_dir, *entry; + + rtas_dir = proc_mkdir("rtas", 0); + if (!rtas_dir) { + printk(KERN_ERR "Failed to create rtas proc directory\n"); + } else { + entry = create_proc_entry("error_log", S_IRUSR, rtas_dir); + if (entry) + entry->proc_fops = &proc_rtas_log_operations; + else + printk(KERN_ERR "Failed to create rtas/error_log proc entry\n"); + } + + if (kernel_thread(rtasd, 0, CLONE_FS) < 0) + printk(KERN_ERR "Failed to start RTAS daemon\n"); + + printk(KERN_ERR "RTAS daemon started\n"); +} + +static int __init surveillance_setup(char *str) +{ + int i; + + if (get_option(&str,&i)) { + if (i == 1) + surveillance_requested = 1; + } + + return 1; +} + +__initcall(rtas_init); +__setup("surveillance=", surveillance_setup); diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/rtc.c linuxppc64_2_4/arch/ppc64/kernel/rtc.c --- linux-2.4.9-ac10/arch/ppc64/kernel/rtc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/rtc.c Tue Sep 25 13:52:29 2001 @@ -0,0 +1,361 @@ +/* + * Real Time Clock interface for PPC64. + * + * Based on rtc.c by Paul Gortmaker + * + * This driver allows use of the real time clock + * from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the + * /proc/driver/rtc pseudo-file for status information. + * + * Interface does not support RTC interrupts nor an alarm. + * + * 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 Mike Corrigan: IBM iSeries rtc support + * 1.1 Dave Engebretsen: IBM pSeries rtc support + */ + +#define RTC_VERSION "1.1" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +extern int piranha_simulator; + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin); + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Now all the various file operations that we export. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) { + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + ppc_md.get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + yrs = rtc_tm.tm_year; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + if (yrs < 70) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ( yrs > 169 ) + return -EINVAL; + + ppc_md.set_rtc_time(&rtc_tm); + + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The various file operations we support. + */ +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int __init rtc_init(void) +{ + misc_register(&rtc_dev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n"); + + return 0; +} + +static void __exit rtc_exit (void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_dev); +} + +module_init(rtc_init); +module_exit(rtc_exit); +EXPORT_NO_SYMBOLS; + +/* + * Info exported via "/proc/driver/rtc". + */ + +static int rtc_proc_output (char *buf) +{ + + char *p; + struct rtc_time tm; + + p = buf; + + ppc_md.get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + + p += sprintf(p, + "DST_enable\t: no\n" + "BCD\t\t: yes\n" + "24hr\t\t: yes\n" ); + + return p - buf; +} + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +/* + * Get the RTC from the virtual service processor + * This requires flowing LpEvents to the primary partition + */ +void iSeries_get_rtc_time(struct rtc_time *rtc_tm) +{ + if (piranha_simulator) + return; + + mf_getRtc(rtc_tm); + rtc_tm->tm_mon--; +} + + +void pSeries_get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned long ret[8]; + int error; + + error = call_rtas("get-time-of-day", 0, 8, (void *)&ret); + + if (error != 0){ + printk(KERN_WARNING "error: reading the clock failed\n"); + return; + } + + rtc_tm->tm_sec = ret[5]; + rtc_tm->tm_min = ret[4]; + rtc_tm->tm_hour = ret[3]; + rtc_tm->tm_mday = ret[2]; + rtc_tm->tm_mon = ret[1]; + rtc_tm->tm_year = ret[0]; +} + +int pSeries_set_rtc_time(struct rtc_time *tm) +{ + int error; + + error = call_rtas("set-time-of-day", 7, 1, NULL, + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, 0); + if (error != 0) + printk(KERN_WARNING "error: setting the clock failed\n"); + + return 0; +} + +/* + * Set the RTC in the virtual service processor + * This requires flowing LpEvents to the primary partition + */ +int iSeries_set_rtc_time(struct rtc_time *tm) +{ + mf_setRtc(tm); + return 0; +} + +void iSeries_get_boot_time(struct rtc_time *tm) +{ + unsigned long time; + static unsigned long lastsec = 1; + + u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); + u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); + int year = 1970; + int year1 = ( dataWord1 >> 24 ) & 0x000000FF; + int year2 = ( dataWord1 >> 16 ) & 0x000000FF; + int sec = ( dataWord1 >> 8 ) & 0x000000FF; + int min = dataWord1 & 0x000000FF; + int hour = ( dataWord2 >> 24 ) & 0x000000FF; + int day = ( dataWord2 >> 8 ) & 0x000000FF; + int mon = dataWord2 & 0x000000FF; + + if ( piranha_simulator ) + return; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year1); + BCD_TO_BIN(year2); + year = year1 * 100 + year2; + + time = mktime(year, mon, day, hour, min, sec); + time += ( jiffies / HZ ); + + /* Now THIS is a nasty hack! + * It ensures that the first two calls get different answers. + * That way the loop in init_time (time.c) will not think + * the clock is stuck. + */ + if ( lastsec ) { + time -= lastsec; + --lastsec; + } + + to_tm(time, tm); +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/setup.c linuxppc64_2_4/arch/ppc64/kernel/setup.c --- linux-2.4.9-ac10/arch/ppc64/kernel/setup.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/setup.c Fri Sep 28 15:44:16 2001 @@ -1,6 +1,6 @@ /* * - * Common prep/pmac/chrp boot and setup code. + * Common boot and setup code. * * Copyright (C) 2001 PPC64 Team, IBM Corp * @@ -38,6 +38,9 @@ extern HTAB htab_data; extern unsigned long loops_per_jiffy; +extern unsigned long embedded_sysmap_start; +extern unsigned long embedded_sysmap_end; + int have_of = 1; extern void chrp_init(unsigned long r3, @@ -49,6 +52,7 @@ extern void chrp_init_map_io_space( void ); extern void iSeries_init( void ); extern void iSeries_init_early( void ); +extern void pSeries_init_early( void ); extern void mm_init_ppc64( void ); unsigned long decr_overclock = 1; @@ -62,21 +66,14 @@ char saved_command_line[256]; unsigned char aux_device_present; -struct int_control_struct int_control = -{ - __no_use_cli, - __no_use_sti, - __no_use_restore_flags, - __no_use_save_flags -}; struct ide_machdep_calls ppc_ide_md; -int parse_bootinfo(void); void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7); +int parse_bootinfo(void); unsigned long DMA_MODE_READ, DMA_MODE_WRITE; -int _machine = _MACH_chrp; /* DRENG prom.c needs this assumption, a better way needs to be found. */ +int _machine = _MACH_unknown; #ifdef CONFIG_MAGIC_SYSRQ unsigned long SYSRQ_KEY; @@ -120,7 +117,7 @@ unsigned long offset = reloc_offset(); struct Naca *_naca = RELOC(naca); - _naca->debug_switch = PPC_DEBUG_DEFAULT; + _naca->debug_switch = PPC_DEBUG_DEFAULT; /* | PPCDBG_BUSWALK | PPCDBG_PHBINIT | PPCDBG_MM | PPCDBG_MMINIT | PPCDBG_TCEINIT | PPCDBG_TCE */; } void naca_init(void) { @@ -152,121 +149,105 @@ } /* - * Determine the type of system this we are running on. - * For the PPC64 kernel, we assume chrp. - */ -void identify_machine(void) { - if ( itLpNaca.xLparInstalled == 1 ) - _machine = _MACH_iSeries; - else - _machine = _MACH_chrp; -} - -/* * Do some initial setup of the system. The paramters are those which * were passed in from the bootloader. */ void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - identify_machine(); - - switch (_machine) { - case _MACH_chrp: - parse_bootinfo(); - chrp_init_map_io_space(); - break; - case _MACH_iSeries: + /* pSeries systems are identified in prom.c via OF. */ + if ( itLpNaca.xLparInstalled == 1 ) + _machine = _MACH_iSeries; + switch (_machine) { + case _MACH_iSeries: iSeries_init_early(); - break; - default: - } - - udbg_init(); - - udbg_puts("\n-----------------------------------------------------\n"); - udbg_puts("Naca Info...\n\n"); - udbg_puts("naca = 0x"); - udbg_puthex((unsigned long)naca); - udbg_putc('\n'); - - udbg_puts("naca->processorCount = 0x"); - udbg_puthex(naca->processorCount); - udbg_putc('\n'); - - udbg_puts("naca->physicalMemorySize = 0x"); - udbg_puthex(naca->physicalMemorySize); - udbg_putc('\n'); - - udbg_puts("naca->dCacheL1LineSize = 0x"); - udbg_puthex(naca->dCacheL1LineSize); - udbg_putc('\n'); - - udbg_puts("naca->dCacheL1LogLineSize = 0x"); - udbg_puthex(naca->dCacheL1LogLineSize); - udbg_putc('\n'); - - udbg_puts("naca->dCacheL1LinesPerPage = 0x"); - udbg_puthex(naca->dCacheL1LinesPerPage); - udbg_putc('\n'); - - udbg_puts("naca->iCacheL1LineSize = 0x"); - udbg_puthex(naca->iCacheL1LineSize); - udbg_putc('\n'); - - udbg_puts("naca->iCacheL1LogLineSize = 0x"); - udbg_puthex(naca->iCacheL1LogLineSize); - udbg_putc('\n'); - - udbg_puts("naca->iCacheL1LinesPerPage = 0x"); - udbg_puthex(naca->iCacheL1LinesPerPage); - udbg_putc('\n'); - - udbg_puts("naca->serialPortAddr = 0x"); - udbg_puthex(naca->serialPortAddr); - udbg_putc('\n'); - - udbg_puts("naca->interrupt_controller = 0x"); - udbg_puthex(naca->interrupt_controller); - udbg_putc('\n'); - - udbg_printf("\nHTAB Info ...\n\n"); - udbg_printf("htab_data.htab = 0x%lx\n", htab_data.htab); - udbg_printf("htab_data.num_ptegs = 0x%lx\n", htab_data.htab_num_ptegs); - - udbg_puts("\n-----------------------------------------------------\n"); + break; + case _MACH_pSeries: + pSeries_init_early(); +#ifdef CONFIG_BLK_DEV_INITRD + initrd_start = initrd_end = 0; +#endif + parse_bootinfo(); + break; + } + udbg_puts("\n-----------------------------------------------------\n"); + udbg_puts("Naca Info...\n\n"); + udbg_puts("naca = 0x"); + udbg_puthex((unsigned long)naca); + udbg_putc('\n'); + + udbg_puts("naca->processorCount = 0x"); + udbg_puthex(naca->processorCount); + udbg_putc('\n'); + + udbg_puts("naca->physicalMemorySize = 0x"); + udbg_puthex(naca->physicalMemorySize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LineSize = 0x"); + udbg_puthex(naca->dCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LogLineSize = 0x"); + udbg_puthex(naca->dCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->dCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LineSize = 0x"); + udbg_puthex(naca->iCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LogLineSize = 0x"); + udbg_puthex(naca->iCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->iCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->pftSize = 0x"); + udbg_puthex(naca->pftSize); + udbg_putc('\n'); + + udbg_puts("naca->serialPortAddr = 0x"); + udbg_puthex(naca->serialPortAddr); + udbg_putc('\n'); + + udbg_puts("naca->interrupt_controller = 0x"); + udbg_puthex(naca->interrupt_controller); + udbg_putc('\n'); + + udbg_printf("\nHTAB Info ...\n\n"); + udbg_puts("htab_data.htab = 0x"); + udbg_puthex((unsigned long)htab_data.htab); + udbg_putc('\n'); + udbg_puts("htab_data.num_ptegs = 0x"); + udbg_puthex(htab_data.htab_num_ptegs); + udbg_putc('\n'); + + udbg_puts("\n-----------------------------------------------------\n"); + + + if ( _machine & _MACH_pSeries ) { + finish_device_tree(); + chrp_init(r3, r4, r5, r6, r7); + } - if ( _machine == _MACH_chrp ) { - finish_device_tree(); - chrp_init(r3, r4, r5, r6, r7); - } + mm_init_ppc64(); - mm_init_ppc64(); - - switch (_machine) { - case _MACH_chrp: // DRENG _MACH_chrp needs to be _MACH_pSeries now - // The following relies on the device tree being fully configured. - parse_cmd_line(r3, r4, r5, r6, r7); - - break; - case _MACH_iSeries: - iSeries_init(); - break; - default: - // Need a way to die here-prob have to display something to the panel DRENG - } -} - -/* - * I really need to add multiple-console support... -- Cort - */ -int __init pmac_display_supported(char *name) -{ - return 0; -} -void __init pmac_find_display(void) -{ + switch (_machine) { + case _MACH_iSeries: + iSeries_init(); + break; + default: + /* The following relies on the device tree being */ + /* fully configured. */ + parse_cmd_line(r3, r4, r5, r6, r7); + } } void machine_restart(char *cmd) @@ -308,7 +289,7 @@ len += sprintf(len+buffer,"processor\t: %lu\n",i); len += sprintf(len+buffer,"cpu\t\t: "); - pvr = naca->paca[i].pvr; + pvr = xPaca[i].pvr; switch (PVR_VER(pvr)) { @@ -394,100 +375,106 @@ * Fetch the cmd_line from open firmware. */ void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7) { - struct device_node *chosen; - char *p; + struct device_node *chosen; + char *p; #ifdef CONFIG_BLK_DEV_INITRD - if (r3 && r4 && r4 != 0xdeadbeef) - { - if (r3 < KERNELBASE) - r3 += KERNELBASE; - initrd_start = r3; - initrd_end = r3 + r4; - ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); - initrd_below_start_ok = 1; + if ((initrd_start == 0) && r3 && r4 && r4 != 0xdeadbeef) { + initrd_start = (r3 >= KERNELBASE) ? r3 : (unsigned long)__va(r3); + initrd_end = initrd_start + r4; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; } #endif - cmd_line[0] = 0; - chosen = find_devices("chosen"); - if (chosen != NULL) { - p = get_property(chosen, "bootargs", NULL); - if (p != NULL) - strncpy(cmd_line, p, sizeof(cmd_line)); - } - cmd_line[sizeof(cmd_line) - 1] = 0; - - /* Look for mem= option on command line */ - if (strstr(cmd_line, "mem=")) { - char *p, *q; - unsigned long maxmem = 0; - extern unsigned long __max_memory; - - for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { - q = p + 4; - if (p > cmd_line && p[-1] != ' ') - continue; - maxmem = simple_strtoul(q, &q, 0); - if (*q == 'k' || *q == 'K') { - maxmem <<= 10; - ++q; - } else if (*q == 'm' || *q == 'M') { - maxmem <<= 20; - ++q; - } - } - __max_memory = maxmem; - } - ppc_md.progress("id mach: done", 0x200); + cmd_line[0] = 0; + chosen = find_devices("chosen"); + if (chosen != NULL) { + p = get_property(chosen, "bootargs", NULL); + if (p != NULL) + strncpy(cmd_line, p, sizeof(cmd_line)); + } + cmd_line[sizeof(cmd_line) - 1] = 0; + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + extern unsigned long __max_memory; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } + ppc_md.progress("id mach: done", 0x200); +} + + +char *bi_tag2str(unsigned long tag) +{ + switch (tag) { + case BI_FIRST: + return "BI_FIRST"; + case BI_LAST: + return "BI_LAST"; + case BI_CMD_LINE: + return "BI_CMD_LINE"; + case BI_BOOTLOADER_ID: + return "BI_BOOTLOADER_ID"; + case BI_INITRD: + return "BI_INITRD"; + case BI_SYSMAP: + return "BI_SYSMAP"; + case BI_MACHTYPE: + return "BI_MACHTYPE"; + default: + return "BI_UNKNOWN"; + } } int parse_bootinfo(void) { -#if 0 - /* DRENG this stuff is needs to be completly redone in the context of - * yaboot. Remove for now - I think we can wait on this for some time. - */ struct bi_record *rec; - extern char __bss_start[]; extern char *sysmap; extern unsigned long sysmap_size; - rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); - if ( rec->tag != BI_FIRST ) - { - /* - * This 0x10000 offset is a terrible hack but it will go away when - * we have the bootloader handle all the relocation and - * prom calls -- Cort - */ - rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); - if ( rec->tag != BI_FIRST ) - return -1; - } - for ( ; rec->tag != BI_LAST ; - rec = (struct bi_record *)((ulong)rec + rec->size) ) - { - ulong *data = rec->data; - switch (rec->tag) - { + rec = prom.bi_recs; + + if ( rec == NULL || rec->tag != BI_FIRST ) + return -1; + + for ( ; rec->tag != BI_LAST ; rec = bi_rec_next(rec) ) { + switch (rec->tag) { case BI_CMD_LINE: - memcpy(cmd_line, (void *)data, rec->size); + memcpy(cmd_line, (void *)rec->data, rec->size); break; case BI_SYSMAP: - sysmap = (char *)((data[0] >= (KERNELBASE)) ? data[0] : - (data[0]+KERNELBASE)); - sysmap_size = data[1]; + sysmap = (char *)((rec->data[0] >= (KERNELBASE)) + ? rec->data[0] : (unsigned long)__va(rec->data[0])); + sysmap_size = rec->data[1]; break; #ifdef CONFIG_BLK_DEV_INITRD case BI_INITRD: - initrd_start = data[0]; - initrd_end = data[0] + rec->size; + initrd_start = (unsigned long)__va(rec->data[0]); + initrd_end = initrd_start + rec->data[1]; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; break; #endif /* CONFIG_BLK_DEV_INITRD */ } } -#endif + return 0; } @@ -655,6 +642,21 @@ udbg_puts(" "); udbg_puthex(srr1); udbg_puts("\n"); } +int set_spread_lpevents( char * str ) +{ + /* The parameter is the number of processors to share in processing lp events */ + unsigned long i; + unsigned long val = simple_strtoul( str, NULL, 0 ); + if ( ( val > 0 ) && ( val <= maxPacas ) ) { + for ( i=1; i int smp_threads_ready = 0; volatile int smp_commenced = 0; @@ -79,19 +81,6 @@ extern struct Naca *naca; extern struct Paca xPaca[]; -/* Forward declarations */ -static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait); -static int smp_iSeries_probe(void); -static void smp_iSeries_kick_cpu(int nr); -static void smp_iSeries_setup_cpu(int nr); - -static void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait); -static int smp_chrp_probe(void); -static void smp_chrp_kick_cpu(int nr); -static void smp_chrp_setup_cpu(int cpu_nr); - -static void smp_xics_message_pass(int target, int msg, unsigned long data, int wait); -static int smp_xics_probe(void); void xics_setup_cpu(void); void xics_cause_IPI(int cpu); @@ -101,43 +90,10 @@ volatile unsigned long xics_ipi_message[NR_CPUS] = {0}; #define smp_message_pass(t,m,d,w) \ - do { if (smp_ops) \ - atomic_inc(&ipi_sent); \ - smp_ops->message_pass((t),(m),(d),(w)); \ + do { atomic_inc(&ipi_sent); \ + ppc_md.smp_message_pass((t),(m),(d),(w)); \ } while(0) -static struct smp_ops_t { - void (*message_pass)(int target, int msg, unsigned long data, int wait); - int (*probe)(void); - void (*kick_cpu)(int nr); - void (*setup_cpu)(int nr); - -} *smp_ops; - -/* iSeries (iSeries) */ -static struct smp_ops_t iSeries_smp_ops = { - smp_iSeries_message_pass, - smp_iSeries_probe, - smp_iSeries_kick_cpu, - smp_iSeries_setup_cpu -}; - -/* CHRP with openpic */ -static struct smp_ops_t chrp_smp_ops = { - smp_openpic_message_pass, - smp_chrp_probe, - smp_chrp_kick_cpu, - smp_chrp_setup_cpu, -}; - -/* CHRP with new XICS interrupt controller */ -static struct smp_ops_t xics_smp_ops = { - smp_xics_message_pass, - smp_xics_probe, - smp_chrp_kick_cpu, - smp_chrp_setup_cpu, -}; - #ifdef CONFIG_KDB void smp_kdb_stop(void) { @@ -178,6 +134,21 @@ } } +static int smp_iSeries_numProcs(void) +{ + unsigned np, i; + struct ItLpPaca * lpPaca; + + np = 0; + for (i=0; i < maxPacas; ++i) { + lpPaca = xPaca[i].xLpPacaPtr; + if ( lpPaca->xDynProcStatus < 2 ) { + ++np; + } + } + return np; +} + static int smp_iSeries_probe(void) { unsigned i; @@ -220,6 +191,18 @@ { } +/* This is called very early. */ +void smp_init_iSeries(void) +{ + ppc_md.smp_message_pass = smp_iSeries_message_pass; + ppc_md.smp_probe = smp_iSeries_probe; + ppc_md.smp_kick_cpu = smp_iSeries_kick_cpu; + ppc_md.smp_setup_cpu = smp_iSeries_setup_cpu; + + naca->processorCount = smp_iSeries_numProcs(); +} + + static void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) { @@ -254,7 +237,7 @@ } static void -smp_chrp_kick_cpu(int nr) +smp_kick_cpu(int nr) { /* Verify we have a Paca for processor nr */ if ( ( nr <= 0 ) || @@ -292,6 +275,7 @@ call_rtas("thaw-time-base", 0, 1, NULL); mb(); frozen = 0; + tb_last_stamp = get_tb(); smp_tb_synchronized = 1; } else { atomic_inc(&ready); @@ -335,14 +319,21 @@ return naca->processorCount; } -#if 0 -static void -smp_xics_setup_cpu(int cpu_nr) +/* This is called very early */ +void smp_init_pSeries(void) { - if (cpu_nr > 0) - xics_setup_cpu(); + if(naca->interrupt_controller == IC_OPEN_PIC) { + ppc_md.smp_message_pass = smp_openpic_message_pass; + ppc_md.smp_probe = smp_chrp_probe; + ppc_md.smp_kick_cpu = smp_kick_cpu; + ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; + } else { + ppc_md.smp_message_pass = smp_xics_message_pass; + ppc_md.smp_probe = smp_xics_probe; + ppc_md.smp_kick_cpu = smp_kick_cpu; + ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; + } } -#endif void smp_local_timer_interrupt(struct pt_regs * regs) @@ -481,13 +472,13 @@ #ifdef CONFIG_XMON xmon(0); #endif +#ifdef CONFIG_PPC_ISERIES + HvCall_terminateMachineSrc(); +#endif goto out; } barrier(); udelay(1); -#ifdef CONFIG_PPC_ISERIES - HvCallCfg_getLps(); -#endif } if (wait) { @@ -497,13 +488,13 @@ if (--timeout == 0) { printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); +#ifdef CONFIG_PPC_ISERIES + HvCall_terminateMachineSrc(); +#endif goto out; } barrier(); udelay(1); -#ifdef CONFIG_PPC_ISERIES - HvCallCfg_getLps(); -#endif } } ret = 0; @@ -576,7 +567,7 @@ init_idle(); for (i = 0; i < NR_CPUS; i++) { - paca = &(naca->paca[i]); + paca = &xPaca[i]; paca->prof_counter=1; paca->prof_multiplier = 1; if(i != 0) { @@ -590,7 +581,6 @@ paca->xStab_data.virt = (unsigned long)&stab_array[PAGE_SIZE * (i-1)]; arpn = physRpn_to_absRpn(___pa(paca->xStab_data.virt) >> 12); paca->xStab_data.real = arpn << 12; - paca->xHwProcNum = cpu_hw_index[i]; memset((void *)paca->xStab_data.virt, 0, PAGE_SIZE); paca->default_decr = tb_ticks_per_jiffy / decr_overclock; } @@ -605,24 +595,8 @@ */ cacheflush_time = 5 * 1024; - switch ( _machine ) { - case _MACH_chrp: - if (OpenPIC_Addr) { - smp_ops = &chrp_smp_ops; - } else { - smp_ops = &xics_smp_ops; - } - break; - case _MACH_iSeries: - smp_ops = &iSeries_smp_ops; - break; - default: - printk("SMP not supported on this machine.\n"); - return; - } - /* Probe arch for CPUs */ - cpu_nr = smp_ops->probe(); + cpu_nr = ppc_md.smp_probe(); printk("Probe found %d CPUs\n", cpu_nr); @@ -680,7 +654,7 @@ current_set[i].sp_real); /* wake up cpus */ - smp_ops->kick_cpu(i); + ppc_md.smp_kick_cpu(i); /* * wait to see if the cpu made a callin (is actually up). @@ -688,9 +662,6 @@ * -- Cort */ for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) { -#ifdef CONFIG_PPC_ISERIES - HvCallCfg_getLps(); -#endif udelay(100); } @@ -707,10 +678,12 @@ } /* Setup CPU 0 last (important) */ - smp_ops->setup_cpu(0); + ppc_md.smp_setup_cpu(0); - if (smp_num_cpus < 2) + if (smp_num_cpus < 2) { + tb_last_stamp = get_tb(); smp_tb_synchronized = 1; + } } void __init smp_commence(void) @@ -731,7 +704,7 @@ set_dec(xPaca[cpu].default_decr); cpu_callin_map[cpu] = 1; - smp_ops->setup_cpu(cpu); + ppc_md.smp_setup_cpu(cpu); init_idle(); @@ -742,12 +715,9 @@ * atomic. * -- Cort */ - cpu_callin_map[cpu] = 1; + cpu_online_map |= 1UL << smp_processor_id(); while(!smp_commenced) { -#ifdef CONFIG_PPC_ISERIES - HvCallCfg_getLps(); -#endif barrier(); } __sti(); @@ -785,7 +755,7 @@ */ void __init smp_store_cpu_info(int id) { - naca->paca[id].pvr = _get_PVR(); + xPaca[id].pvr = _get_PVR(); } static int __init maxcpus(char *str) diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/stab.c linuxppc64_2_4/arch/ppc64/kernel/stab.c --- linux-2.4.9-ac10/arch/ppc64/kernel/stab.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/stab.c Tue Sep 25 13:52:29 2001 @@ -20,53 +20,34 @@ inline int make_ste(unsigned long stab, unsigned long esid, unsigned long vsid); inline void make_slbe(unsigned long esid, unsigned long vsid); -extern unsigned long reloc_offset(void); extern struct Naca *naca; /* - * Initialize the segment table for this processor with the set - * of kernel addresses. + * Initialize the segment table for this processor. We fault in all + * entries except the first kernel segment. */ +void stab_initialize(unsigned long skip_esid_c, unsigned long stab) +{ + unsigned long esid, vsid; -void -stab_initialize(unsigned long skip_esid_c, unsigned long stab) { - unsigned long offset = reloc_offset(); - unsigned long esid, last_esid, vsid; - - /* - * Put in STEs for the 0xC00000000 - 0xF00000000 segments. - */ esid = GET_ESID(KERNELBASE); + vsid = get_kernel_vsid(esid << SID_SHIFT); - /* - * If asked to skip loading esid 0xC00000000 then skip it. - */ - if ( skip_esid_c ) - esid += (REGION_STRIDE >> SID_SHIFT); - - /* - * Put entries into the STAB for the first segment of the 4 kernel - * regions (0xC - 0xF). These are left in the table during flush. - * All other kernel segments (including bolted) are demand faulted. - */ - last_esid = GET_ESID(KERNELBASE) + (2 * (REGION_STRIDE >> SID_SHIFT)); + if (skip_esid_c) + return; - for (; esid <= last_esid; esid += (REGION_STRIDE >> SID_SHIFT)) { - /* Map the esid to an EA & find the vsid we need. */ - vsid = get_kernel_vsid(esid << SID_SHIFT); - if(!__is_processor(PV_POWER4)) { - make_ste(stab, esid, vsid); - } else { - make_slbe(esid, vsid ); - } - } + if (!__is_processor(PV_POWER4)) + make_ste(stab, esid, vsid); + else + make_slbe(esid, vsid); } /* * Create a segment table entry for the given esid/vsid pair. */ inline int -make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) { +make_ste(unsigned long stab, unsigned long esid, unsigned long vsid) +{ unsigned long entry, group, old_esid, castout_entry, i; unsigned int global_entry; STE *ste, *castout_ste; @@ -101,7 +82,7 @@ /* * Could not find empty entry, pick one with a round robin selection. * Search all entries in the two groups. Note that the first time - * we get here, we start with entry REGION_COUNT - 1 so the initializer + * we get here, we start with entry 1 so the initializer * can be common with the SLB castout code. */ @@ -155,7 +136,8 @@ /* * Create a segment buffer entry for the given esid/vsid pair. */ -inline void make_slbe(unsigned long esid, unsigned long vsid) { +inline void make_slbe(unsigned long esid, unsigned long vsid) +{ unsigned long entry, castout_entry; slb_dword0 castout_esid_data; union { @@ -212,7 +194,7 @@ entry = castout_entry; castout_entry++; if(castout_entry >= naca->slb_size) { - castout_entry = REGION_COUNT - 1; + castout_entry = 1; } get_paca()->xStab_data.next_round_robin = castout_entry; @@ -246,6 +228,7 @@ unsigned long trap) { unsigned long vsid, esid; + int kernel_segment = 0; PMC_SW_PROCESSOR(stab_faults); @@ -256,6 +239,7 @@ /* Kernel or user address? */ if (REGION_ID(ea) >= KERNEL_REGION_ID) { + kernel_segment = 1; vsid = get_kernel_vsid( ea ); } else { struct mm_struct *mm = current->mm; @@ -270,14 +254,14 @@ if (trap == 0x380 || trap == 0x480) { make_slbe(esid, vsid); } else { - unsigned char top_entry, stab_entry, *segments, i; + unsigned char top_entry, stab_entry, *segments; stab_entry = make_ste(get_paca()->xStab_data.virt, esid, vsid); PMC_SW_PROCESSOR_A(stab_entry_use, stab_entry & 0xf); segments = get_paca()->xSegments; top_entry = segments[0]; - if(top_entry < (STAB_CACHE_SIZE - 1)) { + if(!kernel_segment && top_entry < (STAB_CACHE_SIZE - 1)) { top_entry++; segments[top_entry] = stab_entry; if(top_entry == STAB_CACHE_SIZE - 1) top_entry = 0xff; @@ -293,11 +277,15 @@ * Kernel and Bolted entries are not removed as we cannot tolerate * faults on those addresses. */ -void flush_stab(void) { + +#define STAB_PRESSURE 0 + +void flush_stab(void) +{ STE *stab = (STE *) get_paca()->xStab_data.virt; unsigned char *segments = get_paca()->xSegments; unsigned long flags, i; - + if(!__is_processor(PV_POWER4)) { unsigned long entry; STE *ste; @@ -306,7 +294,7 @@ __asm__ __volatile__ ("isync" : : : "memory"); __save_and_cli(flags); - if(segments[0] != 0xff) { + if(segments[0] != 0xff && !STAB_PRESSURE) { for(i = 1; i <= segments[0]; i++) { ste = stab + segments[i]; ste->dw0.dw0.v = 0; @@ -316,13 +304,17 @@ /* Invalidate all entries. */ ste = stab; - /* Never flush the first four entries. */ - ste += 4; - for(entry = 4; + /* Never flush the first entry. */ + ste += 1; + for(entry = 1; entry < (PAGE_SIZE / sizeof(STE)); entry++, ste++) { - ste->dw0.dw0.v = 0; - PMC_SW_PROCESSOR(stab_invalidations); + unsigned long ea; + ea = ste->dw0.dw0.esid << SID_SHIFT; + if (STAB_PRESSURE || ea < KERNELBASE) { + ste->dw0.dw0.v = 0; + PMC_SW_PROCESSOR(stab_invalidations); + } } } @@ -337,33 +329,12 @@ /* Force flush to complete. */ __asm__ __volatile__ ("sync" : : : "memory"); } else { - /* Invalidate the entire SLB (except entry 1), and then put */ - /* back in the bolted range translation. */ - /* This only does the first bolted segment presently. DRENG */ - unsigned long msr, tmp; - - /* esid v idx */ - slb_dword0 esid_data = {GET_ESID(BOLTEDBASE), 1, 0, 1}; - - /* vsid ks kp */ - slb_dword1 vsid_data = {0, 0, 1, 0, 0, 0, 0}; - vsid_data.vsid = get_kernel_vsid(BOLTEDBASE); + unsigned long flags; PMC_SW_PROCESSOR(stab_invalidations); - __asm__ __volatile__("\n\ - mfmsr %0 \n\ - rldicl %1,%0,48,1 \n\ - rldicl %1,%1,16,0 \n\ - mtmsrd %1 \n\ - isync \n\ - slbia \n\ - isync \n\ - slbmte %2,%3 \n\ - isync \n\ - mtmsrd %0" - : "=&r"(msr), "=&r"(tmp) - : "r" (vsid_data), "r" (esid_data) - : "memory"); + __save_and_cli(flags); + __asm__ __volatile__("isync; slbia; isync":::"memory"); + __restore_flags(flags); } } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/sys_ppc32.c linuxppc64_2_4/arch/ppc64/kernel/sys_ppc32.c --- linux-2.4.9-ac10/arch/ppc64/kernel/sys_ppc32.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/sys_ppc32.c Thu Sep 27 08:56:50 2001 @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -672,7 +673,7 @@ buf.count = 0; buf.dirent = dirent; - error = vfs_readdir(file, fillonedir, &buf); + error = vfs_readdir(file, (filldir_t)fillonedir, &buf); if (error < 0) goto out_putf; error = buf.count; @@ -707,7 +708,8 @@ int error; }; -static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, +static int +filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, unsigned int d_type) { struct linux_dirent32 * dirent; @@ -751,7 +753,7 @@ buf.count = count; buf.error = 0; - error = vfs_readdir(file, filldir, &buf); + error = vfs_readdir(file, (filldir_t)filldir, &buf); if (error < 0) goto out_putf; lastdirent = buf.previous; @@ -3276,8 +3278,6 @@ int ret; off_t of; - PPCDBG(PPCDBG_SYS32, "sys32_sendfile - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - if (offset && get_user(of, offset)) return -EFAULT; @@ -3285,17 +3285,12 @@ ret = sys_sendfile((int)out_fd, (int)in_fd, offset ? &of : NULL, count); set_fs(old_fs); - if (!ret && offset && put_user(of, offset)) + if (offset && put_user(of, offset)) return -EFAULT; - - PPCDBG(PPCDBG_SYS32, "sys32_sendfile - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); return ret; } - - - extern asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen); asmlinkage long sys32_setsockopt(int fd, int level, int optname, char* optval, int optlen) @@ -4359,7 +4354,7 @@ set_fs (KERNEL_DS); ret = sys_wait4((int)pid, stat_addr ? &status : NULL, options, &r); set_fs (old_fs); - if (put_rusage (ru, &r)) return -EFAULT; + if (put_rusage ((struct rusage32 *)ru, &r)) return -EFAULT; if (stat_addr && put_user (status, stat_addr)) return -EFAULT; return ret; @@ -4877,4 +4872,52 @@ + +struct __sysctl_args32 { + u32 name; + int nlen; + u32 oldval; + u32 oldlenp; + u32 newval; + u32 newlen; + u32 __unused[4]; +}; + +extern asmlinkage long sys32_sysctl(struct __sysctl_args32 *args) +{ + struct __sysctl_args32 tmp; + int error; + size_t oldlen, *oldlenp = NULL; + unsigned long addr = (((long)&args->__unused[0]) + 7) & ~7; + + if (copy_from_user(&tmp, args, sizeof(tmp))) + return -EFAULT; + + if (tmp.oldval && tmp.oldlenp) { + /* Duh, this is ugly and might not work if sysctl_args + is in read-only memory, but do_sysctl does indirectly + a lot of uaccess in both directions and we'd have to + basically copy the whole sysctl.c here, and + glibc's __sysctl uses rw memory for the structure + anyway. */ + if (get_user(oldlen, (u32 *)A(tmp.oldlenp)) || + put_user(oldlen, (size_t *)addr)) + return -EFAULT; + oldlenp = (size_t *)addr; + } + + lock_kernel(); + error = do_sysctl((int *)A(tmp.name), tmp.nlen, (void *)A(tmp.oldval), + oldlenp, (void *)A(tmp.newval), tmp.newlen); + unlock_kernel(); + if (oldlenp) { + if (!error) { + if (get_user(oldlen, (size_t *)addr) || + put_user(oldlen, (u32 *)A(tmp.oldlenp))) + error = -EFAULT; + } + copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused)); + } + return error; +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/syscalls.c linuxppc64_2_4/arch/ppc64/kernel/syscalls.c --- linux-2.4.9-ac10/arch/ppc64/kernel/syscalls.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/syscalls.c Fri Sep 28 02:03:09 2001 @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -119,8 +120,8 @@ (struct ipc_kludge *) ptr, sizeof (tmp)))) break; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, - third); + ret = sys_msgrcv (first, (struct msgbuf *)(unsigned long)tmp.msgp, + second, tmp.msgtyp, third); break; } default: @@ -286,3 +287,19 @@ PPCDBG(PPCDBG_SYS64X, "sys_olduname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return error; } + + +asmlinkage time_t sys64_time(time_t* tloc) +{ + time_t i; + + /* SMP: This is fairly trivial. We grab CURRENT_TIME and + stuff it to user space. No side effects */ + i = CURRENT_TIME; + if (tloc) { + if (put_user(i,tloc)) + i = -EFAULT; + } + return i; +} + diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/time.c linuxppc64_2_4/arch/ppc64/kernel/time.c --- linux-2.4.9-ac10/arch/ppc64/kernel/time.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/time.c Sat Sep 29 13:39:03 2001 @@ -25,21 +25,6 @@ * non ambiguous timestamps even around leap seconds. This needs * a new timestamp format and a good name. * - * - * The following comment is partially obsolete (at least the long wait - * is no more a valid reason): - * Since the MPC8xx has a programmable interrupt timer, I decided to - * use that rather than the decrementer. Two reasons: 1.) the clock - * frequency is low, causing 2.) a long wait in the timer interrupt - * while ((d = get_dec()) == dval) - * loop. The MPC8xx can be driven from a variety of input clocks, - * so a number of assumptions have been made here because the kernel - * parameter HZ is a constant. We assume (correctly, today :-) that - * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal. - * This is then divided by 4, providing a 8192 Hz clock into the PIT. - * Since it is not possible to get a nice 100 Hz clock out of this, without - * creating a software PLL, I have set HZ to 128. -- Dan - * * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 * "A Kernel Model for Precision Timekeeping" by Dave Mills * @@ -69,6 +54,9 @@ #include #include #include +#ifdef CONFIG_PPC_ISERIES +#include +#endif #include @@ -78,9 +66,17 @@ time_t last_rtc_update; extern rwlock_t xtime_lock; extern int piranha_simulator; +#ifdef CONFIG_PPC_ISERIES +#define TB_RECAL_COUNT (180*HZ) +#define FIRST_TB_RECAL_COUNT (10*HZ) +#define TITAN_PRINTK_THRESHOLD 5 +long iSeries_tb_recal_count = FIRST_TB_RECAL_COUNT; +unsigned long iSeries_recal_titan = 0; +unsigned long iSeries_recal_tb = 0; +#endif -unsigned long tb_ticks_per_jiffy = 5000000; /* initial sane values */ -unsigned long tb_ticks_per_usec = 500; +unsigned long tb_ticks_per_jiffy; +unsigned long tb_ticks_per_usec; unsigned long tb_to_us; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; @@ -120,36 +116,93 @@ atomic_inc((atomic_t *)&prof_buffer[nip]); } + static __inline__ void timer_check_rtc(void) { - /* - * update the rtc when needed, this should be performed on the - * right fraction of a second. Half or full second ? - * Full second works on mk48t59 clocks, others need testing. - * Note that this update is basically only used through - * the adjtimex system calls. Setting the HW clock in - * any other way is a /dev/rtc and userland business. - * This is still wrong by -0.5/+1.5 jiffies because of the - * timer interrupt resolution and possible delay, but here we - * hit a quantization limit which can only be solved by higher - * resolution timers and decoupling time management from timer - * interrupts. This is also wrong on the clocks - * which require being written at the half second boundary. - * We should have an rtc call that only sets the minutes and - * seconds like on Intel to avoid problems with non UTC clocks. - */ - if ( (time_status & STA_UNSYNC) == 0 && - xtime.tv_sec - last_rtc_update >= 659 && - abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && - jiffies - wall_jiffies == 1) { - if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0) - last_rtc_update = xtime.tv_sec+1; - else - /* Try again one minute later */ - last_rtc_update += 60; + /* + * update the rtc when needed, this should be performed on the + * right fraction of a second. Half or full second ? + * Full second works on mk48t59 clocks, others need testing. + * Note that this update is basically only used through + * the adjtimex system calls. Setting the HW clock in + * any other way is a /dev/rtc and userland business. + * This is still wrong by -0.5/+1.5 jiffies because of the + * timer interrupt resolution and possible delay, but here we + * hit a quantization limit which can only be solved by higher + * resolution timers and decoupling time management from timer + * interrupts. This is also wrong on the clocks + * which require being written at the half second boundary. + * We should have an rtc call that only sets the minutes and + * seconds like on Intel to avoid problems with non UTC clocks. + */ + if ( (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + struct rtc_time tm; + to_tm(xtime.tv_sec+1 + time_offset, &tm); + if (ppc_md.set_rtc_time(&tm) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Try again one minute later */ + last_rtc_update += 60; + } +} + +#ifdef CONFIG_PPC_ISERIES + +/* + * This function recalibrates the timebase based on the time-of-day value in the Titan chip. + * The Titan is much more accurate than the value returned by the service processor for the + * timebase frequency. This will compute a new value for tb_ticks_per_jiffy every three + * minutes. It does not attempt to correct the time for past timebase inaccuracies, only + * to adjust the conversion from timebase ticks to jiffies (and thus seconds). + */ + +static void iSeries_tb_recal(void) +{ + unsigned long titan, tb; + tb = get_tb(); + titan = HvCallXm_loadTod(); + if ( iSeries_recal_titan ) { + unsigned long tb_ticks = tb - iSeries_recal_tb; + unsigned long titan_usec = (titan - iSeries_recal_titan) >> 12; + unsigned long new_tb_ticks_per_jiffy = (tb_ticks * (1000000/HZ))/titan_usec; + long tick_diff = new_tb_ticks_per_jiffy - tb_ticks_per_jiffy; + char sign = '+'; + + if ( tick_diff < 0 ) { + tick_diff = -tick_diff; + sign = '-'; + } + if ( tick_diff ) { + if ( tick_diff < tb_ticks_per_jiffy/25 ) { + if ( tick_diff > TITAN_PRINTK_THRESHOLD ) + printk( "Titan recalibrate: new tb_ticks_per_jiffy = %lu (%c%ld)\n", + new_tb_ticks_per_jiffy, sign, tick_diff ); + tb_ticks_per_jiffy = new_tb_ticks_per_jiffy; + } + else { + printk( "Titan recalibrate: FAILED (difference > 4 percent)\n" + " new tb_ticks_per_jiffy = %lu\n" + " old tb_ticks_per_jiffy = %lu\n", + new_tb_ticks_per_jiffy, tb_ticks_per_jiffy ); + } } + } + iSeries_recal_titan = titan; + iSeries_recal_tb = tb; +} +static __inline__ void iSeries_tb_recal_check(void) +{ + --iSeries_tb_recal_count; + if ( iSeries_tb_recal_count <= 0 ) { + iSeries_tb_recal(); + iSeries_tb_recal_count = TB_RECAL_COUNT; + } } +#endif /* * For iSeries shared processors, we have to let the hypervisor @@ -161,6 +214,8 @@ * call will not be needed) */ +unsigned long tb_last_stamp=0; + /* * timer_interrupt - gets called when the decrementer overflows, * with interrupts disabled. @@ -170,49 +225,49 @@ { int next_dec; unsigned long cur_tb; - struct Paca * paca = (struct Paca *)mfspr(SPRG3); unsigned long cpu = paca->xPacaIndex; struct ItLpQueue * lpq; irq_enter(cpu); - paca = (struct Paca *)mfspr(SPRG3); - if (!user_mode(regs)) ppc_do_profile(instruction_pointer(regs)); - while ( paca->next_jiffy_update_tb <= (cur_tb = get_tb()) ) { + paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; + + while (paca->next_jiffy_update_tb <= (cur_tb = get_tb())) { #ifdef CONFIG_SMP smp_local_timer_interrupt(regs); -#endif - if ( cpu == 0 ) { +#endif + if (cpu == 0) { write_lock(&xtime_lock); + tb_last_stamp = paca->next_jiffy_update_tb; do_timer(regs); timer_check_rtc(); write_unlock(&xtime_lock); +#ifdef CONFIG_PPC_ISERIES + iSeries_tb_recal_check(); +#endif } paca->next_jiffy_update_tb += tb_ticks_per_jiffy; } + next_dec = paca->next_jiffy_update_tb - cur_tb; - if ( next_dec > paca->default_decr ) - next_dec = paca->default_decr; + if (next_dec > paca->default_decr) + next_dec = paca->default_decr; set_dec(next_dec); - - if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) - ppc_md.heartbeat(); - paca->xLpPaca.xIntDword.xFields.xDecrInt = 0; lpq = paca->lpQueuePtr; - if ( lpq ) - lpEvent_count += ItLpQueue_process( lpq, regs ); - + if (lpq && ItLpQueue_isLpIntPending(lpq)) + lpEvent_count += ItLpQueue_process(lpq, regs); + irq_exit(cpu); if (softirq_pending(cpu)) do_softirq(); - + return 1; /* lets ret_from_int know we can do checks */ } @@ -222,26 +277,30 @@ void do_gettimeofday(struct timeval *tv) { unsigned long flags; - unsigned long delta, lost_ticks, usec, sec; + unsigned long lost_ticks, sec, usec; + unsigned long delta; read_lock_irqsave(&xtime_lock, flags); sec = xtime.tv_sec; usec = xtime.tv_usec; - delta = tb_ticks_per_jiffy - ( xPaca[0].next_jiffy_update_tb - get_tb() ); + delta = tb_ticks_since(tb_last_stamp); #ifdef CONFIG_SMP - /* As long as timebases are not in sync, gettimeofday can only - * have jiffy resolution on SMP. + /* + * As long as timebases are not in sync, gettimeofday can only + * have jiffy resolution on SMP.(except iSeries where the + * timebases are synchronized by the hypervisor) */ if (!smp_tb_synchronized) delta = 0; #endif /* CONFIG_SMP */ + lost_ticks = jiffies - wall_jiffies; read_unlock_irqrestore(&xtime_lock, flags); /* Convert the lost ticks into micro-seconds. */ - usec += ( ( tb_ticks_per_jiffy * lost_ticks ) + delta ) / tb_ticks_per_usec; + usec += ((tb_ticks_per_jiffy * lost_ticks) + delta) / tb_ticks_per_usec; while (usec > 1000000) { sec++; usec -= 1000000; @@ -275,7 +334,7 @@ * still reasonable when gettimeofday resolution is 1 jiffy. */ - tb_delta = tb_ticks_per_jiffy - ( xPaca[0].next_jiffy_update_tb - get_tb() ); + tb_delta = tb_ticks_since(tb_last_stamp); tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; new_sec = tv->tv_sec; @@ -304,55 +363,33 @@ void __init time_init(void) { - time_t sec, old_sec; - unsigned long old_stamp, stamp, elapsed; /* This function is only called on the boot processor */ unsigned long flags; + struct rtc_time tm; - if (ppc_md.time_init != NULL) - time_offset = ppc_md.time_init(); - - ppc_md.calibrate_decr(); + ppc_md.calibrate_decr(); - /* Now that the decrementer is calibrated, it can be used in case the - * clock is stuck, but the fact that we have to handle the 601 - * makes things more complex. Repeatedly read the RTC until the - * next second boundary to try to achieve some precision... - */ - stamp = get_tb(); - sec = ppc_md.get_rtc_time(); - elapsed = 0; if ( ! piranha_simulator ) { - do { - old_stamp = stamp; - old_sec = sec; - stamp = get_tb(); - elapsed += stamp - old_stamp; - sec = ppc_md.get_rtc_time(); - } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy); - if (sec==old_sec) { - printk("Warning: real time clock seems stuck!\n"); - } + ppc_md.get_boot_time(&tm); } write_lock_irqsave(&xtime_lock, flags); - xtime.tv_sec = sec; - last_jiffy_stamp(0) = stamp; + xtime.tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + tb_last_stamp = get_tb(); + xtime.tv_usec = 0; - /* No update now, we just read the time from the RTC ! */ last_rtc_update = xtime.tv_sec; write_unlock_irqrestore(&xtime_lock, flags); + +#ifdef CONFIG_PPC_ISERIES + iSeries_recal_tb = get_tb(); + iSeries_recal_titan = HvCallXm_loadTod(); +#endif + /* Not exact, but the timer interrupt takes care of this */ set_dec(tb_ticks_per_jiffy); - /* If platform provided a timezone (pmac), we correct the time - * using do_sys_settimeofday() which in turn calls warp_clock() - */ - if (time_offset) { - struct timezone tz; - tz.tz_minuteswest = -time_offset / 60; - tz.tz_dsttime = 0; - do_sys_settimeofday(NULL, &tz); - } + do_get_fast_time = do_gettimeofday; } #define TICK_SIZE tick @@ -445,34 +482,3 @@ */ GregorianDay(tm); } - -/* Auxiliary function to compute scaling factors */ -/* Actually the choice of a timebase running at 1/4 the of the bus - * frequency giving resolution of a few tens of nanoseconds is quite nice. - * It makes this computation very precise (27-28 bits typically) which - * is optimistic considering the stability of most processor clock - * oscillators and the precision with which the timebase frequency - * is measured but does not harm. - */ -/*unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) { */ -/* unsigned mlt=0, tmp, err; */ - /* No concern for performance, it's done once: use a stupid - * but safe and compact method to find the multiplier. - */ -/* - for (tmp = 1U<<31; tmp != 0; tmp >>= 1) { - if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp; - } -*/ - /* We might still be off by 1 for the best approximation. - * A side effect of this is that if outscale is too large - * the returned value will be zero. - * Many corner cases have been checked and seem to work, - * some might have been forgotten in the test however. - */ -/* - err = inscale*(mlt+1); - if (err <= inscale/2) mlt++; - return mlt; - } -*/ diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/traps.c linuxppc64_2_4/arch/ppc64/kernel/traps.c --- linux-2.4.9-ac10/arch/ppc64/kernel/traps.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/traps.c Fri Sep 21 16:01:03 2001 @@ -115,11 +115,6 @@ { if ( !user_mode(regs) ) { -#if defined(CONFIG_8xx) && defined(CONFIG_PCI) - /* the qspan pci read routines can cause machine checks -- Cort */ - bad_page_fault(regs, regs->dar); - return; -#endif #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_fault_handler) { debugger_fault_handler(regs); @@ -132,22 +127,6 @@ #endif printk("Machine check in kernel mode.\n"); printk("Caused by (from SRR1=%lx): ", regs->msr); - switch (regs->msr & 0xF0000) { - case 0x80000: - printk("Machine check signal\n"); - break; - case 0x40000: - printk("Transfer error ack signal\n"); - break; - case 0x20000: - printk("Data parity error signal\n"); - break; - case 0x10000: - printk("Address parity error signal\n"); - break; - default: - printk("Unknown values in msr\n"); - } show_regs(regs); #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) debugger(regs); @@ -191,7 +170,7 @@ } void -InstructionBreakpoint(struct pt_regs *regs) +InstructionBreakpointException(struct pt_regs *regs) { #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_iabr_match(regs)) @@ -205,31 +184,8 @@ } void -RunModeException(struct pt_regs *regs) -{ - _exception(SIGTRAP, regs); -} - -void ProgramCheckException(struct pt_regs *regs) { -#if defined(CONFIG_4xx) - unsigned int esr = mfspr(SPRN_ESR); - - if (esr & ESR_PTR) { -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) - if (debugger_bpt(regs)) - return; -#endif -#ifdef CONFIG_KDB - if (kdb(KDB_REASON_BREAK, 0, regs)) - return; -#endif - _exception(SIGTRAP, regs); - } else { - _exception(SIGILL, regs); - } -#else if (regs->msr & 0x100000) { /* IEEE FP exception */ _exception(SIGFPE, regs); @@ -247,7 +203,6 @@ } else { _exception(SIGILL, regs); } -#endif } void @@ -319,48 +274,6 @@ printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld\n", current, current->pid, regs->nip, regs->link, regs->gpr[0], regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); -} - -#ifdef CONFIG_8xx -void -SoftwareEmulation(struct pt_regs *regs) -{ - extern int do_mathemu(struct pt_regs *); - int errcode; - - if (!user_mode(regs)) { - show_regs(regs); -#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) - debugger(regs); -#endif -#ifdef CONFIG_KDB - if (kdb(KDB_REASON_FAULT, 0, regs)) - return ; -#endif - print_backtrace((unsigned long *)regs->gpr[1]); - panic("Kernel Mode Software FPU Emulation"); - } - -#ifdef CONFIG_MATH_EMULATION - if ((errcode = do_mathemu(regs))) { -#else - if ((errcode = Soft_emulate_8xx(regs))) { -#endif - if (errcode > 0) - _exception(SIGFPE, regs); - else if (errcode == -EFAULT) - _exception(SIGSEGV, regs); - else - _exception(SIGILL, regs); - } -} -#endif - -void -TAUException(struct pt_regs *regs) -{ - printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n", - regs->nip, regs->msr, regs->trap); } void __init trap_init(void) diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/udbg.c linuxppc64_2_4/arch/ppc64/kernel/udbg.c --- linux-2.4.9-ac10/arch/ppc64/kernel/udbg.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/udbg.c Mon Sep 24 22:38:44 2001 @@ -21,20 +21,21 @@ #define WANT_PPCDBG_TAB /* Only defined here */ #include #include +#include extern struct Naca *naca; extern int _machine; struct NS16550 { - /* this struct must be packed */ - unsigned char rbr; /* 0 */ - unsigned char ier; /* 1 */ - unsigned char fcr; /* 2 */ - unsigned char lcr; /* 3 */ - unsigned char mcr; /* 4 */ - unsigned char lsr; /* 5 */ - unsigned char msr; /* 6 */ - unsigned char scr; /* 7 */ + /* this struct must be packed */ + unsigned char rbr; /* 0 */ + unsigned char ier; /* 1 */ + unsigned char fcr; /* 2 */ + unsigned char lcr; /* 3 */ + unsigned char mcr; /* 4 */ + unsigned char lsr; /* 5 */ + unsigned char msr; /* 6 */ + unsigned char scr; /* 7 */ }; #define thr rbr @@ -52,57 +53,64 @@ #define LSR_TEMT 0x40 /* Xmitter empty */ #define LSR_ERR 0x80 /* Error */ -void *comport1; +volatile struct NS16550 *udbg_comport; static inline void eieio(void) { asm volatile ("eieio" : :); } void -udbg_init(void) +udbg_init_uart(void *comport) { - if ( _machine != _MACH_iSeries ) { - volatile struct NS16550 *port = (struct NS16550 *)comport1; - port->lcr = 0x00; eieio(); - port->ier = 0xFF; eieio(); - port->ier = 0x00; eieio(); - port->lcr = 0x80; eieio(); /* Access baud rate */ - port->dll = 12; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */ - port->dlm = 0; eieio(); /* dll >> 8 which should be zero for fast rates; */ - port->lcr = 0x03; eieio(); /* 8 data, 1 stop, no parity */ - port->mcr = 0x03; eieio(); /* RTS/DTR */ - port->fcr = 0x07; eieio(); /* Clear & enable FIFOs */ + if (comport) { + udbg_comport = (struct NS16550 *)comport; + udbg_comport->lcr = 0x00; eieio(); + udbg_comport->ier = 0xFF; eieio(); + udbg_comport->ier = 0x00; eieio(); + udbg_comport->lcr = 0x80; eieio(); /* Access baud rate */ + udbg_comport->dll = 12; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */ + udbg_comport->dlm = 0; eieio(); /* dll >> 8 which should be zero for fast rates; */ + udbg_comport->lcr = 0x03; eieio(); /* 8 data, 1 stop, no parity */ + udbg_comport->mcr = 0x03; eieio(); /* RTS/DTR */ + udbg_comport->fcr = 0x07; eieio(); /* Clear & enable FIFOs */ } } - void udbg_putc(unsigned char c) { - if ( _machine != _MACH_iSeries ) { - volatile struct NS16550 *port = (struct NS16550 *)comport1; - while ((port->lsr & LSR_THRE) == 0) - /* wait for idle */; - port->thr = c; eieio(); - if (c == '\n') { - /* Also put a CR. This is for convenience. */ - while ((port->lsr & LSR_THRE) == 0) - /* wait for idle */; - port->thr = '\r'; eieio(); - } - } - else { - printk("%c", c); - } + if ( udbg_comport ) { + while ((udbg_comport->lsr & LSR_THRE) == 0) + /* wait for idle */; + udbg_comport->thr = c; eieio(); + if (c == '\n') { + /* Also put a CR. This is for convenience. */ + while ((udbg_comport->lsr & LSR_THRE) == 0) + /* wait for idle */; + udbg_comport->thr = '\r'; eieio(); + } + } else if ( _machine == _MACH_iSeries ) { + /* ToDo: switch this via ppc_md */ + printk("%c", c); + } +} +int udbg_getc_poll(void) +{ + if (udbg_comport) { + if ((udbg_comport->lsr & LSR_DR) != 0) + return udbg_comport->rbr; + else + return -1; + } + return -1; } unsigned char udbg_getc(void) { - if ( _machine != _MACH_iSeries ) { - volatile struct NS16550 *port = (struct NS16550 *)comport1; - while ((port->lsr & LSR_DR) == 0) + if ( udbg_comport ) { + while ((udbg_comport->lsr & LSR_DR) == 0) /* wait for char */; - return port->rbr; + return udbg_comport->rbr; } return 0; } @@ -110,16 +118,49 @@ void udbg_puts(const char *s) { - if ( _machine != _MACH_iSeries ) { + if (ppc_md.udbg_putc) { + char c; + + if (s && *s != '\0') { + while ((c = *s++) != '\0') + ppc_md.udbg_putc(c); + } else { + udbg_puts("NULL"); + } + } else { + printk("%s", s); + } +} + +int +udbg_write(const char *s, int n) +{ + int remain = n; char c; - if ( s && *s != '\0' ) - while ( ( c = *s++ ) != '\0' ) - udbg_putc(c); - else - udbg_puts("NULL"); - } - else - printk("%s", s); + if (!ppc_md.udbg_putc) + for (;;); /* stop here for cpuctl */ + if ( s && *s != '\0' ) { + while ( (( c = *s++ ) != '\0') && (remain-- > 0)) { + ppc_md.udbg_putc(c); + } + } else + udbg_puts("NULL"); + return n - remain; +} + +int +udbg_read(char *buf, int buflen) { + char c, *p = buf; + int i; + if (!ppc_md.udbg_putc) + for (;;); /* stop here for cpuctl */ + for (i = 0; i < buflen; ++i) { + do { + c = ppc_md.udbg_getc(); + } while (c == 0x11 || c == 0x13); + *p++ = c; + } + return i; } void @@ -140,13 +181,13 @@ void udbg_printSP(const char *s) { - if ( _machine != _MACH_iSeries ) { - unsigned long sp; - asm("mr %0,1" : "=r" (sp) :); - if (s) - udbg_puts(s); - udbg_puthex(sp); - } + if (_machine == _MACH_pSeries) { + unsigned long sp; + asm("mr %0,1" : "=r" (sp) :); + if (s) + udbg_puts(s); + udbg_puthex(sp); + } } void @@ -167,99 +208,37 @@ void udbg_ppcdbg(unsigned long flags, const char *fmt, ...) { - unsigned long active_debugs = flags & naca->debug_switch; - if ( active_debugs ) { - va_list ap; - unsigned char buf[256]; - unsigned long i, len = 0; - - for(i=0; i < PPCDBG_NUM_FLAGS ;i++) { - if (((1U << i) & active_debugs) && - trace_names[i]) { - len += strlen(trace_names[i]); - udbg_puts(trace_names[i]); - break; - } + unsigned long active_debugs = flags & naca->debug_switch; + if ( active_debugs ) { + va_list ap; + unsigned char buf[256]; + unsigned long i, len = 0; + for(i=0; i < PPCDBG_NUM_FLAGS ;i++) { + if (((1U << i) & active_debugs) && + trace_names[i]) { + len += strlen(trace_names[i]); + udbg_puts(trace_names[i]); + break; } - sprintf(buf, " [%s]: ", current->comm); - len += strlen(buf); - udbg_puts(buf); - - while(len < 18) { - udbg_putc(' '); - len++; - } - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - udbg_puts(buf); - va_end(ap); } + sprintf(buf, " [%s]: ", current->comm); + len += strlen(buf); + udbg_puts(buf); + + while(len < 18) { + udbg_puts(" "); + len++; + } + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + udbg_puts(buf); + va_end(ap); + } } unsigned long udbg_ifdebug(unsigned long flags) { - return (flags & naca->debug_switch); + return (flags & naca->debug_switch); } - -/* Print out an area of memory. - * The msg will be printed, followed by a newline, followed by the - * memory dump. - */ -void -udbg_dump(char *msg, void *addr, unsigned len) -{ - if ( _machine != _MACH_iSeries ) { - int i; - int j; - int numrows; - unsigned char c; - unsigned char tc; - char *buff = addr; - - if (msg) { - udbg_puts(msg); - udbg_puts("\n"); - } - - if (!buff) { - udbg_puts(" Danger, Will Robinson, Null Pointer\n"); - return; - } - numrows = (len + 15) / 16; - - for (i = 0; i < numrows; i++) { - udbg_printf("%016lx ", &buff[i*16]); - for (j = 0; j < 16; j++) { - if (j + i * 16 >= len) { - udbg_puts(" "); - } else { - udbg_printf("%02x", (unsigned char)buff[j + i * 16]); - } /* endif */ - if (j % 8 == 7) { - udbg_puts(" "); - } /* endif */ - } /* endfor */ - - udbg_puts(" a|"); - - for (j = 0; j < 16; j++) { - if (j + i * 16 >= len) { - udbg_putc(' '); - } else { - c = (unsigned char)buff[j + i * 16]; - tc = c; - if (tc >= 32 && tc < 127 && tc != ' ') { - udbg_putc(tc); - } else { - udbg_putc('.'); - } /* endif */ - } /* endif */ - } /* endfor */ - udbg_putc('|'); - udbg_putc('\n'); - } /* endfor */ - } -} - diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/viopath.c linuxppc64_2_4/arch/ppc64/kernel/viopath.c --- linux-2.4.9-ac10/arch/ppc64/kernel/viopath.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/viopath.c Wed Dec 31 18:00:00 1969 @@ -1,711 +0,0 @@ -/* - * arch/ppc64/viopath.c - * - * iSeries Virtual I/O Message Path code - * - * Author: Dave Boutcher - * (C) Copyright 2000 IBM Corporation - * - * 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) anyu 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 _HVLPEVENT_H -#include -#endif -#ifndef _HVLPConfig_H -#include -#endif -#ifndef _HVCallCfg_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _VIO_H -#include -#endif -#ifndef _MF_H -#include -#endif -#ifndef _ISERIES_PROC_H -#include -#endif - -/* Status of the path to each other partition in the system. - * This is overkill, since we will only ever establish connections - * to our hosting partition and the primary partition on the system. - * But this allows for other support in the future. - */ -static struct viopathStatus { - int isOpen:1; /* Did we open the path? */ - int isActive:1; /* Do we have a mon msg outstanding */ -#if defined(CONFIG_VIOCONS) - int xCharUsers; -#endif -#if defined(CONFIG_VIODASD) - int xBlockUsers; -#endif -#if defined(CONFIG_VIOCD) - int xCDUsers; -#endif -#if defined(CONFIG_VIOTAPE) - int xTapeUsers; -#endif - int xConfigUsers; - HvLpInstanceId mSourceInst; - HvLpInstanceId mTargetInst; - int numberAllocated; - spinlock_t statuslock; - unsigned long statuslockFlags; -} viopathStatus[HVMAXARCHITECTEDLPS]; - -/* We use this structure to handle asynchronous responses. The caller - * blocks on the semaphore and the handler posts the semaphore. - */ -struct doneAllocParms_t { - struct semaphore *sem; - int number; -}; - -/* Put a sequence number in each mon msg. The value is not - * important. Start at something other than 0 just for - * readability. wrapping this is ok. - */ -static u8 viomonseq = 22; - -/* Our hosting logical partition. We get this at startup - * time, and different modules access this variable directly. - */ -HvLpIndex viopath_hostLp; - -/* For each kind of incoming event we set a pointer to a - * routine to call. - */ -static vio_event_handler_t *vio_handleCharEvent; -static vio_event_handler_t *vio_handleBlockEvent; -static vio_event_handler_t *vio_handleCDEvent; -static vio_event_handler_t *vio_handleTapeEvent; - -/* - * For each kind of event we allocate a buffer that is - * guaranteed not to cross a page boundary - */ -void * vio_char_event_buffer; -void * vio_block_event_buffer; -void * vio_cd_event_buffer; -void * vio_tape_event_buffer; - -/*************************************************************************** - * A page to build an lp event in - ***************************************************************************/ -unsigned long VIOReqPage; - -/*************************************************************************** - * Handle reads from the proc file system - ***************************************************************************/ -static int proc_read(char *buf, char **start, off_t offset, - int blen, int *eof, void *data) -{ - HvLpEvent_Rc hvrc; - DECLARE_MUTEX_LOCKED(Semaphore); - dma_addr_t dmaa = pci_map_single( NULL, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); - int len = PAGE_SIZE; - - if (len > blen) - len = blen; - - memset(buf,0x00,len); - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_config | vioconfigget, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(unsigned long)&Semaphore, - VIOVERSION << 16, - ((u64)dmaa) << 32, - len, - 0, - 0); - if (hvrc != HvLpEvent_Rc_Good) - { - printk("viopath hv error on op %d\n",(int)hvrc); - } - - down(&Semaphore); - - pci_unmap_single( NULL, dmaa, PAGE_SIZE, PCI_DMA_FROMDEVICE); - - *eof = 1; - return strlen(buf); -} - -/*************************************************************************** - * Handle writes to our proc file system - ***************************************************************************/ -static int proc_write(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - printk("viopath: in proc_write, got %ld bytes starting with %c\n", - count, buffer[0]); - return count; -} - -/*************************************************************************** - * setup our proc file system entries - ***************************************************************************/ -void vio_proc_init(struct proc_dir_entry *iSeries_proc) -{ - struct proc_dir_entry *ent; - ent = create_proc_entry("config", S_IFREG|S_IRUSR, iSeries_proc); - if (!ent) return; - ent->nlink = 1; - ent->data = NULL; - ent->read_proc = proc_read; - ent->write_proc = proc_write; -} - -/* Boot time initialization of the vio path code. Needs - * to be called before any VIO components start - */ -void viopath_init(void) -{ - int i; - memset(viopathStatus,0x00,sizeof(viopathStatus)); - for (i=0; ixFlags.xFunction == HvLpEvent_Function_Int) - { - printk("viopath: got monitor INT event\n"); - remoteLp = event->xSourceLp; - if (!viopathStatus[remoteLp].isActive) - sendMonMsg(remoteLp); - } - else - { - remoteLp = event->xTargetLp; - printk("viopath: got monitor ACK event %d, sinst %d, tinst %d\n", - (int)event->xCorrelationToken, - event->xSourceInstanceId, - event->xTargetInstanceId); - /* Other partition went away! */ - if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) || - (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) - { - printk("viopath: ignoring ack....mismatched instances\n"); - } - else - { - printk("viopath: closing path %d\n",remoteLp); - - viopathStatus[remoteLp].isActive = 0; - - if (vio_handleBlockEvent!= NULL) - (*vio_handleBlockEvent)(NULL); - - if (vio_handleCharEvent != NULL) - (*vio_handleCharEvent)(NULL); - - if (vio_handleCDEvent != NULL) - (*vio_handleCDEvent)(NULL); - - if (vio_handleTapeEvent != NULL) - (*vio_handleTapeEvent)(NULL); - } - } -} - -void vio_setBlockHandler(vio_event_handler_t *beh) -{ - vio_handleBlockEvent = *beh; -} - -void vio_clearBlockHandler(void) -{ - vio_handleBlockEvent = NULL; -} - -void vio_setCharHandler(vio_event_handler_t *ceh) -{ - vio_handleCharEvent = *ceh; -} - -void vio_clearCharHandler(void) -{ - vio_handleCharEvent = NULL; -} - -void vio_setCDHandler(vio_event_handler_t *ceh) -{ - vio_handleCDEvent = *ceh; -} - -void vio_clearCDHandler(void) -{ - vio_handleCDEvent = NULL; -} - -void vio_setTapeHandler(vio_event_handler_t *ceh) -{ - vio_handleTapeEvent = *ceh; -} - -void vio_clearTapeHandler(void) -{ - vio_handleTapeEvent = NULL; -} - -static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) -{ - HvLpIndex remoteLp; - if (event->xFlags.xFunction == HvLpEvent_Function_Int) - { - remoteLp = event->xSourceLp; - if (event->xSourceInstanceId != viopathStatus[remoteLp].mTargetInst) - { - printk("viopath: int msg rcvd, source inst (%d) doesnt match (%d)\n", - viopathStatus[remoteLp].mTargetInst, - event->xSourceInstanceId); - return; - } - - if (event->xTargetInstanceId != viopathStatus[remoteLp].mSourceInst) - { - printk("viopath: int msg rcvd, target inst (%d) doesnt match (%d)\n", - viopathStatus[remoteLp].mSourceInst, - event->xTargetInstanceId); - return; - } - } - else - { - remoteLp = event->xTargetLp; - if (event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) - { - printk("viopath: ack msg rcvd, source inst (%d) doesnt match (%d)\n", - viopathStatus[remoteLp].mSourceInst, - event->xSourceInstanceId); - return; - } - - if (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst) - { - printk("viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n", - viopathStatus[remoteLp].mTargetInst, - event->xTargetInstanceId); - return; - } - } - - - switch (event->xSubtype & VIOMAJOR_SUBTYPE_MASK) { - case viomajorsubtype_config: - up((struct semaphore *)event->xCorrelationToken); - break; - - case viomajorsubtype_monitor: - handleMonitorEvent(event); - break; - case viomajorsubtype_blockio: - if (vio_handleBlockEvent) - { - (*vio_handleBlockEvent)(event); - } - else - { - printk("vio: unexpected virtual blockio event\n"); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) - { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } - break; - case viomajorsubtype_chario: - if (vio_handleCharEvent) - { - (*vio_handleCharEvent)(event); - } - else - { - printk("vio: unexpected virtual chario event\n"); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) - { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } - break; - case viomajorsubtype_cdio: - if (vio_handleCDEvent) - { - (*vio_handleCDEvent)(event); - } - else - { - printk("vio: unexpected virtual cd event\n"); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) - { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } - break; - case viomajorsubtype_tape: - if (vio_handleTapeEvent) - { - (*vio_handleTapeEvent)(event); - } - else - { - printk("vio: unexpected virtual tape event\n"); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) - { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } - break; - default: - printk("vio: unexpected virtual io event subtype %d\n",event->xSubtype); - if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) - { - event->xRc = HvLpEvent_Rc_InvalidSubtype; - HvCallEvent_ackLpEvent(event); - } - } -} - - -static void viopath_donealloc(void *parm, int number) -{ - struct doneAllocParms_t *doneAllocParmsp = (struct doneAllocParms_t *)parm; - doneAllocParmsp->number = number; - up(doneAllocParmsp->sem); -} - -int viopath_open(HvLpIndex remoteLp, int subtype) -{ - HvLpEvent_Rc hvrc; - struct doneAllocParms_t doneAllocParms; - DECLARE_MUTEX_LOCKED(Semaphore); - - if ((remoteLp >= HvMaxArchitectedLps) || (remoteLp == HvLpIndexInvalid)) - return -EINVAL; - - if (VIOReqPage == 0) - { - /* Get a page to build read/write LP events in */ - VIOReqPage = get_free_page(GFP_KERNEL); - if (!VIOReqPage) - { - printk("viopath: error allocating I/O memory\n"); - return -ENOMEM; - } - - vio_char_event_buffer = (void *)VIOReqPage; - vio_block_event_buffer = (void *)(VIOReqPage+256); - vio_cd_event_buffer = (void *)(VIOReqPage+512); - vio_tape_event_buffer = (void *)(VIOReqPage+768); - } - - if ( (0) -#if defined(CONFIG_VIODASD) - || (subtype == viomajorsubtype_blockio) -#endif -#if defined(CONFIG_VIOCONS) - || (subtype == viomajorsubtype_chario) -#endif -#if defined(CONFIG_VIOCD) - || (subtype == viomajorsubtype_cdio) -#endif -#if defined(CONFIG_VIOTAPE) - || (subtype == viomajorsubtype_tape) -#endif - ) - { - viopath_statuslock(remoteLp); - switch (subtype) { -#if defined(CONFIG_VIODASD) - case viomajorsubtype_blockio: - viopathStatus[remoteLp].xBlockUsers++; - break; -#endif -#if defined(CONFIG_VIOCONS) - case viomajorsubtype_chario: - viopathStatus[remoteLp].xCharUsers++; - break; -#endif -#if defined(CONFIG_VIOCD) - case viomajorsubtype_cdio: - viopathStatus[remoteLp].xCDUsers++; - break; -#endif -#if defined(CONFIG_VIOTAPE) - case viomajorsubtype_tape: - viopathStatus[remoteLp].xTapeUsers++; - break; -#endif - default: - } - if (!viopathStatus[remoteLp].isOpen) - { - HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); - - viopathStatus[remoteLp].mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); - viopathStatus[remoteLp].mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); - - printk("viopath: open, setting sinst %d, tinst %d\n", - viopathStatus[remoteLp].mSourceInst, - viopathStatus[remoteLp].mTargetInst); - - doneAllocParms.sem = &Semaphore; - - mf_allocateLpEvents(remoteLp, - HvLpEvent_Type_VirtualIo, - 250, /* TODO: Put a sizeof VIOLpEvent in here! */ - 25, /* TODO: Work out the real number */ - &viopath_donealloc, - &doneAllocParms); - - down(&Semaphore); - - viopathStatus[remoteLp].numberAllocated = doneAllocParms.number; - - HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo, &vio_handleEvent); - - viopathStatus[remoteLp].isOpen = 1; - - hvrc = HvCallEvent_signalLpEventFast(remoteLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_monitor, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_DeferredAck, - viopathStatus[remoteLp].mSourceInst, - viopathStatus[remoteLp].mTargetInst, - 0, 0, 0, 0, 0, 0); - - if (hvrc == HvLpEvent_Rc_Good) - { - viopathStatus[remoteLp].isActive = 1; - } - else - { - viopathStatus[remoteLp].isActive = 0; - } - } - viopath_statusunlock(remoteLp); - - return 0; - } - else /* invalid subtype */ - { - printk("viopath: invalid path subtype %d\n",subtype); - return -EINVAL; - } -} - -int viopath_close(HvLpIndex remoteLp, int subtype) -{ - printk("viopath: close(%d,%4.4x)\n",remoteLp,subtype); - - if ((remoteLp >= HvMaxArchitectedLps) || (remoteLp == HvLpIndexInvalid)) - return -EINVAL; - - if ( (0) -#if defined(CONFIG_VIODASD) - || (subtype == viomajorsubtype_blockio) -#endif -#if defined(CONFIG_VIOCONS) - || (subtype == viomajorsubtype_chario) -#endif -#if defined(CONFIG_VIOCD) - || (subtype == viomajorsubtype_cdio) -#endif -#if defined(CONFIG_VIOTAPE) - || (subtype == viomajorsubtype_tape) -#endif - ) - { - viopath_statuslock(remoteLp); - switch (subtype) { -#if defined(CONFIG_VIODASD) - case viomajorsubtype_blockio: - viopathStatus[remoteLp].xBlockUsers--; - break; -#endif -#if defined(CONFIG_VIOCONS) - case viomajorsubtype_chario: - viopathStatus[remoteLp].xCharUsers--; - break; -#endif -#if defined(CONFIG_VIOCD) - case viomajorsubtype_cdio: - viopathStatus[remoteLp].xCDUsers--; - break; -#endif -#if defined(CONFIG_VIOTAPE) - case viomajorsubtype_tape: - viopathStatus[remoteLp].xTapeUsers--; - break; -#endif - default: - } - - if ((viopathStatus[remoteLp].isOpen) -#if defined(CONFIG_VIODASD) - && (viopathStatus[remoteLp].xBlockUsers == 0) -#endif -#if defined(CONFIG_VIOCONS) - && (viopathStatus[remoteLp].xCharUsers == 0) -#endif -#if defined(CONFIG_VIOCD) - && (viopathStatus[remoteLp].xCDUsers == 0) -#endif -#if defined(CONFIG_VIOTAPE) - && (viopathStatus[remoteLp].xTapeUsers == 0) -#endif - ) - { - printk("viopath: closing event path\n"); - HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); - viopathStatus[remoteLp].isOpen = 0; - viopathStatus[remoteLp].isActive = 0; - } - viopath_statusunlock(remoteLp); - - return 0; - } - else /* invalid subtype */ - { - printk("viopath: invalid path subtype %d\n",subtype); - return -EINVAL; - } -} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/kernel/xics.c linuxppc64_2_4/arch/ppc64/kernel/xics.c --- linux-2.4.9-ac10/arch/ppc64/kernel/xics.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/kernel/xics.c Thu Sep 27 10:48:29 2001 @@ -1,4 +1,4 @@ -/* +/* * arch/ppc/kernel/xics.c * * Copyright 2000 IBM Corporation. @@ -17,6 +17,7 @@ #include #include #include +#include #include "i8259.h" #include "xics.h" #include @@ -27,6 +28,7 @@ void xics_disable_irq(u_int irq); void xics_mask_and_ack_irq(u_int irq); void xics_end_irq(u_int irq); +void xics_set_affinity(unsigned int irq_nr, unsigned long cpumask); struct hw_interrupt_type xics_pic = { " XICS ", @@ -35,7 +37,8 @@ xics_enable_irq, xics_disable_irq, xics_mask_and_ack_irq, - xics_end_irq + xics_end_irq, + xics_set_affinity }; struct hw_interrupt_type xics_8259_pic = { @@ -50,7 +53,7 @@ #define XICS_IPI 2 #define XICS_IRQ_OFFSET 0x10 - +#define XICS_ALL_CPU_GROUP 0xff #define XICS_IRQ_SPURIOUS 0 #define DEFAULT_PRIORITY 0 @@ -77,19 +80,54 @@ struct xics_info xics_info; -#define xirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.word) -#define cppr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.bytes[0]) -#define poll_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr_poll.word) -#define qirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->qirr.bytes[0]) - unsigned long long intr_base = 0; -unsigned int xics_irq_8259_cascade = 0; +int xics_irq_8259_cascade = 0; +unsigned int default_server = 0; struct xics_interrupt_node { unsigned long long addr; unsigned long long size; } inodes[NR_CPUS*2]; +typedef struct { + int (*xirr_info_get)(int cpu); + void (*xirr_info_set)(int cpu, int val); + void (*cppr_info)(int cpu, u8 val); + void (*qirr_info)(int cpu, u8 val); +} xics_ops; + + +static int pSeries_xirr_info_get(int n_cpu) +{ + return (xics_info.per_cpu[n_cpu]->xirr.word); +} + +static void pSeries_xirr_info_set(int n_cpu, int value) +{ + xics_info.per_cpu[n_cpu]->xirr.word = value; +} + +static void pSeries_cppr_info(int n_cpu, u8 value) +{ + xics_info.per_cpu[n_cpu]->xirr.bytes[0] = value; +} + +static void pSeries_qirr_info(int n_cpu , u8 value) +{ + xics_info.per_cpu[n_cpu]->qirr.bytes[0] = value; +} + +static xics_ops pSeries_ops = { + pSeries_xirr_info_get, + pSeries_xirr_info_set, + pSeries_cppr_info, + pSeries_qirr_info +}; + +static xics_ops *ops = &pSeries_ops; + + + void xics_enable_irq( u_int irq @@ -101,10 +139,15 @@ irq -= XICS_IRQ_OFFSET; if (irq == XICS_IPI) return; +#ifdef CONFIG_IRQ_ALL_CPUS call_status = call_rtas("ibm,set-xive", 3, 1, (unsigned long*)&status, - irq, cpu_hw_index[0], DEFAULT_PRIORITY); + irq, smp_threads_ready ? XICS_ALL_CPU_GROUP : default_server, DEFAULT_PRIORITY); +#else + call_status = call_rtas("ibm,set-xive", 3, 1, (unsigned long*)&status, + irq, default_server, DEFAULT_PRIORITY); +#endif if( call_status != 0 ) { - printk("xics_enable_irq: irq=%x: call_rtas failed; retn=%x, status=%x\n", + printk("xics_enable_irq: irq=%x: call_rtas failed; retn=%lx, status=%lx\n", irq, call_status, status); return; } @@ -122,7 +165,7 @@ call_status = call_rtas("ibm,int-off", 1, 1, (unsigned long*)&status, irq); if( call_status != 0 ) { - printk("xics_disable_irq: irq=%x: call_rtas failed, retn=%x\n", + printk("xics_disable_irq: irq=%x: call_rtas failed, retn=%lx\n", irq, call_status); return; } @@ -135,9 +178,9 @@ { int cpu = smp_processor_id(); - cppr_info(cpu) = 0; /* actually the value overwritten by ack */ + ops->cppr_info(cpu, 0); /* actually the value overwritten by ack */ iosync(); - xirr_info(cpu) = (0xff<<24) | (irq-XICS_IRQ_OFFSET); + ops->xirr_info_set(cpu, ((0xff<<24) | (irq-XICS_IRQ_OFFSET))); iosync(); } @@ -151,11 +194,11 @@ if( irq < XICS_IRQ_OFFSET ) { i8259_pic.ack(irq); iosync(); - xirr_info(cpu) = (0xff<<24) | xics_irq_8259_cascade; + ops->xirr_info_set(cpu, ((0xff<<24) | xics_irq_8259_cascade)); iosync(); } else { - cppr_info(cpu) = 0xff; + ops->cppr_info(cpu, 0xff); iosync(); } } @@ -166,8 +209,8 @@ u_int cpu = smp_processor_id(); u_int vec; int irq; - - vec = xirr_info(cpu); + + vec = ops->xirr_info_get(cpu); /* (vec >> 24) == old priority */ vec &= 0x00ffffff; /* for sanity, this had better be < NR_IRQS - 16 */ @@ -176,10 +219,12 @@ if(irq == -1) { /* Spurious cascaded interrupt. Still must ack xics */ xics_end_irq(XICS_IRQ_OFFSET + xics_irq_8259_cascade); + irq = -1; } - } else if( vec == XICS_IRQ_SPURIOUS ) + } else if( vec == XICS_IRQ_SPURIOUS ) { irq = -1; - else + printk("spurious PPC interrupt!\n"); + } else irq = vec + XICS_IRQ_OFFSET; return irq; } @@ -198,9 +243,8 @@ { extern volatile unsigned long xics_ipi_message[]; int cpu = smp_processor_id(); - - qirr_info(cpu) = 0xff; + ops->qirr_info(cpu, 0xff); while (xics_ipi_message[cpu]) { if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, &xics_ipi_message[cpu])) { mb(); @@ -211,19 +255,18 @@ smp_message_recv(PPC_MSG_RESCHEDULE, regs); } } - } void xics_cause_IPI(int cpu) { - qirr_info(cpu) = 0; + ops->qirr_info(cpu,0) ; } void xics_setup_cpu(void) { int cpu = smp_processor_id(); - cppr_info(cpu) = 0xff; + ops->cppr_info(cpu, 0xff); iosync(); } #endif /* CONFIG_SMP */ @@ -274,6 +317,9 @@ np = np->next; if ((indx < NR_CPUS) && np) goto nextnode; + /* For now all interrupts go through this cpu (not necessarily cpu #0)... */ + default_server = hard_smp_processor_id(); + /* * XXX Assume for now that nodes are in order * We could (and should) get the "interrupt-server-ranges" property @@ -290,28 +336,29 @@ np = find_type_devices("interrupt-controller"); if (!np) { - printk(KERN_WARNING "Can't find ISA Interrupt Controller\n"); - udbg_printf("Can't find ISA Interrupt Controller\n"); - while (1); - } - ireg = (uint *) get_property(np, "interrupts", 0); - if (!ireg) { - printk(KERN_WARNING "Can't find ISA Interrupts Property\n"); - udbg_printf("Can't find ISA Interrupts Property\n"); - while (1); + printk(KERN_WARNING "xics: no ISA Interrupt Controller\n"); + xics_irq_8259_cascade = -1; + } else { + ireg = (uint *) get_property(np, "interrupts", 0); + if (!ireg) { + printk(KERN_WARNING "Can't find ISA Interrupts Property\n"); + udbg_printf("Can't find ISA Interrupts Property\n"); + while (1); + } + xics_irq_8259_cascade = *ireg; } - - xics_irq_8259_cascade = *ireg; + if (_machine == _MACH_pSeries) { #ifdef CONFIG_SMP - for (i = 0; i < naca->processorCount; ++i) { - xics_info.per_cpu[i] = - ioremap((ulong)inodes[cpu_hw_index[i]].addr, - (ulong)inodes[cpu_hw_index[i]].size); - } + for (i = 0; i < naca->processorCount; ++i) { + xics_info.per_cpu[i] = + ioremap((ulong)inodes[get_hard_smp_processor_id(i)].addr, + (ulong)inodes[get_hard_smp_processor_id(i)].size); + } #else - xics_info.per_cpu[0] = ioremap((ulong)intr_base, intr_size); + xics_info.per_cpu[0] = ioremap((ulong)intr_base, intr_size); #endif /* CONFIG_SMP */ + } xics_8259_pic.enable = i8259_pic.enable; xics_8259_pic.disable = i8259_pic.disable; @@ -320,12 +367,14 @@ for (; i < NR_IRQS; ++i) irq_desc[i].handler = &xics_pic; - cppr_info(0) = 0xff; + ops->cppr_info(0, 0xff); iosync(); - if (request_irq(xics_irq_8259_cascade + XICS_IRQ_OFFSET, no_action, - 0, "8259 cascade", 0)) - printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); - i8259_init(); + if (xics_irq_8259_cascade != -1) { + if (request_irq(xics_irq_8259_cascade + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); + } #ifdef CONFIG_SMP request_irq(XICS_IPI + XICS_IRQ_OFFSET, xics_ipi_action, 0, "IPI", 0); @@ -340,4 +389,62 @@ 0, "8259 cascade", 0)) printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); i8259_init(); +} + +/* + * Find first logical cpu and return its physical cpu number + */ +static inline u32 physmask(u32 cpumask) +{ + int i; + + for (i = 0; i < smp_num_cpus; ++i, cpumask >>= 1) { + if (cpumask & 1) + return get_hard_smp_processor_id(i); + } + + printk(KERN_ERR "xics_set_affinity: invalid irq mask\n"); + + return XICS_ALL_CPU_GROUP; +} + +void xics_set_affinity(unsigned int irq, unsigned long cpumask) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + long status; + unsigned long xics_status[2]; + u32 newmask; + + irq -= XICS_IRQ_OFFSET; + if (irq == XICS_IPI) + return; + + spin_lock_irqsave(&desc->lock, flags); + + status = call_rtas("ibm,get-xive", 1, 3, (void *)&xics_status, irq); + + if (status) { + printk("xics_set_affinity: irq=%d ibm,get-xive returns %ld\n", + irq, status); + goto out; + } + + /* For the moment only implement delivery to all cpus or one cpu */ + if (cpumask == 0xffffffff) + newmask = XICS_ALL_CPU_GROUP; + else + newmask = physmask(cpumask); + + status = call_rtas("ibm,set-xive", 3, 1, NULL, + irq, newmask, xics_status[1]); + + if (status) { + printk("xics_set_affinity irq=%d ibm,set-xive returns %ld\n", + irq, status); + goto out; + } + +out: + spin_unlock_irqrestore(&desc->lock, flags); } diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/mm/fault.c linuxppc64_2_4/arch/ppc64/mm/fault.c --- linux-2.4.9-ac10/arch/ppc64/mm/fault.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/mm/fault.c Wed Oct 3 07:44:22 2001 @@ -55,6 +55,11 @@ void bad_page_fault(struct pt_regs *, unsigned long); void do_page_fault(struct pt_regs *, unsigned long, unsigned long); +#ifdef CONFIG_PPCDBG +extern unsigned long get_srr0(void); +extern unsigned long get_srr1(void); +#endif + /* * For 600- and 800-family processors, the error_code parameter is DSISR * for a data fault, SRR1 for an instruction fault. @@ -177,7 +182,7 @@ PPCDBG(PPCDBG_SIGNAL, "Bad addr in user: 0x%lx\n", address); #ifdef CONFIG_XMON ifppcdebug(PPCDBG_SIGNALXMON) - PPCDBG_ENTER_DEBUGGER(); + PPCDBG_ENTER_DEBUGGER_REGS(regs); #endif force_sig_info(SIGSEGV, &info, current); diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/mm/init.c linuxppc64_2_4/arch/ppc64/mm/init.c --- linux-2.4.9-ac10/arch/ppc64/mm/init.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/mm/init.c Mon Oct 1 11:15:48 2001 @@ -38,6 +38,7 @@ #include #include #include +#include #ifdef CONFIG_BLK_DEV_INITRD #include /* for initrd_* */ #endif @@ -72,7 +73,6 @@ static int boot_mapsize; static unsigned long totalram_pages; -static struct device_node *memory_node; extern pgd_t swapper_pg_dir[]; extern char __init_begin, __init_end; @@ -94,9 +94,7 @@ extern unsigned long *find_end_of_memory(void); extern pgd_t ioremap_dir[]; -extern pgd_t bolted_dir[]; pgd_t * ioremap_pgd = (pgd_t *)&ioremap_dir; -pgd_t * bolted_pgd = (pgd_t *)&bolted_dir; static void map_io_page(unsigned long va, unsigned long pa, int flags); extern void die_if_kernel(char *,struct pt_regs *,long); @@ -220,7 +218,12 @@ void * ioremap(unsigned long addr, unsigned long size) { +#ifdef CONFIG_PPC_ISERIES + /* iSeries I/O Remap is a noop */ + return (void*)addr; +#else return __ioremap(addr, size, _PAGE_NO_CACHE); +#endif } extern struct vm_struct * get_im_area( unsigned long size ); @@ -249,21 +252,6 @@ */ if (pa < 16*1024*1024) pa += _ISA_MEM_BASE; - - /* - * Don't allow anybody to remap normal RAM that we're using. - * mem_init() sets high_memory so only do the check after that. - */ -#if 0 /* DRENG / PPPBBB assert to not remap DRAM is not right when I/O - * space is in the middle of DRAM ranges ... - */ - if ( mem_init_done && (pa < virt_to_phys(high_memory)) ) - { - printk("__ioremap(): phys addr %0lx is RAM lr %p\n", pa, - __builtin_return_address(0)); - return NULL; - } -#endif if (size == 0) return NULL; @@ -291,9 +279,15 @@ return (void *) (ea + (addr & ~PAGE_MASK)); } -void iounmap(void *addr) +void iounmap(void *addr) { +#ifdef CONFIG_PPC_ISERIES + /* iSeries I/O Remap is a noop */ + return; +#else /* DRENG / PPPBBB todo */ + return; +#endif } unsigned long iopa(unsigned long addr) @@ -366,6 +360,9 @@ local_flush_tlb_range( mm, mp->vm_start, mp->vm_end ); } else /* MIKEC: It is not clear why this is needed */ + /* paulus: it is needed to clear out stale HPTEs + * when an address space (represented by an mm_struct) + * is being destroyed. */ local_flush_tlb_range( mm, USER_START, USER_END ); } @@ -389,9 +386,6 @@ case IO_REGION_ID: pgd = pgd_offset_i( vmaddr ); break; - case BOLTED_REGION_ID: - pgd = pgd_offset_b( vmaddr ); - break; case USER_REGION_ID: pgd = pgd_offset( vma->vm_mm, vmaddr ); context = vma->vm_mm->context; @@ -440,9 +434,6 @@ case IO_REGION_ID: pgd = pgd_offset_i( start ); break; - case BOLTED_REGION_ID: - pgd = pgd_offset_b( start ); - break; case USER_REGION_ID: pgd = pgd_offset( mm, start ); context = mm->context; @@ -673,12 +664,60 @@ (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10), codepages<< (PAGE_SHIFT-10), datapages<< (PAGE_SHIFT-10), initpages<< (PAGE_SHIFT-10), - PAGE_OFFSET, __va(lmb_end_of_DRAM())); + PAGE_OFFSET, (unsigned long)__va(lmb_end_of_DRAM())); mem_init_done = 1; + /* set the last page of each hardware interrupt stack to be protected */ + initialize_paca_hardware_interrupt_stack(); + #ifdef CONFIG_PPC_ISERIES create_virtual_bus_tce_table(); #endif } + +/* + * This is called when a page has been modified by the kernel. + * It just marks the page as not i-cache clean. We do the i-cache + * flush later when the page is given to a user process, if necessary. + */ +void flush_dcache_page(struct page *page) +{ + clear_bit(PG_arch_1, &page->flags); +} + +/* + * set_pte stores a linux PTE into the linux page table. + * On machines which use an MMU hash table we avoid changing the + * _PAGE_HASHPTE bit. + * If the new PTE has _PAGE_EXEC set, meaning that the user wants + * to be able to execute out of the page, we check if the page is + * i-cache dirty and flush it if so, and mark it clean. + */ +void set_pte(pte_t *ptep, pte_t pte) +{ + pte_update(ptep, ~_PAGE_HPTEFLAGS, pte_val(pte) & ~_PAGE_HPTEFLAGS); + if (mem_init_done && (pte_val(pte) & _PAGE_EXEC) + && pte_pagenr(pte) < max_mapnr) { + struct page *page = pte_page(pte); + if (!test_bit(PG_arch_1, &page->flags)) { + __flush_dcache_icache((unsigned long)page_address(page)); + set_bit(PG_arch_1, &page->flags); + } + } +} + +void clear_user_page(struct page *page, unsigned long vaddr) +{ + prefetchw(&page->flags); + clear_mem_page(page); + clear_bit(PG_arch_1, &page->flags); +} + +void copy_user_page(struct page *to, struct page *from, unsigned long vaddr) +{ + prefetchw(&to->flags); + copy_mem_page(to, from); + clear_bit(PG_arch_1, &to->flags); +} diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/vmlinux.lds linuxppc64_2_4/arch/ppc64/vmlinux.lds --- linux-2.4.9-ac10/arch/ppc64/vmlinux.lds Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/vmlinux.lds Tue Sep 11 08:55:40 2001 @@ -104,7 +104,6 @@ . = ALIGN(4096); __init_end = .; - . = ALIGN(4096); __chrp_begin = .; .text.chrp : { *(.text.chrp) } .data.chrp : { *(.data.chrp) } @@ -118,8 +117,6 @@ . = ALIGN(4096); __openfirmware_end = .; - - . = ALIGN(4096); __toc_start = .; .toc : { @@ -128,7 +125,6 @@ . = ALIGN(4096); __toc_end = .; - . = ALIGN(4096); __bss_start = .; .bss : { diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/xmon/privinst.h linuxppc64_2_4/arch/ppc64/xmon/privinst.h --- linux-2.4.9-ac10/arch/ppc64/xmon/privinst.h Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/xmon/privinst.h Fri Sep 14 20:00:19 2001 @@ -44,7 +44,6 @@ GSETSPR(275, sprg3) GSETSPR(282, ear) GSETSPR(287, pvr) -#ifndef CONFIG_8xx GSETSPR(528, bat0u) GSETSPR(529, bat0l) GSETSPR(530, bat1u) @@ -58,13 +57,6 @@ GSETSPR(1010, iabr) GSETSPR(1013, dabr) GSETSPR(1023, pir) -#else -GSETSPR(144, cmpa) -GSETSPR(145, cmpb) -GSETSPR(146, cmpc) -GSETSPR(147, cmpd) -GSETSPR(158, ictrl) -#endif static inline int get_sr(int n) { diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/xmon/start.c linuxppc64_2_4/arch/ppc64/xmon/start.c --- linux-2.4.9-ac10/arch/ppc64/xmon/start.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/xmon/start.c Tue Sep 25 13:53:20 2001 @@ -18,12 +18,22 @@ #include #include +/* Transition to udbg isn't quite done yet...but very close. */ +#define USE_UDBG +#ifdef USE_UDBG +#include +#endif + +#ifndef USE_UDBG static volatile unsigned char *sccc, *sccd; +#endif unsigned long TXRDY, RXRDY; extern void xmon_printf(const char *fmt, ...); static int xmon_expect(const char *str, unsigned int timeout); +#ifndef USE_UDBG static int console = 0; +#endif static int via_modem = 0; /* static int xmon_use_sccb = 0; --Unused */ @@ -38,11 +48,12 @@ return ret; } +#ifndef USE_UDBG void buf_access(void) { - if ( _machine == _MACH_chrp ) - sccd[3] &= ~0x80; /* reset DLAB */ + sccd[3] &= ~0x80; /* reset DLAB */ } +#endif extern int adb_init(void); @@ -60,13 +71,15 @@ void xmon_map_scc(void) { + /* This maybe isn't the best place to register sysrq 'x' */ + __sysrq_put_key_op('x', &sysrq_xmon_op); +#ifndef USE_UDBG /* should already be mapped by the kernel boot */ sccd = (volatile unsigned char *) (((unsigned long)comport1)); sccc = (volatile unsigned char *) (((unsigned long)comport1)+5); TXRDY = 0x20; RXRDY = 1; - /* This maybe isn't the best place to register sysrq 'x' */ - __sysrq_put_key_op('x', &sysrq_xmon_op); +#endif } static int scc_initialized = 0; @@ -77,6 +90,9 @@ int xmon_write(void *handle, void *ptr, int nb) { +#ifdef USE_UDBG + return udbg_write(ptr, nb); +#else char *p = ptr; int i, c, ct; @@ -100,6 +116,7 @@ *sccd = c; } return i; +#endif } int xmon_wants_key; @@ -108,6 +125,9 @@ int xmon_read(void *handle, void *ptr, int nb) { +#ifdef USE_UDBG + return udbg_read(ptr, nb); +#else char *p = ptr; int i, c; @@ -123,34 +143,34 @@ *p++ = c; } return i; +#endif } int xmon_read_poll(void) { +#ifdef USE_UDBG + return udbg_getc_poll(); +#else if ((*sccc & RXRDY) == 0) { -#ifdef CONFIG_ADB_PMU - if (sys_ctrler == SYS_CTRLER_PMU) - pmu_poll(); -#endif /* CONFIG_ADB_PMU */ return -1; } buf_access(); return *sccd; +#endif } void xmon_init_scc() { - if ( _machine == _MACH_chrp ) - { - sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ - sccd[0] = 12; eieio(); /* DLL = 9600 baud */ - sccd[1] = 0; eieio(); - sccd[2] = 0; eieio(); /* FCR = 0 */ - sccd[3] = 3; eieio(); /* LCR = 8N1 */ - sccd[1] = 0; eieio(); /* IER = 0 */ - } +#ifndef USE_UDBG + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ +#endif scc_initialized = 1; if (via_modem) { diff -uNr --exclude=CVS linux-2.4.9-ac10/arch/ppc64/xmon/xmon.c linuxppc64_2_4/arch/ppc64/xmon/xmon.c --- linux-2.4.9-ac10/arch/ppc64/xmon/xmon.c Wed Oct 3 12:11:10 2001 +++ linuxppc64_2_4/arch/ppc64/xmon/xmon.c Tue Sep 25 13:53:20 2001 @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -26,13 +27,17 @@ #include -#ifndef _PACA_H #include -#endif #define scanhex xmon_scanhex #define skipbl xmon_skipbl +#ifdef CONFIG_SMP +static unsigned long cpus_in_xmon = 0; +static unsigned long got_xmon = 0; +static volatile int take_xmon = -1; +#endif /* CONFIG_SMP */ + static unsigned long adrs; static int size = 1; static unsigned long ndump = 64; @@ -40,8 +45,6 @@ static unsigned long ncsum = 4096; static int termch; -static unsigned long xmon_running = 0; - static u_int bus_error_jmp[100]; #define setjmp xmon_setjmp #define longjmp xmon_longjmp @@ -113,15 +116,15 @@ static struct bpt *at_breakpoint(unsigned long pc); static void bpt_cmds(void); static void cacheflush(void); -/* static char *pretty_print_addr(unsigned long addr); -Unused -aglitke */ -static char *lookup_name(unsigned long addr); +#ifdef CONFIG_SMP +static void cpu_cmd(void); +#endif /* CONFIG_SMP */ static void csum(void); static void mem_translate(void); static void mem_check(void); static void mem_find_real(void); static void mem_find_vsid(void); static void mem_check_full_group(void); -static void mem_check_zones(void); static void mem_check_pagetable_vsids (void); static void mem_map_check_slab(void); @@ -129,12 +132,15 @@ static void mem_map_check_hash(void); static void mem_check_dup_rpn (void); static void show_task(struct task_struct * p); -static void show_state(void); +static void xmon_show_state(void); static void debug_trace(void); extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned long); extern void printf(const char *fmt, ...); +extern void xmon_vfprintf(void *f, const char *fmt, va_list ap); +extern int xmon_putc(int c, void *f); extern int putchar(int ch); +extern int xmon_read_poll(void); extern int setjmp(u_int *); extern void longjmp(u_int *, int); extern unsigned long _ASR; @@ -151,9 +157,9 @@ bi set instruction breakpoint\n\ bc clear breakpoint\n\ d dump bytes\n\ - dd dump double values\n\ - df dump float values\n\ di dump instructions\n\ + df dump float values\n\ + dd dump double values\n\ e print exception information\n\ f flush cache\n\ h dump hash table\n\ @@ -175,11 +181,24 @@ x exit monitor\n\ "; - -static int xmon_trace; +static int xmon_trace[NR_CPUS]; #define SSTEP 1 /* stepping because of 's' command */ #define BRSTEP 2 /* stepping over breakpoint */ +/* + * Stuff for reading and writing memory safely + */ +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} /* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). A PPC stack frame looks like this: @@ -201,7 +220,6 @@ no functions have been called from the current function. */ - /* A traceback table typically follows each function. The find_tb_table() func will fill in this struct. Note that the struct @@ -245,12 +263,6 @@ int cmd; unsigned long msr; - if (xmon_running) - /* If we're already in xmon, just return */ - return; - - xmon_running = 1; - if (excp == NULL) { /* Ok, grab regs as they are now. This won't do a particularily good job because the @@ -302,23 +314,41 @@ regs.trap = 0; excp = ®s; } + msr = get_msr(); set_msrd(msr & ~MSR_EE); /* disable interrupts */ - remove_bpts(); excprint(excp); +#ifdef CONFIG_SMP + if (test_and_set_bit(smp_processor_id(), &cpus_in_xmon)) + for (;;) + ; + while (test_and_set_bit(0, &got_xmon)) { + if (take_xmon == smp_processor_id()) { + take_xmon = -1; + break; + } + } + /* + * XXX: breakpoints are removed while any cpu is in xmon + */ +#endif /* CONFIG_SMP */ + remove_bpts(); cmd = cmds(excp); if (cmd == 's') { - xmon_trace = SSTEP; + xmon_trace[smp_processor_id()] = SSTEP; excp->msr |= 0x400; } else if (at_breakpoint(excp->nip)) { - xmon_trace = BRSTEP; + xmon_trace[smp_processor_id()] = BRSTEP; excp->msr |= 0x400; } else { - xmon_trace = 0; + xmon_trace[smp_processor_id()] = 0; insert_bpts(); } +#ifdef CONFIG_SMP + clear_bit(0, &got_xmon); + clear_bit(smp_processor_id(), &cpus_in_xmon); +#endif /* CONFIG_SMP */ set_msrd(msr); /* restore interrupt enable */ - xmon_running = 0; } /* Code can call this to get a backtrace and continue. */ @@ -403,8 +433,12 @@ void xmon_irq(int irq, void *d, struct pt_regs *regs) { + unsigned long flags; + __save_flags(flags); + __cli(); printf("Keyboard interrupt\n"); xmon(regs); + __restore_flags(flags); } int @@ -419,7 +453,7 @@ --bp->count; remove_bpts(); excprint(regs); - xmon_trace = BRSTEP; + xmon_trace[smp_processor_id()] = BRSTEP; regs->msr |= 0x400; } else { printf("Stopped at breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); @@ -431,10 +465,10 @@ int xmon_sstep(struct pt_regs *regs) { - if (!xmon_trace) + if (!xmon_trace[smp_processor_id()]) return 0; - if (xmon_trace == BRSTEP) { - xmon_trace = 0; + if (xmon_trace[smp_processor_id()] == BRSTEP) { + xmon_trace[smp_processor_id()] = 0; insert_bpts(); } else { xmon(regs); @@ -449,7 +483,7 @@ --dabr.count; remove_bpts(); excprint(regs); - xmon_trace = BRSTEP; + xmon_trace[smp_processor_id()] = BRSTEP; regs->msr |= 0x400; } else { dabr.instr = regs->nip; @@ -465,7 +499,7 @@ --iabr.count; remove_bpts(); excprint(regs); - xmon_trace = BRSTEP; + xmon_trace[smp_processor_id()] = BRSTEP; regs->msr |= 0x400; } else { xmon(regs); @@ -496,6 +530,8 @@ int i; struct bpt *bp; + if (_machine != _MACH_pSeries) + return; bp = bpts; for (i = 0; i < NBPTS; ++i, ++bp) { if (!bp->enabled) @@ -525,6 +561,8 @@ struct bpt *bp; unsigned instr; + if (_machine != _MACH_pSeries) + return; if (!__is_processor(PV_POWER4)) { set_dabr(0); set_iabr(0); @@ -554,6 +592,9 @@ last_cmd = NULL; for(;;) { +#ifdef CONFIG_SMP + printf("%d:", smp_processor_id()); +#endif /* CONFIG_SMP */ printf("mon> "); fflush(stdout); flush_input(); @@ -606,11 +647,8 @@ mem_check_dup_rpn(); break; case 'i': - show_mem(); - break; - case 'k': - mem_check_zones(); - break; + show_mem(); + break; case 'o': mem_check_pagetable_vsids (); break; @@ -639,6 +677,7 @@ break; case 'M': print_sysmap(); + break; case 'S': super_regs(); break; @@ -659,14 +698,19 @@ printf(help_string); break; case 'p': - show_state(); + xmon_show_state(); break; case 'b': bpt_cmds(); break; - case 'c': + case 'C': csum(); break; +#ifdef CONFIG_SMP + case 'c': + cpu_cmd(); + break; +#endif /* CONFIG_SMP */ case 'T': debug_trace(); break; @@ -685,6 +729,56 @@ } } +#ifdef CONFIG_SMP +static void cpu_cmd(void) +{ + unsigned long cpu; + int timeout; + int cmd; + + cmd = inchar(); + if (cmd == 'i') { + printf("stopping all cpus\n"); + /* interrupt other cpu(s) */ + cpu = MSG_ALL_BUT_SELF; + smp_send_xmon_break(cpu); + return; + } + termch = cmd; + if (!scanhex(&cpu)) { + /* print cpus waiting or in xmon */ + printf("cpus stopped:"); + for (cpu = 0; cpu < NR_CPUS; ++cpu) { + if (test_bit(cpu, &cpus_in_xmon)) { + printf(" %d", cpu); + if (cpu == smp_processor_id()) + printf("*", cpu); + } + } + printf("\n"); + return; + } + /* try to switch to cpu specified */ + take_xmon = cpu; + timeout = 10000000; + while (take_xmon >= 0) { + if (--timeout == 0) { + /* yes there's a race here */ + take_xmon = -1; + printf("cpu %u didn't take control\n", cpu); + return; + } + } + /* now have to wait to be given control back */ + while (test_and_set_bit(0, &got_xmon)) { + if (take_xmon == smp_processor_id()) { + take_xmon = -1; + break; + } + } +} +#endif /* CONFIG_SMP */ + static unsigned short fcstab[256] = { 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, @@ -729,8 +823,10 @@ unsigned short fcs; unsigned char v; - scanhex((void *)&adrs); - scanhex((void *)&ncsum); + if (!scanhex(&adrs)) + return; + if (!scanhex(&ncsum)) + return; fcs = 0xffff; for (i = 0; i < ncsum; ++i) { if (mread(adrs+i, &v, 1) == 0) { @@ -946,7 +1042,6 @@ || (stack[2] == (unsigned long)ret_from_syscall_1 && (funcname = "ret_from_syscall_1")) #if 0 - || stack[2] == (unsigned) &ret_from_intercept || stack[2] == (unsigned) &ret_from_syscall_2 || stack[2] == (unsigned) &do_bottom_half_ret || stack[2] == (unsigned) &do_signal_ret @@ -1005,6 +1100,7 @@ return x; } +spinlock_t exception_print_lock = SPIN_LOCK_UNLOCKED; void excprint(struct pt_regs *fp) @@ -1012,6 +1108,13 @@ struct task_struct *c; unsigned long p; struct tbtable tab; + unsigned long flags; + + spin_lock_irqsave(&exception_print_lock, flags); + +#ifdef CONFIG_SMP + printf("cpu %d: ", smp_processor_id()); +#endif /* CONFIG_SMP */ printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(fp->trap), fp); printf(" pc: %lx", fp->nip); @@ -1045,6 +1148,8 @@ printf(" current = %lx, pid = %ld, comm = %s\n", c, c->pid, c->comm); } + + spin_unlock_irqrestore(&exception_print_lock, flags); } void @@ -1156,7 +1261,6 @@ // Dump out relevant Paca data areas. printf("Paca: \n"); ptrPaca = (struct Paca*)get_sprg3(); - printf(" Saved Gpr21=%.16lx Saved Gpr22 =%.16lx \n", ptrPaca->xR21, ptrPaca->xR22); printf(" Local Processor Control Area (LpPaca): \n"); ptrLpPaca = ptrPaca->xLpPacaPtr; @@ -1371,21 +1475,6 @@ } } -/* - * Stuff for reading and writing memory safely - */ -extern inline void sync(void) -{ - asm volatile("sync; isync"); -} - -extern inline void __delay(unsigned int loops) -{ - if (loops != 0) - __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : - "r" (loops) : "ctr"); -} - int mread(unsigned long adrs, void *buf, int size) { @@ -1963,7 +2052,7 @@ } int -hexdigit(c) +hexdigit(int c) { if( '0' <= c && c <= '9' ) return c - '0'; @@ -2020,38 +2109,6 @@ lineptr = str; } -/*static char *pretty_print_addr(unsigned long addr) --Unused - *{ - * printf("%08x", addr); - * if ( lookup_name(addr) ) - * printf(" %s", lookup_name(addr) ); - * return NULL; - *} - */ -static char *lookup_name(unsigned long addr) -{ -return NULL; -#if 0 - /* XXX crap, we don't want the whole of the rest of the map - paulus */ - extern char *sysmap; - extern unsigned long sysmap_size; - char *c = sysmap; - unsigned long cmp; - - if ( !sysmap || !sysmap_size ) - return NULL; - cmp = simple_strtoul(c, &c, 8); - strcpy( last, strsep( &c, "\n")); - while ( c < (sysmap+sysmap_size) ) - { - cmp = simple_strtoul(c, &c, 8); - if ( cmp < addr ) - break; - strcpy( last, strsep( &c, "\n")); - } - return last; -#endif -} /* Starting at codeaddr scan forward for a tbtable and fill in the given table. Return non-zero if successful at doing something. @@ -2169,8 +2226,7 @@ termch = c; scanhex((void *)&ea); - if ( (( ea >= KRANGE_START ) && ( ea <= (KRANGE_START + (1UL<<60) ))) || - (( ea >= BTMALLOC_START) && ( ea <= BTMALLOC_END)) ) { + if ((ea >= KRANGE_START) && (ea <= (KRANGE_START + (1UL<<60)))) { ptep = 0; vsid = get_kernel_vsid(ea); va = ( vsid << 28 ) | ( ea & 0x0fffffff ); @@ -2283,7 +2339,6 @@ unsigned long htab_end; unsigned long last_rpn; HPTE *hpte1, *hpte2; - unsigned long linux_pte; htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG htab_end = (unsigned long)htab_data.htab + htab_size_bytes; @@ -2295,10 +2350,10 @@ printf("htab size : %.16lx\n", htab_size_bytes); #if 1 - for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { if ( hpte1->dw0.dw0.v != 0 ) { if ( hpte1->dw1.dw1.rpn <= last_rpn ) { - for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + for(hpte2 = hpte1+1; hpte2 < (HPTE *)htab_end; hpte2++) { if ( hpte2->dw0.dw0.v != 0 ) { if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); @@ -2343,7 +2398,7 @@ printf("htab base : %.16lx\n", htab_data.htab); printf("htab size : %.16lx\n", htab_size_bytes); - for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { if ( hpte1->dw0.dw0.v != 0 ) { if ( hpte1->dw1.dw1.rpn == rpn ) { printf(" Found rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); @@ -2374,7 +2429,7 @@ printf("htab base : %.16lx\n", htab_data.htab); printf("htab size : %.16lx\n", htab_size_bytes); - for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { if ( hpte1->dw0.dw0.v != 0 ) { if ( ((hpte1->dw0.dw0.avpn)>>5) == vsid ) { printf(" Found vsid: %.16lx \n", ((hpte1->dw0.dw0.avpn) >> 5)); @@ -2427,164 +2482,35 @@ void mem_map_check_hash() { - int i; - - i = max_mapnr; - + int i = max_mapnr; - while (i-- > 0) { - // skip the reserved - if (!PageReserved(mem_map+i)) - { - - if (((mem_map+i)->next_hash) != NULL){ - if (((unsigned long)((mem_map+i)->next_hash) & 0xf000000000000000) != 0xC000000000000000){ - printf(" mem_map check hash - non c0 entry - address/value = %p %lx \n", - mem_map+i,(mem_map+i)->next_hash); + while (i-- > 0) { + /* skip the reserved */ + if (!PageReserved(mem_map+i)) { + if (((mem_map+i)->next_hash) != NULL) { + if ( REGION_ID((mem_map+i)->next_hash) != KERNEL_REGION_ID ) { + printf(" mem_map check hash - non c0 entry - " + "address/value = %p %lx\n", mem_map+i,(mem_map+i)->next_hash); } - if (((mem_map+i)->next_hash) == 0xC000000000000000){ - printf(" mem_map check hash - 0xC000000000000000 entry = %p \n", mem_map+i); - } - } - } - else - { - if (page_count(mem_map+i) < 0) - { - printf(" reserved page with negative count- entry = %lx \n", mem_map + i); + if ((unsigned long)((mem_map+i)->next_hash) == KERNELBASE){ + printf(" mem_map check hash - 0x%lx entry = %p \n", + KERNELBASE, mem_map+i); + } + } + } else { + if (page_count(mem_map+i) < 0) { + printf(" reserved page with negative count- entry = %lx \n", mem_map+i); } } - - } - + } printf(" mem_map check hash completed \n"); - } - -void mem_check_zones() -{ - pg_data_t *pgdat; - unsigned long order; - unsigned type; - int i, mem_map_zone_count, mem_map_zone_flag_zero_count, mem_map_reserved_count; - int mem_map_zone_count_reserved, mem_map_zone_flag_nonzero_count; - - pgdat = pgdat_list; - - - printf(" nr_inactive_clean_pages = %lx, nr_free_pages = %lx \n", - nr_inactive_clean_pages(), - nr_free_pages() - ); - - for (type = 0; type < MAX_NR_ZONES; type++) { - struct list_head *head, *curr; - zone_t *zone = pgdat->node_zones + type; - unsigned long nr, total, flags; - - - printf("zone info zone = %p free pages = %lx \n", - zone, zone->free_pages); - printf(" inactive_clean_pages = %lx \n", zone -> inactive_clean_pages); - printf(" inactive_dirty_pages = %lx \n", zone -> inactive_dirty_pages); - printf(" pages_min = %lx \n", zone -> pages_min); - printf(" pages_low = %lx \n" , zone -> pages_low); - printf(" pages_high = %lx \n" , zone -> pages_high); - - total = 0; - if (zone->size) { - spin_lock_irqsave(&zone->lock, flags); - for (order = 0; order < MAX_ORDER; order++) { - head = &(zone->free_area + order)->free_list; - curr = head; - nr = 0; - printf(" pages on free list for order = %d \n", order); - for (;;) { - curr = memlist_next(curr); - - if (curr == head) - break; - printf(" page = %lx flags= %lx \n", curr, ((struct page *)curr)->flags); - nr++; - // add code to check zone map to assure pages are free - } - total += nr * (1 << order); - printf(" entries of order = %d order size in pages = %lx \n", order, 1< 0) { - // skip the reserved - if (!PageReserved(mem_map+i)) - { - if (((mem_map+i)->zone) == zone){ - mem_map_zone_count ++; - if (((mem_map+i)->flags) == 0){ - mem_map_zone_flag_zero_count ++; - - } - else - { - mem_map_zone_flag_nonzero_count ++; - } - - } - // all non reserved pages should be in zone 0 - if (((mem_map+i)->zone) != pgdat->node_zones){ - printf( " page in wrong zone - entry = %p zone = %lx \n", mem_map+i,(mem_map+i)->zone); - } - - } - else - { - mem_map_reserved_count ++; - if (((mem_map+i)->zone) == zone){ - mem_map_zone_count_reserved ++; - } - - } - } - - - printf(" mem_map zone= %p mem_map count for zone = %lx",zone, mem_map_zone_count); - printf(" mem_map_flag_zero_count = %lx\n",mem_map_zone_flag_zero_count); - printf(" mem_map_flag_nonzero_count = %lx\n",mem_map_zone_flag_nonzero_count); - printf(" mem_map_reserved_count = %lx\n",mem_map_reserved_count); - printf(" mem_map_zone_count_reserved = %lx\n",mem_map_zone_count_reserved); - - - spin_unlock_irqrestore(&zone->lock, flags); - } - - } - - - -} - - void mem_check_dup_rpn () { unsigned long htab_size_bytes; unsigned long htab_end; unsigned long last_rpn; HPTE *hpte1, *hpte2; - unsigned long linux_pte; int dup_count; struct task_struct *p; unsigned long kernel_vsid_c0,kernel_vsid_c1,kernel_vsid_c2,kernel_vsid_c3; @@ -2602,11 +2528,11 @@ printf("htab size : %.16lx\n", htab_size_bytes); - for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { if ( hpte1->dw0.dw0.v != 0 ) { if ( hpte1->dw1.dw1.rpn <= last_rpn ) { dup_count = 0; - for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + for(hpte2 = hpte1+1; hpte2 < (HPTE *)htab_end; hpte2++) { if ( hpte2->dw0.dw0.v != 0 ) { if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { dup_count++; @@ -2617,7 +2543,7 @@ printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); printf(" mem map array entry %p count = %d \n", (mem_map+(hpte1->dw1.dw1.rpn)), (mem_map+(hpte1->dw1.dw1.rpn))->count); - for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + for(hpte2 = hpte1+1; hpte2 < (HPTE *)htab_end; hpte2++) { if ( hpte2->dw0.dw0.v != 0 ) { if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", @@ -2696,7 +2622,6 @@ int found; unsigned long user_address_table_count,kernel_page_table_count; unsigned long pt_vsid; - unsigned long linux_pte; HPTE *hpte1; @@ -2714,7 +2639,7 @@ bogus_rpn_count = 0; user_address_table_count = 0; kernel_page_table_count = 0; - for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + for(hpte1 = htab_data.htab; hpte1 < (HPTE *)htab_end; hpte1++) { if ( hpte1->dw0.dw0.v != 0 ) { valid_table_count++; if ( hpte1->dw1.dw1.rpn <= last_rpn ) { @@ -2931,8 +2856,8 @@ ((p->thread.regs)->ctr), ((p->thread.regs)->link)); printf(" xer : 0x%16.16lx ccr : 0x%16.16lx\n", ((p->thread.regs)->xer), ((p->thread.regs)->ccr)); - printf(" mq : 0x%16.16lx trap : 0x%16.16lx\n", - ((p->thread.regs)->mq), ((p->thread.regs)->trap)); + printf(" trap : 0x%16.16lx\n", + ((p->thread.regs)->trap)); printf(" dar : 0x%16.16lx dsis : 0x%16.16lx\n", ((p->thread.regs)->dar), ((p->thread.regs)->dsisr)); printf(" rslt : 0x%16.16lx org3 : 0x%16.16lx\n", @@ -2951,7 +2876,7 @@ } -static void show_state(void) +static void xmon_show_state(void) { struct task_struct *p; @@ -3017,6 +2942,3 @@ cmd = skipbl(); } } - - - diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/Makefile linuxppc64_2_4/drivers/Makefile --- linux-2.4.9-ac10/drivers/Makefile Wed Oct 3 12:11:11 2001 +++ linuxppc64_2_4/drivers/Makefile Thu Sep 27 14:00:26 2001 @@ -8,7 +8,7 @@ mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ - fc4 net/hamradio i2c acpi bluetooth + fc4 net/hamradio i2c acpi bluetooth iseries subdir-y := parport char block net sound misc media cdrom subdir-m := $(subdir-y) @@ -25,6 +25,7 @@ subdir-$(CONFIG_VT) += video subdir-$(CONFIG_MAC) += macintosh subdir-$(CONFIG_ALL_PPC) += macintosh +subdir-$(CONFIG_PPC_ISERIES) += iseries subdir-$(CONFIG_USB) += usb subdir-$(CONFIG_INPUT) += input subdir-$(CONFIG_PHONE) += telephony diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/block/genhd.c linuxppc64_2_4/drivers/block/genhd.c --- linux-2.4.9-ac10/drivers/block/genhd.c Wed Oct 3 12:11:12 2001 +++ linuxppc64_2_4/drivers/block/genhd.c Fri Sep 28 15:44:22 2001 @@ -194,6 +194,9 @@ #ifdef CONFIG_VT console_map_init(); #endif +#ifdef CONFIG_VIODASD + viodasd_init(); +#endif return 0; } diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/block/ll_rw_blk.c linuxppc64_2_4/drivers/block/ll_rw_blk.c --- linux-2.4.9-ac10/drivers/block/ll_rw_blk.c Wed Oct 3 12:11:12 2001 +++ linuxppc64_2_4/drivers/block/ll_rw_blk.c Fri Sep 28 15:44:22 2001 @@ -1229,6 +1229,9 @@ #ifdef CONFIG_BLK_DEV_XD xd_init(); #endif +#ifdef CONFIG_VIOCD + viocd_init(); +#endif #ifdef CONFIG_BLK_DEV_MFM mfm_init(); #endif diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/char/Config.in linuxppc64_2_4/drivers/char/Config.in --- linux-2.4.9-ac10/drivers/char/Config.in Wed Oct 3 12:11:12 2001 +++ linuxppc64_2_4/drivers/char/Config.in Fri Sep 28 15:44:23 2001 @@ -180,6 +180,9 @@ dep_tristate 'Intel i8x0 Random Number Generator support' CONFIG_INTEL_RNG $CONFIG_PCI tristate '/dev/nvram support' CONFIG_NVRAM +if [ "$CONFIG_PPC_ISERIES" != "y" ]; then + tristate 'Enhanced Real Time Clock Support' CONFIG_RTC +fi tristate 'Enhanced Real Time Clock Support' CONFIG_RTC if [ "$CONFIG_IA64" = "y" ]; then bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/char/n_tty.c linuxppc64_2_4/drivers/char/n_tty.c --- linux-2.4.9-ac10/drivers/char/n_tty.c Fri Apr 6 12:42:55 2001 +++ linuxppc64_2_4/drivers/char/n_tty.c Wed Jun 6 18:47:40 2001 @@ -538,7 +538,7 @@ * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, &tty->process_char_map) || tty->lnext) { + if (!test_bit(c, tty->process_char_map) || tty->lnext) { finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { @@ -659,7 +659,7 @@ handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, &tty->read_flags); + set_bit(tty->read_head, tty->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; @@ -811,38 +811,38 @@ memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', &tty->process_char_map); + set_bit('\r', tty->process_char_map); if (I_INLCR(tty)) - set_bit('\n', &tty->process_char_map); + set_bit('\n', tty->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), &tty->process_char_map); - set_bit(KILL_CHAR(tty), &tty->process_char_map); - set_bit(EOF_CHAR(tty), &tty->process_char_map); - set_bit('\n', &tty->process_char_map); - set_bit(EOL_CHAR(tty), &tty->process_char_map); + set_bit(ERASE_CHAR(tty), tty->process_char_map); + set_bit(KILL_CHAR(tty), tty->process_char_map); + set_bit(EOF_CHAR(tty), tty->process_char_map); + set_bit('\n', tty->process_char_map); + set_bit(EOL_CHAR(tty), tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(LNEXT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(EOL2_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), &tty->process_char_map); - set_bit(STOP_CHAR(tty), &tty->process_char_map); + set_bit(START_CHAR(tty), tty->process_char_map); + set_bit(STOP_CHAR(tty), tty->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), &tty->process_char_map); - set_bit(QUIT_CHAR(tty), &tty->process_char_map); - set_bit(SUSP_CHAR(tty), &tty->process_char_map); + set_bit(INTR_CHAR(tty), tty->process_char_map); + set_bit(QUIT_CHAR(tty), tty->process_char_map); + set_bit(SUSP_CHAR(tty), tty->process_char_map); } - clear_bit(__DISABLED_CHAR, &tty->process_char_map); + clear_bit(__DISABLED_CHAR, tty->process_char_map); sti(); tty->raw = 0; tty->real_raw = 0; @@ -1058,7 +1058,7 @@ int eol; eol = test_and_clear_bit(tty->read_tail, - &tty->read_flags); + tty->read_flags); c = tty->read_buf[tty->read_tail]; spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/char/tty_io.c linuxppc64_2_4/drivers/char/tty_io.c --- linux-2.4.9-ac10/drivers/char/tty_io.c Wed Oct 3 12:11:15 2001 +++ linuxppc64_2_4/drivers/char/tty_io.c Fri Sep 28 15:44:23 2001 @@ -2239,6 +2239,11 @@ * set up the console device so that later boot sequences can * inform about problems etc.. */ + +#ifdef CONFIG_VIOCONS + viocons_init(); +#endif + #ifdef CONFIG_VT con_init(); #endif @@ -2345,6 +2350,10 @@ /* console calls tty_register_driver() before kmalloc() works. * Thus, we can't devfs_register() then. Do so now, instead. */ +#ifdef CONFIG_VIOCONS + viocons_init2(); +#endif + #ifdef CONFIG_VT con_init_devfs(); #endif diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/Makefile linuxppc64_2_4/drivers/iseries/Makefile --- linux-2.4.9-ac10/drivers/iseries/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/Makefile Thu Sep 27 14:00:26 2001 @@ -0,0 +1,43 @@ +# +# Makefile for the iSeries-specific device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +# The target object and module list name. + +# O_TARGET := macintosh.o + +O_TARGET := iseries.o + +# Objects that export symbols. + +# export-objs := adb.o rtc.o mac_hid.o via-pmu.o + +export-objs := veth.o viocons.o viotape.o viodasd.o viocd.o + +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Each configuration option enables a list of files. + +obj-$(CONFIG_VETH) += veth.o +obj-$(CONFIG_VIOCONS) += viocons.o +obj-$(CONFIG_VIOPATH) += viopath.o +obj-$(CONFIG_VIOTAPE) += viotape.o +obj-$(CONFIG_VIODASD) += viodasd.o +obj-$(CONFIG_VIOCD) += viocd.o ../cdrom/cdrom.o + +# The global Rules.make. + +include $(TOPDIR)/Rules.make + diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/veth-proc.c linuxppc64_2_4/drivers/iseries/veth-proc.c --- linux-2.4.9-ac10/drivers/iseries/veth-proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/veth-proc.c Thu Sep 27 14:00:26 2001 @@ -0,0 +1,73 @@ +/* File veth-proc.c created by Kyle A. Lucke on Wed Oct 18 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_H +#include "veth.h" +#endif + + +static struct proc_dir_entry * veth_proc_root = NULL; + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + long i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + long vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lp%ld", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%ld", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } +} + diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/veth.c linuxppc64_2_4/drivers/iseries/veth.c --- linux-2.4.9-ac10/drivers/iseries/veth.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/veth.c Mon Oct 1 12:12:49 2001 @@ -0,0 +1,1761 @@ +/* File veth.c created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/**************************************************************************/ +/* */ +/* IBM eServer iSeries Virtual Ethernet Device Driver */ +/* Copyright (C) 2001 Kyle A. Lucke (klucke@us.ibm.com), IBM Corp. */ +/* */ +/* 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 */ +/* */ +/* This module contains the implementation of a virtual ethernet device */ +/* for use with iSeries LPAR Linux. It utilizes low-level message passing*/ +/* provided by the hypervisor to enable an ethernet-like network device */ +/* that can be used to enable inter-partition communications on the same */ +/* physical iSeries. */ +/* */ +/* The iSeries LPAR hypervisor has currently defined the ability for a */ +/* partition to communicate on up to 16 different virtual ethernets, all */ +/* dynamically configurable, at least for an OS/400 partition. The */ +/* dynamic nature is not supported for Linux yet. */ +/* */ +/* Each virtual ethernet a given Linux partition participates in will */ +/* cause a network device with the form vethXX to be created, where XX is */ +/* a decimal number from 0 to 15, corresponding to the virtual ethernet */ +/* the given netdevice talks on. This is slightly different from the */ +/* standard eth0, eth1, etc. way of naming network devices, but without */ +/* this little naming convention, it would not be as easy to configure */ +/* the tcpip interfaces to a given veth device, and if the partition */ +/* was configured to use a new virtual ethernet at some point, the devices*/ +/* would most likely get renumbered. */ +/* */ +/* This driver (and others like it on other partitions) is responsible */ +/* routing packets to and from other partitions. The MAC addresses used */ +/* by the virtual ethernets contain meaning, and should not be modified. */ +/* Doing so could disable the ability of your Linux partition to */ +/* communicate with the other OS/400 partitions on your physical iSeries. */ +/* Similarly, setting the MAC address to something other than the */ +/* "virtual burned-in" address is not allowed, for the same reason. */ +/* */ +/* Notes: */ +/* */ +/* 1. Although there is the capability to talk on multiple shared */ +/* ethernets to communicate to the same partition, each shared */ +/* ethernet to a given partition X will use a finite, shared amount */ +/* of hypervisor messages to do the communication. So having 2 shared */ +/* ethernets to the same remote partition DOES NOT double the */ +/* available bandwidth. Each of the 2 shared ethernets will share the */ +/* same bandwidth available to another. */ +/* */ +/* 2. It is allowed to have a virtual ethernet that does not communicate */ +/* with any other partition. It won't do anything, but it's allowed. */ +/* */ +/* 3. There is no "loopback" mode for a virtual ethernet device. If you */ +/* send a packet to your own mac address, it will just be dropped, you */ +/* won't get it on the receive side. Such a thing could be done, */ +/* but my default driver DOES NOT do so. */ +/* */ +/* 4. Multicast addressing is implemented via broadcasting the multicast */ +/* frames to other partitions. It is the responsibility of the */ +/* receiving partition to filter the addresses desired. */ +/* */ +/* 5. This module utilizes several different bottom half handlers for */ +/* non-high-use path function (setup, error handling, etc.). Multiple */ +/* bottom halves were used because only one would not keep up to the */ +/* much faster iSeries device drivers this Linux driver is talking to. */ +/* All hi-priority work (receiving frames, handling frame acks) is done*/ +/* in the interrupt handler for maximum performance. */ +/* */ +/* Tunable parameters: */ +/* */ +/* VethBuffersToAllocate: This compile time option defaults to 120. It can*/ +/* be safely changed to something greater or less than the default. It */ +/* controls how much memory Linux will allocate per remote partition it is*/ +/* communicating with. The user can play with this to see how it affects */ +/* performance, packets dropped, etc. Without trying to understand the */ +/* complete driver, it can be thought of as the maximum number of packets */ +/* outstanding to a remote partition at a time. */ +/* */ +/**************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VETH_H +#include "veth.h" +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _ISERIES_PROC_H +#include +#endif +#include +#include + + +#define veth_printk(fmt, args...) \ +printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + +#define veth_error_printk(fmt, args...) \ +printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args) + +#ifdef MODULE + #define VIRT_TO_ABSOLUTE(a) virt_to_absolute_outline(a) +#else + #define VIRT_TO_ABSOLUTE(a) virt_to_absolute(a) +#endif + +static const char __initdata *version = +"v0.9 02/15/2001 Kyle Lucke, klucke@us.ibm.com\n"; + +static int probed __initdata = 0; +#define VethBuffersToAllocate 120 + +static struct VethFabricMgr *mFabricMgr = NULL; +static struct proc_dir_entry * veth_proc_root = NULL; + +DECLARE_MUTEX_LOCKED(VethProcSemaphore); + +static int veth_open(struct net_device *dev); +static int veth_close(struct net_device *dev); +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static void veth_handleEvent(struct HvLpEvent *, struct pt_regs *); +static void veth_handleAck(struct HvLpEvent *); +static void veth_handleInt(struct HvLpEvent *); +static void veth_openConnections(void); +static void veth_openConnection(u8, int lockMe); +static void veth_closeConnection(u8, int lockMe); +static void veth_intFinishOpeningConnections(void *, int number); +static void veth_finishOpeningConnections(void *); +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *); +static int veth_multicast_wanted(struct VethPort *port, u64 dest); +static void veth_set_multicast_list(struct net_device *dev); + +static void veth_sendCap(struct VethLpConnection *); +static void veth_sendMonitor(struct VethLpConnection *); +static void veth_takeCap(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeCapAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_takeMonitorAck(struct VethLpConnection *, struct VethLpEvent *); +static void veth_msgsInit(struct VethLpConnection *connection); +static void veth_recycleMsg(struct VethLpConnection *, u16); +static void veth_capBh(struct VethLpConnection *); +static void veth_capAckBh(struct VethLpConnection *); +static void veth_monitorAckBh(struct VethLpConnection *); +static void veth_takeFrames(struct VethLpConnection *, struct VethLpEvent *); +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev); +static struct net_device_stats *veth_get_stats(struct net_device *dev); +static void veth_intFinishMsgsInit(void *, int); +static void veth_finishMsgsInit(struct VethLpConnection *connection); +static void veth_intFinishCapBh(void *, int); +static void veth_finishCapBh(struct VethLpConnection *connection); +static void veth_finishCapBhLocked(struct VethLpConnection *connection); +static void veth_finishSendCap(struct VethLpConnection *connection); +static void veth_timedAck(unsigned long connectionPtr); +#ifdef MODULE +static void veth_waitForEnd(void); +#endif +static void veth_failMe(struct VethLpConnection *connection); + +extern struct pci_dev * iSeries_veth_dev; + +int __init veth_probe(void) +{ + struct net_device *dev= NULL; + struct VethPort *port = NULL; + int vlansFound = 0; + int displayVersion = 0; + + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + if (probed) + return -ENODEV; + probed = 1; + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + vlansFound++; + + dev = init_vethdev(NULL, sizeof(struct VethPort), vlanIndex); + + if (dev == NULL) { + veth_error_printk("Unable to allocate net_device structure!\n"); + break; + } + + if (!dev->priv) + dev->priv = kmalloc(sizeof(struct VethPort), GFP_KERNEL); + if (!dev->priv) { + veth_error_printk("Unable to allocate memory\n"); + return -ENOMEM; + } + + veth_printk("Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev); + port = mFabricMgr->mPorts[vlanIndex] = (struct VethPort *)dev->priv; + memset(port, 0, sizeof(struct VethPort)); + rwlock_init(&(port->mMcastGate)); + mFabricMgr->mPorts[vlanIndex]->mDev = dev; + + dev->dev_addr[0] = 0x02; + dev->dev_addr[1] = 0x01; + dev->dev_addr[2] = 0xFF; + dev->dev_addr[3] = vlanIndex; + dev->dev_addr[4] = 0xFF; + dev->dev_addr[5] = HvLpConfig_getLpIndex_outline(); + dev->mtu = 9000; + + memcpy(&(port->mMyAddress), dev->dev_addr, 6); + + dev->open = &veth_open; + dev->hard_start_xmit = &veth_start_xmit; + dev->stop = &veth_close; + dev->get_stats = veth_get_stats; + dev->set_multicast_list = &veth_set_multicast_list; + dev->do_ioctl = &veth_ioctl; + + /* display version info if adapter is found */ + if (!displayVersion) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + displayVersion = 1; + veth_printk("%s", version); + } + + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + if (vlansFound > 0) + return 0; + else + return -ENODEV; +} + +#ifdef MODULE +MODULE_AUTHOR("Kyle Lucke "); +MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); + +DECLARE_MUTEX_LOCKED(VethModuleBhDone); +int VethModuleReopen = 1; + +void veth_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + int i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + int vlanIndex = 0; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + char name[10] = ""; + sprintf(name, "lpar%d", i); + remove_proc_entry(name, veth_proc_root); + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + char name[10] = ""; + sprintf(name, "veth%d", vlanIndex); + remove_proc_entry(name, veth_proc_root); + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + remove_proc_entry("veth", iSeries_proc); + + up(&VethProcSemaphore); +} + +void veth_waitForEnd(void) +{ + up(&VethModuleBhDone); +} + +void __exit veth_module_cleanup(void) +{ + int i; + struct VethFabricMgr *myFm = mFabricMgr; + struct tq_struct myBottomHalf; + struct net_device *thisOne = NULL; + + VethModuleReopen = 0; + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + veth_closeConnection(i, 1); + } + + myBottomHalf.routine = (void *)(void *)veth_waitForEnd; + + queue_task(&myBottomHalf, &tq_immediate); + mark_bh(IMMEDIATE_BH); + + down(&VethModuleBhDone); + + HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); + + mb(); + mFabricMgr = NULL; + mb(); + + down(&VethProcSemaphore); + + iSeries_proc_callback(&veth_proc_delete); + + down(&VethProcSemaphore); + + for (i = 0; i < HvMaxArchitectedLps; ++i) + { + if (myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs > 0) + { + mf_deallocateLpEvents(myFm->mConnection[i].mRemoteLp, + HvLpEvent_Type_VirtualLan, + myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs, + NULL, + NULL); + } + + if (myFm->mConnection[i].mMsgs != NULL) + { + kfree(myFm->mConnection[i].mMsgs); + } + } + + for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) + { + if (myFm->mPorts[i] != NULL) + { + thisOne = myFm->mPorts[i]->mDev; + myFm->mPorts[i] = NULL; + + mb(); + + if (thisOne != NULL) + { + veth_printk("Unregistering %s (veth=%d)\n", thisOne->name, i); + unregister_netdev(thisOne); + } + } + } + + kfree(myFm); +} + +module_exit(veth_module_cleanup); +#endif + + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + long i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + long vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lpar%d", (int)i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%d", (int)vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } + + up(&VethProcSemaphore); +} + +int __init veth_module_init(void) +{ + int status; + int i; + + mFabricMgr = kmalloc(sizeof(struct VethFabricMgr), GFP_KERNEL); + memset(mFabricMgr, 0, sizeof(struct VethFabricMgr)); + veth_printk("Initializing veth module, fabric mgr (address=%p)\n", mFabricMgr); + + mFabricMgr->mEyecatcher = 0x56455448464D4752ULL; + mFabricMgr->mThisLp = HvLpConfig_getLpIndex_outline(); + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + mFabricMgr->mConnection[i].mEyecatcher = 0x564554484C50434EULL; + veth_failMe(mFabricMgr->mConnection+i); + spin_lock_init(&mFabricMgr->mConnection[i].mAckGate); + spin_lock_init(&mFabricMgr->mConnection[i].mStatusGate); + } + + status = veth_probe(); + + if (status == 0) + { + veth_openConnections(); + } + + iSeries_proc_callback(&veth_proc_init); + + return status; +} + +module_init(veth_module_init); + +static void veth_failMe(struct VethLpConnection *connection) +{ + connection->mConnectionStatus.mSentCap = 0; + connection->mConnectionStatus.mCapAcked = 0; + connection->mConnectionStatus.mGotCap = 0; + connection->mConnectionStatus.mGotCapAcked = 0; + connection->mConnectionStatus.mSentMonitor = 0; + connection->mConnectionStatus.mFailed = 1; +} + +static int veth_open(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + memset(&port->mStats, 0, sizeof(port->mStats)); + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + + return 0; +} + +static int veth_close(struct net_device *dev) +{ + netif_stop_queue(dev); + + MOD_DEC_USE_COUNT; + + return 0; +} + +static struct net_device_stats *veth_get_stats(struct net_device *dev) +{ + struct VethPort *port = (struct VethPort *)dev->priv; + + return(&port->mStats); +} + + +static int veth_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned char *frame = skb->data; + HvLpIndex remoteLp = frame[5]; + int i = 0; + int clone = 0; + + if (mFabricMgr == NULL) + { + veth_error_printk("NULL fabric manager with active ports!\n"); + netif_stop_queue(dev); + return 1; + } + + mb(); + + if ((*frame & 0x01) != 0x01) /* broadcast or multicast */ + { + if ((remoteLp != mFabricMgr->mThisLp) && + (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, remoteLp))) + veth_pTransmit(skb, remoteLp, dev); + } + else + { + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != mFabricMgr->mThisLp) + { + if (clone) + skb = skb_clone(skb, GFP_ATOMIC); + else + clone = 1; + + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + /* the ack handles deleting the skb */ + veth_pTransmit(skb, i, dev); + } + } + } + } + + return 0; +} + +static void veth_pTransmit(struct sk_buff *skb, HvLpIndex remoteLp, struct net_device *dev) +{ + struct VethLpConnection *connection = mFabricMgr->mConnection + remoteLp; + HvLpEvent_Rc returnCode; + + if (connection->mConnectionStatus.mFailed != 1) + { + struct VethMsg *msg = NULL; + VETHSTACKPOP(&(connection->mMsgStack), msg); + + if (msg != NULL) + { + if ((skb->len > 14) && + (skb->len <= 9018)) + { + dma_addr_t dma_addr = pci_map_single(iSeries_veth_dev, + skb->data, + skb->len, + PCI_DMA_TODEVICE); + + + + if (dma_addr != -1) + { + msg->mSkb = skb; + msg->mEvent.mSendData.mAddress[0] = dma_addr; + msg->mEvent.mSendData.mLength[0] = skb->len; + msg->mEvent.mSendData.mEofMask = 0xFFFFFFFFUL; + + test_and_set_bit(0, &(msg->mInUse)); + + returnCode = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFrames, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + msg->mIndex, + msg->mEvent.mFpData.mData1, + msg->mEvent.mFpData.mData2, + msg->mEvent.mFpData.mData3, + msg->mEvent.mFpData.mData4, + msg->mEvent.mFpData.mData5); + } + else + { + returnCode = -1; /* Bad return code */ + } + + if (returnCode != HvLpEvent_Rc_Good) + { + struct VethPort *port = (struct VethPort *)dev->priv; + + if (msg->mEvent.mSendData.mAddress[0]) + { + pci_unmap_single(iSeries_veth_dev, dma_addr, skb->len, PCI_DMA_TODEVICE); + } + + dev_kfree_skb_irq(skb); + + msg->mSkb = NULL; + memset(&(msg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&(connection->mMsgStack), msg); + port->mStats.tx_dropped++; + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_packets++; + port->mStats.tx_bytes += skb->len; + } + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } + } + else + { + struct VethPort *port = (struct VethPort *)dev->priv; + port->mStats.tx_dropped++; + } +} + +static int veth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + + return -EOPNOTSUPP; +} + +static void veth_set_multicast_list(struct net_device *dev) +{ + char *addrs; + struct VethPort *port = (struct VethPort *)dev->priv; + u64 newAddress = 0; + unsigned long flags; + + write_lock_irqsave(&port->mMcastGate, flags); + + if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */ + port->mPromiscuous = 1; + } else { + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) { + port->mAllMcast = 1; + } else { + int i; + /* Update table */ + port->mNumAddrs = 0; + + for (i = 0; ((i < dev->mc_count) && (i < 12)); i++) { /* for each address in the list */ + addrs = dmi->dmi_addr; + dmi = dmi->next; + if ((*addrs & 0x01) == 1) { /* multicast address? */ + memcpy(&newAddress, addrs, 6); + newAddress &= 0xFFFFFFFFFFFF0000; + + port->mMcasts[port->mNumAddrs] = newAddress; + mb(); + port->mNumAddrs = port->mNumAddrs + 1; + } + } + } + } + + write_unlock_irqrestore(&port->mMcastGate, flags); +} + + +static void veth_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) + { + veth_handleAck(event); + } + else if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + veth_handleInt(event); + } +} + +static void veth_handleAck(struct HvLpEvent *event) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xTargetLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCapAck(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + veth_takeMonitorAck(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown ack type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_handleInt(struct HvLpEvent *event) +{ + int i=0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[event->xSourceLp]); + struct VethLpEvent *vethEvent = (struct VethLpEvent *)event; + + switch(event->xSubtype) + { + case VethEventTypeCap: + { + veth_takeCap(connection, vethEvent); + break; + } + case VethEventTypeMonitor: + { + /* do nothing... this'll hang out here til we're dead, and the hypervisor will return it for us. */ + break; + } + case VethEventTypeFramesAck: + { + for (i=0; i < VethMaxFramesMsgsAcked; ++i) + { + u16 msg = vethEvent->mDerivedData.mFramesAckData.mToken[i]; + veth_recycleMsg(connection, msg); + } + break; + } + case VethEventTypeFrames: + { + veth_takeFrames(connection, vethEvent); + break; + } + default: + { + veth_error_printk("Unknown interrupt type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); + } + }; +} + +static void veth_openConnections() +{ + int i=0; + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualLan, &veth_handleEvent); + + /* Now I need to run through the active lps and open connections to the ones I'm supposed to + open to. */ + + for (i=HvMaxArchitectedLps-1; i >=0; --i) + { + if (i != mFabricMgr->mThisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) + { + veth_openConnection(i, 1); + } + else + { + veth_closeConnection(i, 1); + } + } + } +} + +static void veth_intFinishOpeningConnections(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberAllocated = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishOpeningConnections(void *parm) +{ + unsigned long flags; + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishOpeningConnectionsLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *connection) +{ + if (connection->mNumberAllocated >= 2) + { + connection->mConnectionStatus.mCapMonAlloced = 1; + veth_sendCap(connection); + } + else + { + veth_error_printk("Couldn't allocate base msgs for lpar %d, only got %d\n", connection->mRemoteLp, connection->mNumberAllocated); + veth_failMe(connection); + } +} + +static void veth_openConnection(u8 remoteLp, int lockMe) +{ + unsigned long flags; + unsigned long flags2; + HvLpInstanceId source; + HvLpInstanceId target; + u64 i = 0; + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + + memset(&connection->mCapBhTq, 0, sizeof(connection->mCapBhTq)); + connection->mCapBhTq.routine = (void *)(void *)veth_capBh; + + memset(&connection->mCapAckBhTq, 0, sizeof(connection->mCapAckBhTq)); + connection->mCapAckBhTq.routine = (void *)(void *)veth_capAckBh; + + memset(&connection->mMonitorAckBhTq, 0, sizeof(connection->mMonitorAckBhTq)); + connection->mMonitorAckBhTq.routine = (void *)(void *)veth_monitorAckBh; + + memset(&connection->mAllocBhTq, 0, sizeof(connection->mAllocBhTq)); + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + connection->mRemoteLp = remoteLp; + + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + + /* clean up non-acked msgs */ + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + + connection->mConnectionStatus.mOpen = 1; + + source = connection->mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + target = connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); + + if (connection->mConnectionStatus.mCapMonAlloced != 1) + { + connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; + mf_allocateLpEvents(remoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + 2, + &veth_intFinishOpeningConnections, + connection); + } + else + { + veth_finishOpeningConnectionsLocked(connection); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_closeConnection(u8 remoteLp, int lockMe) +{ + struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); + unsigned long flags; + unsigned long flags2; + if (lockMe) + spin_lock_irqsave(&connection->mStatusGate, flags); + + del_timer(&connection->mAckTimer); + + if (connection->mConnectionStatus.mOpen == 1) + { + HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + connection->mConnectionStatus.mOpen = 0; + veth_failMe(connection); + + /* reset ack data */ + spin_lock_irqsave(&connection->mAckGate, flags2); + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + connection->mNumAcks = 0; + + spin_unlock_irqrestore(&connection->mAckGate, flags2); + } + + if (lockMe) + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_msgsInit(struct VethLpConnection *connection) +{ + connection->mAllocBhTq.routine = (void *)(void *)veth_finishMsgsInit; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + &veth_intFinishMsgsInit, + connection); +} + +static void veth_intFinishMsgsInit(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + connection->mNumberRcvMsgs = number; + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_intFinishCapBh(void *parm, int number) +{ + struct VethLpConnection *connection = (struct VethLpConnection *)parm; + connection->mAllocBhTq.data = parm; + if (number > 0) + connection->mNumberLpAcksAlloced += number; + + queue_task(&connection->mAllocBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void veth_finishMsgsInit(struct VethLpConnection *connection) +{ + int i=0; + unsigned int numberGotten = 0; + u64 amountOfHeapToGet = connection->mMyCap.mUnionData.mFields.mNumberBuffers * sizeof(struct VethMsg); + char *msgs = NULL; + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (connection->mNumberRcvMsgs >= connection->mMyCap.mUnionData.mFields.mNumberBuffers) + { + msgs = kmalloc(amountOfHeapToGet, GFP_ATOMIC); + + connection->mMsgs = (struct VethMsg *)msgs; + + if (msgs != NULL) + { + memset(msgs, 0, amountOfHeapToGet); + + for (i=0; i < connection->mMyCap.mUnionData.mFields.mNumberBuffers; ++i) + { + connection->mMsgs[i].mIndex = i; + ++numberGotten; + VETHSTACKPUSH(&(connection->mMsgStack), (connection->mMsgs+i)); + } + if (numberGotten > 0) + { + connection->mNumMsgs = numberGotten; + } + } + else + { + kfree(msgs); + connection->mMsgs = NULL; + } + } + + connection->mMyCap.mUnionData.mFields.mNumberBuffers = connection->mNumMsgs; + + if (connection->mNumMsgs < 10) + connection->mMyCap.mUnionData.mFields.mThreshold = 1; + else if (connection->mNumMsgs < 20) + connection->mMyCap.mUnionData.mFields.mThreshold = 4; + else if (connection->mNumMsgs < 40) + connection->mMyCap.mUnionData.mFields.mThreshold = 10; + else + connection->mMyCap.mUnionData.mFields.mThreshold = 20; + + connection->mMyCap.mUnionData.mFields.mTimer = VethAckTimeoutUsec; + + veth_finishSendCap(connection); + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_sendCap(struct VethLpConnection *connection) +{ + if (connection->mMsgs == NULL) + { + connection->mMyCap.mUnionData.mFields.mNumberBuffers = VethBuffersToAllocate; + veth_msgsInit(connection); + } + else + { + veth_finishSendCap(connection); + } +} + +static void veth_finishSendCap(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeCap, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mMyCap.mUnionData.mNoFields.mReserved1, + connection->mMyCap.mUnionData.mNoFields.mReserved2, + connection->mMyCap.mUnionData.mNoFields.mReserved3, + connection->mMyCap.mUnionData.mNoFields.mReserved4, + connection->mMyCap.mUnionData.mNoFields.mReserved5); + + if ((returnCode == HvLpEvent_Rc_PartitionDead) || + (returnCode == HvLpEvent_Rc_PathClosed)) + { + connection->mConnectionStatus.mSentCap = 0; + } + else if (returnCode != HvLpEvent_Rc_Good) + { + veth_error_printk("Couldn't send cap to lpar %d, rc %x\n", connection->mRemoteLp, (int)returnCode); + veth_failMe(connection); + } + else + { + connection->mConnectionStatus.mSentCap = 1; + } +} + +static void veth_takeCap(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapBhPending))) + { + connection->mCapBhTq.data = connection; + memcpy(&connection->mCapEvent, event, sizeof(connection->mCapEvent)); + queue_task(&connection->mCapBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities from lpar %d while already processing one\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +static void veth_takeCapAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mCapAckBhPending))) + { + connection->mCapAckBhTq.data = connection; + memcpy(&connection->mCapAckEvent, event, sizeof(connection->mCapAckEvent)); + queue_task(&connection->mCapAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a capabilities ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_takeMonitorAck(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + if (!test_and_set_bit(0,&(connection->mMonitorAckBhPending))) + { + connection->mMonitorAckBhTq.data = connection; + memcpy(&connection->mMonitorAckEvent, event, sizeof(connection->mMonitorAckEvent)); + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { + veth_error_printk("Received a monitor ack from lpar %d while already processing one\n", connection->mRemoteLp); + } +} + +static void veth_recycleMsg(struct VethLpConnection *connection, u16 msg) +{ + if (msg < connection->mNumMsgs) + { + struct VethMsg *myMsg = connection->mMsgs + msg; + if (test_and_clear_bit(0, &(myMsg->mInUse))) + { + pci_unmap_single(iSeries_veth_dev, + myMsg->mEvent.mSendData.mAddress[0], + myMsg->mEvent.mSendData.mLength[0], + PCI_DMA_TODEVICE); + dev_kfree_skb_irq(myMsg->mSkb); + + myMsg->mSkb = NULL; + memset(&(myMsg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); + VETHSTACKPUSH(&connection->mMsgStack, myMsg); + } + else + { + if (connection->mConnectionStatus.mOpen) + { + veth_error_printk("Received a frames ack for msg %d from lpar %d while not outstanding\n", msg, connection->mRemoteLp); + } + } + } +} + +static void veth_capBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + unsigned long flags; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + u64 numAcks = 0; + spin_lock_irqsave(&connection->mStatusGate, flags); + connection->mConnectionStatus.mGotCap = 1; + + memcpy(remoteCap, &(event->mDerivedData.mCapabilitiesData), sizeof(connection->mRemoteCap)); + + if ((remoteCap->mUnionData.mFields.mNumberBuffers <= VethMaxFramesMsgs) && + (remoteCap->mUnionData.mFields.mNumberBuffers != 0) && + (remoteCap->mUnionData.mFields.mThreshold <= VethMaxFramesMsgsAcked) && + (remoteCap->mUnionData.mFields.mThreshold != 0)) + { + numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + if (connection->mNumberLpAcksAlloced < numAcks) + { + numAcks = numAcks - connection->mNumberLpAcksAlloced; + connection->mAllocBhTq.routine = (void *)(void *)veth_finishCapBh; + mf_allocateLpEvents(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + sizeof(struct VethLpEvent), + numAcks, + &veth_intFinishCapBh, + connection); + } + else + veth_finishCapBhLocked(connection); + } + else + { + veth_error_printk("Received incompatible capabilities from lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_InvalidSubtypeData; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } + + clear_bit(0,&(connection->mCapBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_capAckBh(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapAckEvent; + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + if (event->mBaseEvent.xRc == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mCapAcked = 1; + + if ((connection->mConnectionStatus.mGotCap == 1) && + (connection->mConnectionStatus.mGotCapAcked == 1)) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Bad rc(%d) from lpar %d on capabilities\n", event->mBaseEvent.xRc, connection->mRemoteLp); + veth_failMe(connection); + } + + clear_bit(0,&(connection->mCapAckBhPending)); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_monitorAckBh(struct VethLpConnection *connection) +{ + unsigned long flags; + + spin_lock_irqsave(&connection->mStatusGate, flags); + + veth_failMe(connection); + + veth_printk("Monitor ack returned for lpar %d\n", connection->mRemoteLp); + + if (connection->mConnectionStatus.mOpen) + { + veth_closeConnection(connection->mRemoteLp, 0); + + udelay(100); + + queue_task(&connection->mMonitorAckBhTq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + else + { +#ifdef MODULE + if (VethModuleReopen) +#endif + veth_openConnection(connection->mRemoteLp, 0); +#ifdef MODULE + else + { + int i=0; + + for (i=0; i < connection->mNumMsgs; ++i) + { + veth_recycleMsg(connection, i); + } + } +#endif + clear_bit(0,&(connection->mMonitorAckBhPending)); + } + + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +#define number_of_pages(v, l) ((((unsigned long)(v) & ((1 << 12) - 1)) + (l) + 4096 - 1) / 4096) +#define page_offset(v) ((unsigned long)(v) & ((1 << 12) - 1)) + +static void veth_takeFrames(struct VethLpConnection *connection, struct VethLpEvent *event) +{ + int i; + struct VethPort *port = NULL; + struct BufList + { + union + { + struct + { + u32 token2; + u32 garbage; + } token1; + u64 address; + } addr; + u64 size; + }; + + struct BufList myBufList[4]; + struct BufList remoteList; + + for (i=0; i < VethMaxFramesPerMsg; ++i) + { + u16 length = event->mDerivedData.mSendData.mLength[i]; + u32 address = event->mDerivedData.mSendData.mAddress[i]; + if ((address != 0) && + (length <= 9018) && + (length > 14)) + { + struct sk_buff *skb = alloc_skb(event->mDerivedData.mSendData.mLength[i], GFP_ATOMIC); + remoteList.addr.token1.token2 = address; + remoteList.size = length; + if (skb != NULL) + { + HvLpDma_Rc returnCode = HvLpDma_Rc_Good; + int numPages = number_of_pages((skb->data), length); + + + myBufList[0].addr.address = (0x8000000000000000LL | (VIRT_TO_ABSOLUTE((unsigned long)skb->data))); + myBufList[0].size = (numPages > 1) ? (4096 - page_offset(skb->data)) : length; + + if (numPages > 1) + { + myBufList[1].addr.address = (0x8000000000000000LL | (VIRT_TO_ABSOLUTE((unsigned long) skb->data + myBufList[0].size))); + myBufList[1].size = (numPages > 2) ? (4096 - page_offset(skb->data)) : length - myBufList[0].size; + + if (numPages > 2) + { + myBufList[2].addr.address = (0x8000000000000000LL | (VIRT_TO_ABSOLUTE((unsigned long) skb->data + myBufList[0].size + myBufList[1].size))); + myBufList[2].size = (numPages > 3) ? (4096 - page_offset(skb->data)) : length - myBufList[1].size - myBufList[0].size; + + if (numPages > 3) + { + myBufList[3].addr.address = 0x8000000000000000LL | (VIRT_TO_ABSOLUTE((unsigned long) skb->data + myBufList[0].size + myBufList[1].size + myBufList[2].size)); + myBufList[3].size = (numPages > 4) ? (4096 - page_offset(skb->data)) : length - myBufList[2].size - myBufList[1].size - myBufList[0].size; + } + } + } + + returnCode = HvCallEvent_dmaBufList(HvLpEvent_Type_VirtualLan, + event->mBaseEvent.xSourceLp, + HvLpDma_Direction_RemoteToLocal, + connection->mSourceInst, + connection->mTargetInst, + HvLpDma_AddressType_RealAddress, + HvLpDma_AddressType_TceIndex, + 0x8000000000000000LL | (VIRT_TO_ABSOLUTE((unsigned long)&myBufList)), + 0x8000000000000000LL | (VIRT_TO_ABSOLUTE((unsigned long)&remoteList)), + length); + + if (returnCode == HvLpDma_Rc_Good) + { + HvLpVirtualLanIndex vlan = skb->data[9]; + u64 dest = *((u64 *)skb->data) & 0xFFFFFFFFFFFF0000; + + if (((vlan < HvMaxArchitectedVirtualLans) && + ((port = mFabricMgr->mPorts[vlan]) != NULL)) && + ((dest == port->mMyAddress) || /* it's for me */ + (dest == 0xFFFFFFFFFFFF0000) || /* it's a broadcast */ + (veth_multicast_wanted(port, dest)) || /* it's one of my multicasts */ + (port->mPromiscuous == 1))) /* I'm promiscuous */ + { + skb_put(skb, length); + skb->dev = port->mDev; + skb->protocol = eth_type_trans(skb, port->mDev); + skb->ip_summed = CHECKSUM_NONE; + netif_rx(skb); /* send it up */ + port->mStats.rx_packets++; + port->mStats.rx_bytes += length; + + } + else + { + dev_kfree_skb_irq(skb); + } + } + else + { + printk("bad lp event rc %x length %d remote address %x raw address %x\n", (int)returnCode, length, remoteList.addr.token1.token2, address); + dev_kfree_skb_irq(skb); + } + } + } + else + break; + } + /* Ack it */ + + { + unsigned long flags; + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks < VethMaxFramesMsgsAcked) + { + connection->mEventData.mAckData.mToken[connection->mNumAcks] = event->mBaseEvent.xCorrelationToken; + ++connection->mNumAcks; + + if (connection->mNumAcks == connection->mRemoteCap.mUnionData.mFields.mThreshold) + { + HvLpEvent_Rc rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%x) acking frames from lpar %d\n", (int)rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + } +} +#undef number_of_pages +#undef page_offset + +static void veth_timedAck(unsigned long connectionPtr) +{ + unsigned long flags; + HvLpEvent_Rc rc; + struct VethLpConnection *connection = (struct VethLpConnection *) connectionPtr; + /* Ack all the events */ + spin_lock_irqsave(&connection->mAckGate, flags); + + if (connection->mNumAcks > 0) + { + rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + veth_error_printk("Bad lp event return code(%x) acking frames from lpar %d!\n", (int)rc, connection->mRemoteLp); + } + + connection->mNumAcks = 0; + + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + } + + spin_unlock_irqrestore(&connection->mAckGate, flags); + + /* Reschedule the timer */ + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); +} + +static int veth_multicast_wanted(struct VethPort *port, u64 thatAddr) +{ + int returnParm = 0; + int i; + unsigned long flags; + + if ((*((char *)&thatAddr) & 0x01) != 1) + return 0; + + read_lock_irqsave(&port->mMcastGate, flags); + if (port->mAllMcast) + return 1; + + for (i=0; i < port->mNumAddrs; ++i) + { + u64 thisAddr = port->mMcasts[i]; + + if (thisAddr == thatAddr) + { + returnParm = 1; + break; + } + } + read_unlock_irqrestore(&port->mMcastGate, flags); + + return returnParm; +} + +static void veth_sendMonitor(struct VethLpConnection *connection) +{ + HvLpEvent_Rc returnCode = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeMonitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + connection->mSourceInst, + connection->mTargetInst, + 0, 0, 0, 0, 0, 0); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mSentMonitor = 1; + connection->mConnectionStatus.mFailed = 0; + + /* Start the ACK timer */ + init_timer(&connection->mAckTimer); + connection->mAckTimer.function = veth_timedAck; + connection->mAckTimer.data = (unsigned long) connection; + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); + + } + else + { + veth_error_printk("Monitor send to lpar %d failed with rc %x\n", connection->mRemoteLp, (int)returnCode); + veth_failMe(connection); + } +} + +static void veth_finishCapBh(struct VethLpConnection *connection) +{ + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); + veth_finishCapBhLocked(connection); + spin_unlock_irqrestore(&connection->mStatusGate, flags); +} + +static void veth_finishCapBhLocked(struct VethLpConnection *connection) +{ + struct VethLpEvent *event = &connection->mCapEvent; + struct VethCapData *remoteCap = &(connection->mRemoteCap); + int numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; + + /* Convert timer to jiffies */ + if (connection->mMyCap.mUnionData.mFields.mTimer) + connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000; + else + connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000; + + if (connection->mNumberLpAcksAlloced >= numAcks) + { + HvLpEvent_Rc returnCode = HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + + if (returnCode == HvLpEvent_Rc_Good) + { + connection->mConnectionStatus.mGotCapAcked = 1; + + if (connection->mConnectionStatus.mSentCap != 1) + { + connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(connection->mRemoteLp, HvLpEvent_Type_VirtualLan); + + veth_sendCap(connection); + } + else if (connection->mConnectionStatus.mCapAcked == 1) + { + if (connection->mConnectionStatus.mSentMonitor != 1) + veth_sendMonitor(connection); + } + } + else + { + veth_error_printk("Failed to ack remote cap for lpar %d with rc %x\n", connection->mRemoteLp, (int)returnCode); + veth_failMe(connection); + } + } + else + { + veth_error_printk("Couldn't allocate all the frames ack events for lpar %d\n", connection->mRemoteLp); + event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; + HvCallEvent_ackLpEvent((struct HvLpEvent *)event); + } +} + +int proc_veth_dump_connection +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + long whichConnection = (long) data; + int len = 0; + struct VethLpConnection *connection = NULL; + + if ((whichConnection < 0) || (whichConnection > HvMaxArchitectedLps) || (mFabricMgr == NULL)) + { + veth_error_printk("Got bad data from /proc file system\n"); + len = sprintf(page, "ERROR\n"); + } + else + { + int thereWasStuffBefore = 0; + connection = &(mFabricMgr->mConnection[whichConnection]); + + out += sprintf(out, "Remote Lp:\t%d\n", connection->mRemoteLp); + out += sprintf(out, "Source Inst:\t%04X\n", connection->mSourceInst); + out += sprintf(out, "Target Inst:\t%04X\n", connection->mTargetInst); + out += sprintf(out, "Num Msgs:\t%d\n", connection->mNumMsgs); + out += sprintf(out, "Num Lp Acks:\t%d\n", connection->mNumberLpAcksAlloced); + out += sprintf(out, "Num Acks:\t%d\n", connection->mNumAcks); + + if (connection->mConnectionStatus.mOpen) + { + out += sprintf(out, "mConnectionStatus.mCapMonAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapMonAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mBaseMsgsAlloced) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "BaseMsgsAlloced"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "CapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCap) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCap"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mGotCapAcked) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "GotCapAcked"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mSentMonitor) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "SentMonitor"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mPopulatedRings) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "PopulatedRings"); + thereWasStuffBefore = 1; + } + + if (connection->mConnectionStatus.mFailed) + { + if (thereWasStuffBefore) + out += sprintf(out,"/"); + else + out += sprintf(out,"<"); + out += sprintf(out, "Failed"); + thereWasStuffBefore = 1; + } + + if (thereWasStuffBefore) + out += sprintf(out, ">"); + + out += sprintf(out, "\n"); + + out += sprintf(out, "Capabilities (System:):\n"); + out += sprintf(out, "\tLocal:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mMyCap.mUnionData.mFields.mVersion, + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + connection->mMyCap.mUnionData.mFields.mThreshold, + connection->mMyCap.mUnionData.mFields.mTimer); + out += sprintf(out, "\tRemote:<"); + out += sprintf(out, "%d/%d/%d/%d>\n", + connection->mRemoteCap.mUnionData.mFields.mVersion, + connection->mRemoteCap.mUnionData.mFields.mNumberBuffers, + connection->mRemoteCap.mUnionData.mFields.mThreshold, + connection->mRemoteCap.mUnionData.mFields.mTimer); + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_veth_dump_port +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + long whichPort = (long) data; + int len = 0; + struct VethPort *port = NULL; + + if ((whichPort < 0) || (whichPort > HvMaxArchitectedVirtualLans) || (mFabricMgr == NULL)) + len = sprintf(page, "Virtual ethernet is not configured.\n"); + else + { + int i=0; + u32 *myAddr; + u16 *myEndAddr; + port = mFabricMgr->mPorts[whichPort]; + + if (port != NULL) + { + myAddr = (u32 *)&(port->mMyAddress); + myEndAddr = (u16 *)(myAddr + 1); + out += sprintf(out, "Net device:\t%p\n", port->mDev); + out += sprintf(out, "Address:\t%08X%04X\n", myAddr[0], myEndAddr[0]); + out += sprintf(out, "Promiscuous:\t%d\n", port->mPromiscuous); + out += sprintf(out, "All multicast:\t%d\n", port->mAllMcast); + out += sprintf(out, "Number multicast:\t%d\n", port->mNumAddrs); + + for (i=0; i < port->mNumAddrs; ++i) + { + u32 *multi = (u32 *)&(port->mMcasts[i]); + u16 *multiEnd = (u16 *)(multi + 1); + out += sprintf(out, " %08X%04X\n", multi[0], multiEnd[0]); + } + } + else + { + out += sprintf(page, "veth%d is not configured.\n", (int)whichPort); + } + + len = out - page; + } + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + + diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/veth.h linuxppc64_2_4/drivers/iseries/veth.h --- linux-2.4.9-ac10/drivers/iseries/veth.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/veth.h Thu Sep 27 14:00:26 2001 @@ -0,0 +1,255 @@ +/* File veth.h created by Kyle A. Lucke on Mon Aug 7 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_H +#define _VETH_H + +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPEVENT_H +#include +#endif +#include + +#define VethEventNumTypes (4) +#define VethEventTypeCap (0) +#define VethEventTypeFrames (1) +#define VethEventTypeMonitor (2) +#define VethEventTypeFramesAck (3) + +#define VethMaxFramesMsgsAcked (20) +#define VethMaxFramesMsgs (0xFFFF) +#define VethMaxFramesPerMsg (6) +#define VethAckTimeoutUsec (1000000) + +#define VETHSTACKTYPE(T) struct VethStack##T +#define VETHSTACK(T) \ +VETHSTACKTYPE(T) \ +{ \ +struct T *head; \ +spinlock_t lock; \ +} +#define VETHSTACKCTOR(s) do { (s)->head = NULL; spin_lock_init(&(s)->lock); } while(0) +#define VETHSTACKPUSH(s, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p)->next = (s)->head; \ +(s)->head = (p); \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHSTACKPOP(s,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(s)->lock,flags); \ +(p) = (s)->head; \ +if ((s)->head != NULL) \ +{ \ +(s)->head = (s)->head->next; \ +} \ +spin_unlock_irqrestore(&(s)->lock, flags); \ +} while(0) + +#define VETHQUEUE(T) \ +struct VethQueue##T \ +{ \ +T *head; \ +T *tail; \ +spinlock_t lock; \ +} +#define VETHQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; spin_lock_init(&(q)->lock); } while(0) +#define VETHQUEUEENQ(q, p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +#define VETHQUEUEDEQ(q,p) \ +do { \ +unsigned long flags; \ +spin_lock_irqsave(&(q)->lock,flags); \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +spin_unlock_irqrestore(&(q)->lock, flags); \ +} while(0) + +struct VethFramesData +{ + u32 mAddress[6]; + u16 mLength[6]; + u32 mEofMask; +}; + +struct VethFramesAckData +{ + u16 mToken[VethMaxFramesMsgsAcked]; +}; + +struct VethCapData +{ + union + { + struct Fields + { + u8 mVersion; + u8 mReserved1; + u16 mNumberBuffers; + u16 mThreshold; + u16 mReserved2; + u32 mTimer; + u32 mReserved3; + u64 mReserved4; + u64 mReserved5; + u64 mReserved6; + } mFields; + struct NoFields + { + u64 mReserved1; + u64 mReserved2; + u64 mReserved3; + u64 mReserved4; + u64 mReserved5; + } mNoFields; + } mUnionData; +}; + +struct VethFastPathData +{ + u64 mData1; + u64 mData2; + u64 mData3; + u64 mData4; + u64 mData5; +}; + +struct VethLpEvent +{ + struct HvLpEvent mBaseEvent; + union { + struct VethFramesData mSendData; + struct VethCapData mCapabilitiesData; + struct VethFramesAckData mFramesAckData; + struct VethFastPathData mFastPathData; + } mDerivedData; + +}; + +struct VethMsg +{ + struct VethMsg *next; + union { + struct VethFramesData mSendData; + struct VethFastPathData mFpData; + } mEvent; + int mIndex; + unsigned long mInUse; + struct sk_buff *mSkb; +}; + + +struct VethControlBlock +{ + struct net_device *mDev; + struct VethControlBlock *mNext; + HvLpVirtualLanIndex mVlanId; +}; + +struct VethLpConnection +{ + u64 mEyecatcher; + HvLpIndex mRemoteLp; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + u32 mNumMsgs; + struct VethMsg *mMsgs; + int mNumberRcvMsgs; + int mNumberLpAcksAlloced; + union + { + struct VethFramesAckData mAckData; + struct VethFastPathData mFpData; + } mEventData; + spinlock_t mAckGate; + u32 mNumAcks; + spinlock_t mStatusGate; + struct + { + u64 mOpen : 1; + u64 mCapMonAlloced : 1; + u64 mBaseMsgsAlloced : 1; + u64 mSentCap : 1; + u64 mCapAcked : 1; + u64 mGotCap : 1; + u64 mGotCapAcked : 1; + u64 mSentMonitor : 1; + u64 mPopulatedRings : 1; + u64 mReserved : 54; + u64 mFailed : 1; + } mConnectionStatus; + struct VethCapData mMyCap; + struct VethCapData mRemoteCap; + unsigned long mCapAckBhPending; + struct tq_struct mCapAckBhTq; + struct VethLpEvent mCapAckEvent; + unsigned long mCapBhPending; + struct tq_struct mCapBhTq; + struct VethLpEvent mCapEvent; + unsigned long mMonitorAckBhPending; + struct tq_struct mMonitorAckBhTq; + struct VethLpEvent mMonitorAckEvent; + unsigned long mAllocBhPending; + struct tq_struct mAllocBhTq; + int mNumberAllocated; + struct timer_list mAckTimer; + u32 mTimeout; + VETHSTACK(VethMsg) mMsgStack; +}; +#define HVMAXARCHITECTEDVIRTUALLANS 16 +struct VethPort +{ + struct net_device *mDev; + struct net_device_stats mStats; + int mLock; + u64 mMyAddress; + int mPromiscuous; + int mAllMcast; + rwlock_t mMcastGate; + int mNumAddrs; + u64 mMcasts[12]; +}; + +struct VethFabricMgr +{ + u64 mEyecatcher; + HvLpIndex mThisLp; + struct VethLpConnection mConnection[HVMAXARCHITECTEDLPS]; + spinlock_t mPortListGate; + u64 mNumPorts; + struct VethPort *mPorts[HVMAXARCHITECTEDVIRTUALLANS]; +}; + +int proc_veth_dump_connection(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_veth_dump_port(char *page, char **start, off_t off, int count, int *eof, void *data); + +#endif /* _VETH_H */ diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/vio.h linuxppc64_2_4/drivers/iseries/vio.h --- linux-2.4.9-ac10/drivers/iseries/vio.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/vio.h Thu Sep 27 14:00:26 2001 @@ -0,0 +1,115 @@ +/* -*- linux-c -*- + * drivers/char/vio.h + * + * iSeries Virtual I/O Message Path header + * + * Authors: Dave Boutcher + * Ryan Arnold + * Colin Devilbiss + * + * (C) Copyright 2000 IBM Corporation + * + * This header file is used by the iSeries virtual I/O device + * drivers. It defines the interfaces to the common functions + * (implemented in drivers/char/viopath.h) as well as defining + * common functions and structures. Currently (at the time I + * wrote this comment) the iSeries virtual I/O device drivers + * that use this are + * drivers/block/viodasd.c + * drivers/char/viocons.c + * drivers/char/viotape.c + * drivers/cdrom/viocd.c + * + * The iSeries virtual ethernet support (veth.c) uses a whole + * different set of functions. + * + * 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) anyu 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 _VIO_H +#define _VIO_H + +#include +#include + +/* iSeries virtual I/O events use the subtype field in + * HvLpEvent to figure out what kind of vio event is coming + * in. We use a table to route these, and this defines + * the maximum number of distinct subtypes + */ +#define VIO_MAX_SUBTYPES 7 + +/* Each subtype can register a handler to process their events. + * The handler must have this interface. + */ +typedef void (vio_event_handler_t) (struct HvLpEvent * event); + +int viopath_open(HvLpIndex remoteLp, int subtype, int numReq); +int viopath_close(HvLpIndex remoteLp, int subtype, int numReq); +int vio_setHandler(int subtype, vio_event_handler_t * beh); +int vio_clearHandler(int subtype); +int viopath_isactive(HvLpIndex lp); +HvLpInstanceId viopath_sourceinst(HvLpIndex lp); +HvLpInstanceId viopath_targetinst(HvLpIndex lp); +void vio_set_hostlp(void); +void *vio_get_event_buffer(int subtype); +void vio_free_event_buffer(int subtype, void *buffer); + +extern HvLpIndex viopath_hostLp; + +#define VIO_MESSAGE "iSeries virtual I/O: " +#define KERN_DEBUG_VIO KERN_DEBUG VIO_MESSAGE +#define KERN_INFO_VIO KERN_INFO VIO_MESSAGE +#define KERN_WARNING_VIO KERN_WARNING VIO_MESSAGE + +#define VIOCHAR_MAX_DATA 200 + +#define VIOMAJOR_SUBTYPE_MASK 0xff00 +#define VIOMINOR_SUBTYPE_MASK 0x00ff +#define VIOMAJOR_SUBTYPE_SHIFT 8 + +#define VIOVERSION 0x0101 + +enum viosubtypes { + viomajorsubtype_monitor = 0x0100, + viomajorsubtype_blockio = 0x0200, + viomajorsubtype_chario = 0x0300, + viomajorsubtype_config = 0x0400, + viomajorsubtype_cdio = 0x0500, + viomajorsubtype_tape = 0x0600 +}; + + +enum vioconfigsubtype { + vioconfigget = 0x0001, +}; + +enum viorc { + viorc_good = 0x0000, + viorc_noConnection = 0x0001, + viorc_noReceiver = 0x0002, + viorc_noBufferAvailable = 0x0003, + viorc_invalidMessageType = 0x0004, + viorc_invalidRange = 0x0201, + viorc_invalidToken = 0x0202, + viorc_DMAError = 0x0203, + viorc_useError = 0x0204, + viorc_releaseError = 0x0205, + viorc_invalidDisk = 0x0206, + viorc_openRejected = 0x0301 +}; + + +#endif /* _VIO_H */ diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/viocd.c linuxppc64_2_4/drivers/iseries/viocd.c --- linux-2.4.9-ac10/drivers/iseries/viocd.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/viocd.c Thu Sep 27 14:00:26 2001 @@ -0,0 +1,775 @@ +/* -*- linux-c -*- + * drivers/cdrom/viocd.c + * + *************************************************************************** + * iSeries Virtual CD Rom + * + * Authors: Dave Boutcher + * Ryan Arnold + * Colin Devilbiss + * + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu 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 + *************************************************************************** + * This routine provides access to CD ROM drives owned and managed by an + * OS/400 partition running on the same box as this Linux partition. + * + * All operations are performed by sending messages back and forth to + * the OS/400 partition. + * + * + * This device driver can either use it's own major number, or it can + * pretend to be an AZTECH drive. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new CD major number... + * or you can call this a really ugly hack. Your choice. + * + */ + +#include +#include + +/*************************************************************************** + * Decide if we are using our own major or pretending to be an AZTECH drive + ***************************************************************************/ +#ifdef CONFIG_VIOCD_AZTECH +#define MAJOR_NR AZTECH_CDROM_MAJOR +#define do_viocd_request do_aztcd_request +#else +#define MAJOR_NR VIOCD_MAJOR +#endif + +#define VIOCD_VERS "1.04" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "vio.h" +#include + +extern struct pci_dev * iSeries_vio_dev; + +#define signalLpEvent HvCallEvent_signalLpEventFast + +struct viocdlpevent { + struct HvLpEvent event; + u32 mReserved1; + u16 mVersion; + u16 mSubTypeRc; + u16 mDisk; + u16 mFlags; + u32 mToken; + u64 mOffset; // On open, the max number of disks + u64 mLen; // On open, the size of the disk + u32 mBlockSize; // Only set on open + u32 mMediaSize; // Only set on open +}; + +enum viocdsubtype { + viocdopen = 0x0001, + viocdclose = 0x0002, + viocdread = 0x0003, + viocdwrite = 0x0004, + viocdlockdoor = 0x0005, + viocdgetinfo = 0x0006, + viocdcheck = 0x0007 +}; + +/* Should probably make this a module parameter....sigh + */ +#define VIOCD_MAX_CD 8 +int viocd_blocksizes[VIOCD_MAX_CD]; +static u64 viocd_size_in_bytes[VIOCD_MAX_CD]; + +/* This is the structure we use to exchange info between driver and interrupt + * handler + */ +struct viocd_waitevent { + struct semaphore *sem; + int rc; + int changed; +}; + +/* this is a lookup table for the true capabilities of a device */ +struct capability_entry { + char *type; + int capability; +}; + +static struct capability_entry capability_table[] = { + { "6330", CDC_LOCK | CDC_DVD_RAM }, + { "6321", CDC_LOCK }, + { "632B", 0 }, + { NULL , CDC_LOCK }, +}; + +/* These are our internal structures for keeping track of devices + */ +static int viocd_numdev; + +struct cdrom_info { + char rsrcname[10]; + char type[4]; + char model[3]; +}; +static struct cdrom_info *viocd_unitinfo = NULL; + +struct disk_info{ + u32 useCount; + u32 blocksize; + u32 mediasize; +}; +static struct disk_info viocd_diskinfo[VIOCD_MAX_CD]; + +static struct cdrom_device_info viocd_info[VIOCD_MAX_CD]; + +static spinlock_t viocd_lock = SPIN_LOCK_UNLOCKED; + +#define MAX_CD_REQ 1 +static LIST_HEAD(reqlist); + +/* End a request + */ +static int viocd_end_request(struct request *req, int uptodate) +{ + if (end_that_request_first(req, uptodate, DEVICE_NAME)) + return 0; + end_that_request_last(req); + return 1; +} + + +/* Get info on CD devices from OS/400 + */ +static void get_viocd_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + DECLARE_MUTEX_LOCKED(Semaphore); + struct viocd_waitevent we; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid) + return; + + if (viocd_unitinfo == NULL) + viocd_unitinfo = + kmalloc(sizeof(struct cdrom_info) * VIOCD_MAX_CD, + GFP_KERNEL); + + memset(viocd_unitinfo, 0x00, + sizeof(struct cdrom_info) * VIOCD_MAX_CD); + + dmaaddr = pci_map_single(iSeries_vio_dev, viocd_unitinfo, + sizeof(struct cdrom_info) * VIOCD_MAX_CD, + PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) { + printk(KERN_WARNING_VIO "error allocating tce\n"); + return; + } + + we.sem = &Semaphore; + + hvrc = signalLpEvent(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdgetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64) (unsigned long) &we, + VIOVERSION << 16, + dmaaddr, + 0, + sizeof(struct cdrom_info) * VIOCD_MAX_CD, + 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk(KERN_WARNING_VIO "cdrom error sending event. rc %d\n", (int) hvrc); + return; + } + + down(&Semaphore); + + if (we.rc) { + printk(KERN_WARNING_VIO "bad rc %d on getinfo\n", we.rc); + return; + } + + + for (i = 0; (i < VIOCD_MAX_CD) && (viocd_unitinfo[i].rsrcname[0]); i++) { + viocd_numdev++; + } +} + +/* Open a device + */ +static int viocd_open(struct cdrom_device_info *cdi, int purpose) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + struct viocd_waitevent we; + struct disk_info *diskinfo = &viocd_diskinfo[device_no]; + + // If we don't have a host, bail out + if (viopath_hostLp == HvLpIndexInvalid || device_no >= viocd_numdev) + return -ENODEV; + + we.sem = &Semaphore; + hvrc = signalLpEvent(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64) (unsigned long) &we, + VIOVERSION << 16, + ((u64) device_no << 48), + 0, 0, 0); + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", + (int) hvrc); + return -EIO; + } + + down(&Semaphore); + + if (we.rc) + return -EIO; + + if (diskinfo->useCount == 0) { + if(diskinfo->blocksize > 0) { + viocd_blocksizes[device_no] = diskinfo->blocksize; + viocd_size_in_bytes[device_no] = diskinfo->blocksize * diskinfo->mediasize; + } else { + viocd_size_in_bytes[device_no] = 0xFFFFFFFFFFFFFFFF; + } + } + MOD_INC_USE_COUNT; + return 0; +} + +/* Release a device + */ +static void viocd_release(struct cdrom_device_info *cdi) +{ + int device_no = MINOR(cdi->dev); + HvLpEvent_Rc hvrc; + + /* If we don't have a host, bail out */ + if (viopath_hostLp == HvLpIndexInvalid + || device_no >= viocd_numdev) + return; + + hvrc = signalLpEvent(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64) device_no << 48), + 0, 0, 0); + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc); + return; + } + + MOD_DEC_USE_COUNT; +} + +/* Send a read or write request to OS/400 + */ +static int send_request(struct request *req) +{ + HvLpEvent_Rc hvrc; + dma_addr_t dmaaddr; + int device_no = DEVICE_NR(req->rq_dev); + u64 start = req->sector * 512, + len = req->current_nr_sectors * 512; + char reading = req->cmd == READ; + u16 command = reading ? viocdread : viocdwrite; + + + if(start + len > viocd_size_in_bytes[device_no]) { + printk(KERN_WARNING_VIO "viocd%d; access position %lx, past size %lx\n", + device_no, start + len, viocd_size_in_bytes[device_no]); + return -1; + } + + dmaaddr = pci_map_single(iSeries_vio_dev, req->buffer, len, + reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + if (dmaaddr == 0xFFFFFFFF) { + printk(KERN_WARNING_VIO "error allocating tce for address %p len %ld\n", + req->buffer, len); + return -1; + } + +/* FIXME: experimental +// note the special use of mReserved1 to indicate that a response +// is desired, but via NoAck... +// HvLpEvent_AckInd_NoAck, +// ((u64)0x00000001 << 32) | ((u64)VIOVERSION << 16), +*/ + + hvrc = signalLpEvent(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | command, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64) (unsigned long) req->buffer, + VIOVERSION << 16, + ((u64) device_no << 48) | dmaaddr, + start, len, 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk(KERN_WARNING_VIO "hv error on op %d\n", (int) hvrc); + return -1; + } + + return 0; +} + + +/* Do a request + */ +static int rwreq; +static void do_viocd_request(request_queue_t * q) +{ + for (;;) { + struct request *req; + char err_str[80] = ""; + int device_no; + + INIT_REQUEST; + if (rwreq >= MAX_CD_REQ) { + return; + } + + device_no = CURRENT_DEV; + + /* remove the current request from the queue */ + req = CURRENT; + blkdev_dequeue_request(req); + + /* check for any kind of error */ + if (device_no > viocd_numdev) + sprintf(err_str, "Invalid device number %d", device_no); + else if (send_request(req) < 0) + strcpy(err_str, "unable to send message to OS/400!"); + + /* if we had any sort of error, log it and cancel the request */ + if (*err_str) { + printk(KERN_WARNING_VIO "%s\n", err_str); + viocd_end_request(req, 0); + } else { + spin_lock(&viocd_lock); + list_add_tail(&req->queue, &reqlist); + ++rwreq; + spin_unlock(&viocd_lock); + } + } +} + +/* Check if the CD changed + */ +static int viocd_media_changed(struct cdrom_device_info *cdi, int disc_nr) +{ + struct viocd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = MINOR(cdi->dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) { + printk(KERN_WARNING_VIO "Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = signalLpEvent(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64) (unsigned long) &we, + VIOVERSION << 16, + ((u64) device_no << 48), + 0, 0, 0); + + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) { + printk(KERN_WARNING_VIO "bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +static int viocd_lock_door(struct cdrom_device_info *cdi, int locking) +{ + HvLpEvent_Rc hvrc; + u64 device_no = MINOR(cdi->dev); + /* NOTE: flags is 1 or 0 so it won't overwrite the device_no */ + u64 flags = !!locking; + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + struct viocd_waitevent we = { sem:&Semaphore }; + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) { + printk(KERN_WARNING_VIO "Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the lockdoor event to OS/400 */ + hvrc = signalLpEvent(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_cdio | viocdlockdoor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64) (unsigned long) &we, + VIOVERSION << 16, + (device_no << 48) | (flags << 32), + 0, 0, 0); + + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) { + return -EIO; + } + + return 0; +} + +/* This routine handles incoming CD LP events + */ +static void vioHandleCDEvent(struct HvLpEvent *event) +{ + struct viocdlpevent *bevent = (struct viocdlpevent *) event; + struct viocd_waitevent *pwe; + + if (event == NULL) { + /* Notification that a partition went away! */ + return; + } + /* First, we should NEVER get an int here...only acks */ + if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + printk(KERN_WARNING_VIO "Yikes! got an int in viocd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { + case viocdopen: + viocd_diskinfo[bevent->mDisk].blocksize = bevent->mBlockSize; + viocd_diskinfo[bevent->mDisk].mediasize = bevent->mMediaSize; + /* FALLTHROUGH !! */ + case viocdgetinfo: + case viocdlockdoor: + pwe = (struct viocd_waitevent *) (unsigned long) event->xCorrelationToken; + pwe->rc = event->xRc; + up(pwe->sem); + break; + + case viocdclose: + break; + + case viocdwrite: + case viocdread:{ + unsigned long flags; + int reading = ((event->xSubtype & VIOMINOR_SUBTYPE_MASK) == viocdread); + struct request *req = blkdev_entry_to_request(reqlist.next); + /* Since this is running in interrupt mode, we need to make sure we're not + * stepping on any global I/O operations + */ + spin_lock_irqsave(&io_request_lock, flags); + + pci_unmap_single(iSeries_vio_dev, + bevent->mToken, + bevent->mLen, + reading ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + + /* find the event to which this is a response */ + while ((&req->queue != &reqlist) && + ((u64) (unsigned long) req->buffer != bevent->event.xCorrelationToken)) + req = blkdev_entry_to_request(req->queue.next); + + /* if the event was not there, then what are we responding to?? */ + if (&req->queue == &reqlist) { + printk(KERN_WARNING_VIO "Yikes! we didn't ever enqueue this guy!\n"); + spin_unlock_irqrestore(&io_request_lock, + flags); + break; + } + + /* we don't need to keep it around anymore... */ + spin_lock(&viocd_lock); + list_del(&req->queue); + --rwreq; + spin_unlock(&viocd_lock); + { + char stat = event->xRc == HvLpEvent_Rc_Good; + int nsect = bevent->mLen >> 9; + + if (!stat) + printk(KERN_WARNING_VIO + "request %p failed with rc %d:0x%08x\n", + req->buffer, event->xRc, bevent->mSubTypeRc); + while ((nsect > 0) && (req->bh)) { + nsect -= req->current_nr_sectors; + viocd_end_request(req, stat); + } + /* we weren't done yet */ + if (req->bh) { + if (send_request(req) < 0) { + printk(KERN_WARNING_VIO + "couldn't re-submit req %p\n", req->buffer); + viocd_end_request(req, 0); + } else { + spin_lock(&viocd_lock); + list_add_tail(&req->queue, &reqlist); + ++rwreq; + spin_unlock(&viocd_lock); + } + } + } + + /* restart handling of incoming requests */ + do_viocd_request(NULL); + spin_unlock_irqrestore(&io_request_lock, flags); + break; + } + case viocdcheck: + pwe = (struct viocd_waitevent *) (unsigned long) event->xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->mFlags; + up(pwe->sem); + break; + + default: + printk(KERN_WARNING_VIO "invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/* Our file operations table + */ +static struct cdrom_device_ops viocd_dops = { + open:viocd_open, + release:viocd_release, + media_changed:viocd_media_changed, + lock_door:viocd_lock_door, + capability:CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED | CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN | CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_GENERIC_PACKET | CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | CDC_DVD_RAM +}; + +/* Handle reads from the proc file system + */ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + + for (i = 0; i < viocd_numdev; i++) { + len += + sprintf(buf + len, + "viocd device %d is iSeries resource %10.10s type %4.4s, model %3.3s\n", + i, viocd_unitinfo[i].rsrcname, + viocd_unitinfo[i].type, + viocd_unitinfo[i].model); + } + *eof = 1; + return len; +} + + +/* setup our proc file system entries + */ +void viocd_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("viocd", S_IFREG | S_IRUSR, iSeries_proc); + if (!ent) + return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; +} + +/* clean up our proc file system entries + */ +void viocd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocd", iSeries_proc); +} + +static int find_capability(const char *type) +{ + struct capability_entry *entry; + for(entry = capability_table; entry->type; ++entry) + if(!strncmp(entry->type, type, 4)) + break; + return entry->capability; +} + +/* Initialize the whole device driver. Handle module and non-module + * versions + */ +__init int viocd_init(void) +{ + int i, rc; + + if (viopath_hostLp == HvLpIndexInvalid) + vio_set_hostlp(); + + /* If we don't have a host, bail out */ + if (viopath_hostLp == HvLpIndexInvalid) + return -ENODEV; + + rc = viopath_open(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2); + if (rc) { + printk(KERN_WARNING_VIO "error opening path to host partition %d\n", + viopath_hostLp); + return rc; + } + + /* Initialize our request handler + */ + rwreq = 0; + vio_setHandler(viomajorsubtype_cdio, vioHandleCDEvent); + + memset(&viocd_diskinfo, 0x00, sizeof(viocd_diskinfo)); + + get_viocd_info(); + + if (viocd_numdev == 0) { + vio_clearHandler(viomajorsubtype_cdio); + viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2); + return 0; + } + + printk(KERN_INFO_VIO + "%s: iSeries Virtual CD vers %s, major %d, max disks %d, hosting partition %d\n", + DEVICE_NAME, VIOCD_VERS, MAJOR_NR, VIOCD_MAX_CD, viopath_hostLp); + + if (devfs_register_blkdev(MAJOR_NR, "viocd", &cdrom_fops) != 0) { + printk(KERN_WARNING_VIO "Unable to get major %d for viocd CD-ROM\n", MAJOR_NR); + return -EIO; + } + + blksize_size[MAJOR_NR] = viocd_blocksizes; + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + read_ahead[MAJOR_NR] = 4; + + memset(&viocd_info, 0x00, sizeof(viocd_info)); + for (i = 0; i < viocd_numdev; i++) { + viocd_info[i].dev = MKDEV(MAJOR_NR, i); + viocd_info[i].ops = &viocd_dops; + viocd_info[i].speed = 4; + viocd_info[i].capacity = 1; + viocd_info[i].mask = ~find_capability(viocd_unitinfo[i].type); + sprintf(viocd_info[i].name, "viocd%d", i); + if (register_cdrom(&viocd_info[i]) != 0) { + printk(KERN_WARNING_VIO "Cannot register viocd CD-ROM %s!\n", viocd_info[i].name); + } else { + printk(KERN_INFO_VIO + "cd %s is iSeries resource %10.10s type %4.4s, model %3.3s\n", + viocd_info[i].name, + viocd_unitinfo[i].rsrcname, + viocd_unitinfo[i].type, + viocd_unitinfo[i].model); + } + } + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viocd_proc_init); + + return 0; +} + +#ifdef MODULE +void viocd_exit(void) +{ + int i; + for (i = 0; i < viocd_numdev; i++) { + if (unregister_cdrom(&viocd_info[i]) != 0) { + printk(KERN_WARNING_VIO "Cannot unregister viocd CD-ROM %s!\n", viocd_info[i].name); + } + } + if ((devfs_unregister_blkdev(MAJOR_NR, "viocd") == -EINVAL)) { + printk(KERN_WARNING_VIO "can't unregister viocd\n"); + return; + } + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + if (viocd_unitinfo) + kfree(viocd_unitinfo); + + iSeries_proc_callback(&viocd_proc_delete); + + viopath_close(viopath_hostLp, viomajorsubtype_cdio, MAX_CD_REQ+2); + vio_clearHandler(viomajorsubtype_cd); +} +#endif + +#ifdef MODULE +module_init(viocd_init); +module_exit(viocd_exit); +#endif diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/viocons.c linuxppc64_2_4/drivers/iseries/viocons.c --- linux-2.4.9-ac10/drivers/iseries/viocons.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/viocons.c Thu Sep 27 14:00:26 2001 @@ -0,0 +1,1403 @@ +/* -*- linux-c -*- + * drivers/char/viocons.c + * + * iSeries Virtual Terminal + * + * Authors: Dave Boutcher + * Ryan Arnold + * Colin Devilbiss + * + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vio.h" + +#include +#include "asm/iSeries/HvCallEvent.h" +#include "asm/iSeries/HvLpConfig.h" +#include "asm/iSeries/HvCall.h" +#include + +/* Check that the tty_driver_data actually points to our stuff + */ +#define VIOTTY_PARANOIA_CHECK 1 +#define VIOTTY_MAGIC (0x0DCB) + +static int debug; + +static DECLARE_WAIT_QUEUE_HEAD(viocons_wait_queue); + +static int viotty_major = 229; + +#define VTTY_PORTS 10 +#define VIOTTY_SERIAL_START 65 + +static u64 sndMsgSeq[VTTY_PORTS]; +static u64 sndMsgAck[VTTY_PORTS]; + +static spinlock_t consolelock = SPIN_LOCK_UNLOCKED; + +/* THe structure of the events that flow between us and OS/400. You can't + * mess with this unless the OS/400 side changes too + */ +struct viocharlpevent { + struct HvLpEvent event; + u32 mReserved1; + u16 mVersion; + u16 mSubTypeRc; + u8 virtualDevice; + u8 immediateDataLen; + u8 immediateData[VIOCHAR_MAX_DATA]; +}; + +#define viochar_window (10) +#define viochar_highwatermark (3) + +enum viocharsubtype { + viocharopen = 0x0001, + viocharclose = 0x0002, + viochardata = 0x0003, + viocharack = 0x0004, + viocharconfig = 0x0005 +}; + +enum viochar_rc { + viochar_rc_ebusy = 1 +}; + +/* When we get writes faster than we can send it to the partition, + * buffer the data here. There is one set of buffers for each virtual + * port. + * Note that bufferUsed is a bit map of used buffers. + * It had better have enough bits to hold NUM_BUF + * the bitops assume it is a multiple of unsigned long + */ +#define NUM_BUF (8) +#define OVERFLOW_SIZE VIOCHAR_MAX_DATA + +static struct overflowBuffers { + unsigned long bufferUsed; + u8 *buffer[NUM_BUF]; + int bufferBytes[NUM_BUF]; + int curbuf; + int bufferOverflow; + int overflowMessage; +} overflow[VTTY_PORTS]; + +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp); + +static struct tty_driver viotty_driver; +static struct tty_driver viottyS_driver; +static int viotty_refcount; + +static struct tty_struct *viotty_table[VTTY_PORTS]; +static struct tty_struct *viottyS_table[VTTY_PORTS]; +static struct termios *viotty_termios[VTTY_PORTS]; +static struct termios *viottyS_termios[VTTY_PORTS]; +static struct termios *viotty_termios_locked[VTTY_PORTS]; +static struct termios *viottyS_termios_locked[VTTY_PORTS]; + +void hvlog(char *fmt, ...) +{ + int i; + static char buf[256]; + va_list args; + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + HvCall_writeLogBuffer(buf, i); + HvCall_writeLogBuffer("\r", 1); + +} + +/* Our port information. We store a pointer to one entry in the + * tty_driver_data + */ +static struct port_info_tag { + int magic; + struct tty_struct *tty; + HvLpIndex lp; + u8 vcons; + u8 port; +} port_info[VTTY_PORTS]; + +/* Make sure we're pointing to a valid port_info structure. Shamelessly + * plagerized from serial.c + */ +static inline int viotty_paranoia_check(struct port_info_tag *pi, + kdev_t device, const char *routine) +{ +#ifdef VIOTTY_PARANOIA_CHECK + static const char *badmagic = + "%s Warning: bad magic number for port_info struct (%s) in %s\n"; + static const char *badinfo = + "%s Warning: null port_info for (%s) in %s\n"; + + if (!pi) { + printk(badinfo, KERN_WARNING_VIO, kdevname(device), + routine); + return 1; + } + if (pi->magic != VIOTTY_MAGIC) { + printk(badmagic, KERN_WARNING_VIO, kdevname(device), + routine); + return 1; + } +#endif + return 0; +} + +/* + * Handle reads from the proc file system. Right now we just dump the + * state of the first TTY + */ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + struct tty_struct *tty = viotty_table[0]; + struct termios *termios; + if (tty == NULL) { + len += sprintf(buf + len, "no tty\n"); + *eof = 1; + return len; + } + + len += + sprintf(buf + len, + "tty info: COOK_OUT %ld COOK_IN %ld, NO_WRITE_SPLIT %ld\n", + tty->flags & TTY_HW_COOK_OUT, + tty->flags & TTY_HW_COOK_IN, + tty->flags & TTY_NO_WRITE_SPLIT); + + termios = tty->termios; + if (termios == NULL) { + len += sprintf(buf + len, "no termios\n"); + *eof = 1; + return len; + } + len += sprintf(buf + len, "INTR_CHAR %2.2x\n", INTR_CHAR(tty)); + len += sprintf(buf + len, "QUIT_CHAR %2.2x\n", QUIT_CHAR(tty)); + len += + sprintf(buf + len, "ERASE_CHAR %2.2x\n", ERASE_CHAR(tty)); + len += sprintf(buf + len, "KILL_CHAR %2.2x\n", KILL_CHAR(tty)); + len += sprintf(buf + len, "EOF_CHAR %2.2x\n", EOF_CHAR(tty)); + len += sprintf(buf + len, "TIME_CHAR %2.2x\n", TIME_CHAR(tty)); + len += sprintf(buf + len, "MIN_CHAR %2.2x\n", MIN_CHAR(tty)); + len += sprintf(buf + len, "SWTC_CHAR %2.2x\n", SWTC_CHAR(tty)); + len += + sprintf(buf + len, "START_CHAR %2.2x\n", START_CHAR(tty)); + len += sprintf(buf + len, "STOP_CHAR %2.2x\n", STOP_CHAR(tty)); + len += sprintf(buf + len, "SUSP_CHAR %2.2x\n", SUSP_CHAR(tty)); + len += sprintf(buf + len, "EOL_CHAR %2.2x\n", EOL_CHAR(tty)); + len += + sprintf(buf + len, "REPRINT_CHAR %2.2x\n", REPRINT_CHAR(tty)); + len += + sprintf(buf + len, "DISCARD_CHAR %2.2x\n", DISCARD_CHAR(tty)); + len += + sprintf(buf + len, "WERASE_CHAR %2.2x\n", WERASE_CHAR(tty)); + len += + sprintf(buf + len, "LNEXT_CHAR %2.2x\n", LNEXT_CHAR(tty)); + len += sprintf(buf + len, "EOL2_CHAR %2.2x\n", EOL2_CHAR(tty)); + + len += sprintf(buf + len, "I_IGNBRK %4.4x\n", I_IGNBRK(tty)); + len += sprintf(buf + len, "I_BRKINT %4.4x\n", I_BRKINT(tty)); + len += sprintf(buf + len, "I_IGNPAR %4.4x\n", I_IGNPAR(tty)); + len += sprintf(buf + len, "I_PARMRK %4.4x\n", I_PARMRK(tty)); + len += sprintf(buf + len, "I_INPCK %4.4x\n", I_INPCK(tty)); + len += sprintf(buf + len, "I_ISTRIP %4.4x\n", I_ISTRIP(tty)); + len += sprintf(buf + len, "I_INLCR %4.4x\n", I_INLCR(tty)); + len += sprintf(buf + len, "I_IGNCR %4.4x\n", I_IGNCR(tty)); + len += sprintf(buf + len, "I_ICRNL %4.4x\n", I_ICRNL(tty)); + len += sprintf(buf + len, "I_IUCLC %4.4x\n", I_IUCLC(tty)); + len += sprintf(buf + len, "I_IXON %4.4x\n", I_IXON(tty)); + len += sprintf(buf + len, "I_IXANY %4.4x\n", I_IXANY(tty)); + len += sprintf(buf + len, "I_IXOFF %4.4x\n", I_IXOFF(tty)); + len += sprintf(buf + len, "I_IMAXBEL %4.4x\n", I_IMAXBEL(tty)); + + len += sprintf(buf + len, "O_OPOST %4.4x\n", O_OPOST(tty)); + len += sprintf(buf + len, "O_OLCUC %4.4x\n", O_OLCUC(tty)); + len += sprintf(buf + len, "O_ONLCR %4.4x\n", O_ONLCR(tty)); + len += sprintf(buf + len, "O_OCRNL %4.4x\n", O_OCRNL(tty)); + len += sprintf(buf + len, "O_ONOCR %4.4x\n", O_ONOCR(tty)); + len += sprintf(buf + len, "O_ONLRET %4.4x\n", O_ONLRET(tty)); + len += sprintf(buf + len, "O_OFILL %4.4x\n", O_OFILL(tty)); + len += sprintf(buf + len, "O_OFDEL %4.4x\n", O_OFDEL(tty)); + len += sprintf(buf + len, "O_NLDLY %4.4x\n", O_NLDLY(tty)); + len += sprintf(buf + len, "O_CRDLY %4.4x\n", O_CRDLY(tty)); + len += sprintf(buf + len, "O_TABDLY %4.4x\n", O_TABDLY(tty)); + len += sprintf(buf + len, "O_BSDLY %4.4x\n", O_BSDLY(tty)); + len += sprintf(buf + len, "O_VTDLY %4.4x\n", O_VTDLY(tty)); + len += sprintf(buf + len, "O_FFDLY %4.4x\n", O_FFDLY(tty)); + + len += sprintf(buf + len, "C_BAUD %4.4x\n", C_BAUD(tty)); + len += sprintf(buf + len, "C_CSIZE %4.4x\n", C_CSIZE(tty)); + len += sprintf(buf + len, "C_CSTOPB %4.4x\n", C_CSTOPB(tty)); + len += sprintf(buf + len, "C_CREAD %4.4x\n", C_CREAD(tty)); + len += sprintf(buf + len, "C_PARENB %4.4x\n", C_PARENB(tty)); + len += sprintf(buf + len, "C_PARODD %4.4x\n", C_PARODD(tty)); + len += sprintf(buf + len, "C_HUPCL %4.4x\n", C_HUPCL(tty)); + len += sprintf(buf + len, "C_CLOCAL %4.4x\n", C_CLOCAL(tty)); + len += sprintf(buf + len, "C_CRTSCTS %4.4x\n", C_CRTSCTS(tty)); + + len += sprintf(buf + len, "L_ISIG %4.4x\n", L_ISIG(tty)); + len += sprintf(buf + len, "L_ICANON %4.4x\n", L_ICANON(tty)); + len += sprintf(buf + len, "L_XCASE %4.4x\n", L_XCASE(tty)); + len += sprintf(buf + len, "L_ECHO %4.4x\n", L_ECHO(tty)); + len += sprintf(buf + len, "L_ECHOE %4.4x\n", L_ECHOE(tty)); + len += sprintf(buf + len, "L_ECHOK %4.4x\n", L_ECHOK(tty)); + len += sprintf(buf + len, "L_ECHONL %4.4x\n", L_ECHONL(tty)); + len += sprintf(buf + len, "L_NOFLSH %4.4x\n", L_NOFLSH(tty)); + len += sprintf(buf + len, "L_TOSTOP %4.4x\n", L_TOSTOP(tty)); + len += sprintf(buf + len, "L_ECHOCTL %4.4x\n", L_ECHOCTL(tty)); + len += sprintf(buf + len, "L_ECHOPRT %4.4x\n", L_ECHOPRT(tty)); + len += sprintf(buf + len, "L_ECHOKE %4.4x\n", L_ECHOKE(tty)); + len += sprintf(buf + len, "L_FLUSHO %4.4x\n", L_FLUSHO(tty)); + len += sprintf(buf + len, "L_PENDIN %4.4x\n", L_PENDIN(tty)); + len += sprintf(buf + len, "L_IEXTEN %4.4x\n", L_IEXTEN(tty)); + + *eof = 1; + return len; +} + +/* + * Handle writes to our proc file system. Right now just turns on and off + * our debug flag + */ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + if (count) { + if (buffer[0] == '1') { + printk("viocons: debugging on\n"); + debug = 1; + } else { + printk("viocons: debugging off\n"); + debug = 0; + } + } + return count; +} + +/* + * setup our proc file system entries + */ +void viocons_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = + create_proc_entry("viocons", S_IFREG | S_IRUSR, iSeries_proc); + if (!ent) + return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/* + * clean up our proc file system entries + */ +void viocons_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viocons", iSeries_proc); +} + +/* + * Add data to our pending-send buffers. + * + * NOTE: Don't use printk in here because it gets nastily recursive. hvlog can be + * used to log to the hypervisor buffer + */ +static int bufferAdd(u8 port, const char *buf, size_t len, int userFlag) +{ + size_t bleft = len; + size_t curlen; + char *cbuf = (char *) buf; + int nextbuf; + struct overflowBuffers *pov = &overflow[port]; + while (bleft > 0) { + /* If there is no space left in the current buffer, we have + * filled everything up, so return. If we filled the previous + * buffer we would already have moved to the next one. + */ + if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) { + hvlog("buffer %d full. no more space\n", + pov->curbuf); + pov->bufferOverflow++; + pov->overflowMessage = 1; + return len - bleft; + } + + /* Turn on the "used" bit for this buffer. If it's already on, that's + * fine. + */ + set_bit(pov->curbuf, &pov->bufferUsed); + + /* + * See if this buffer has been allocated. If not, allocate it + */ + if (pov->buffer[pov->curbuf] == NULL) + pov->buffer[pov->curbuf] = + kmalloc(OVERFLOW_SIZE, GFP_ATOMIC); + + /* + * Figure out how much we can copy into this buffer + */ + if (bleft < + (OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf])) + curlen = bleft; + else + curlen = + OVERFLOW_SIZE - pov->bufferBytes[pov->curbuf]; + + /* + * Copy the data into the buffer + */ + if (userFlag) + copy_from_user(pov->buffer[pov->curbuf] + + pov->bufferBytes[pov->curbuf], cbuf, + curlen); + else + memcpy(pov->buffer[pov->curbuf] + + pov->bufferBytes[pov->curbuf], cbuf, + curlen); + + pov->bufferBytes[pov->curbuf] += curlen; + cbuf += curlen; + bleft -= curlen; + + /* + * Now see if we've filled this buffer + */ + if (pov->bufferBytes[pov->curbuf] == OVERFLOW_SIZE) { + nextbuf = (pov->curbuf + 1) % NUM_BUF; + + /* + * Move to the next buffer if it hasn't been used yet + */ + if (test_bit(nextbuf, &pov->bufferUsed) == 0) { + pov->curbuf = nextbuf; + } + } + } + return len; +} + +/* Send pending data + * + * NOTE: Don't use printk in here because it gets nastily recursive. hvlog can be + * used to log to the hypervisor buffer + */ +void sendBuffers(u8 port, HvLpIndex lp) +{ + HvLpEvent_Rc hvrc; + int nextbuf; + struct viocharlpevent *viochar; + unsigned long flags; + struct overflowBuffers *pov = &overflow[port]; + + spin_lock_irqsave(&consolelock, flags); + + viochar = (struct viocharlpevent *) + vio_get_event_buffer(viomajorsubtype_chario); + + /* Make sure we got a buffer + */ + if (viochar == NULL) { + hvlog("Yikes...can't get viochar buffer"); + spin_unlock_irqrestore(&consolelock, flags); + return; + } + + if (pov->bufferUsed == 0) { + hvlog("in sendbuffers, but no buffers used\n"); + vio_free_event_buffer(viomajorsubtype_chario, viochar); + spin_unlock_irqrestore(&consolelock, flags); + return; + } + + /* + * curbuf points to the buffer we're filling. We want to start sending AFTER + * this one. + */ + nextbuf = (pov->curbuf + 1) % NUM_BUF; + + /* + * Loop until we find a buffer with the bufferUsed bit on + */ + while (test_bit(nextbuf, &pov->bufferUsed) == 0) + nextbuf = (nextbuf + 1) % NUM_BUF; + + initDataEvent(viochar, lp); + + /* + * While we have buffers with data, and our send window is open, send them + */ + while ((test_bit(nextbuf, &pov->bufferUsed)) && + ((sndMsgSeq[port] - sndMsgAck[port]) < viochar_window)) { + viochar->immediateDataLen = pov->bufferBytes[nextbuf]; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + viochar->event.xSizeMinus1 = + offsetof(struct viocharlpevent, + immediateData) + viochar->immediateDataLen; + + memcpy(viochar->immediateData, pov->buffer[nextbuf], + viochar->immediateDataLen); + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) { + /* + * MUST unlock the spinlock before doing a printk + */ + vio_free_event_buffer(viomajorsubtype_chario, + viochar); + spin_unlock_irqrestore(&consolelock, flags); + + printk(KERN_WARNING_VIO + "console error sending event! return code %d\n", + (int) hvrc); + return; + } + + /* + * clear the bufferUsed bit, zero the number of bytes in this buffer, + * and move to the next buffer + */ + clear_bit(nextbuf, &pov->bufferUsed); + pov->bufferBytes[nextbuf] = 0; + nextbuf = (nextbuf + 1) % NUM_BUF; + } + + + /* + * If we have emptied all the buffers, start at 0 again. + * this will re-use any allocated buffers + */ + if (pov->bufferUsed == 0) { + pov->curbuf = 0; + + if (pov->overflowMessage) + pov->overflowMessage = 0; + + if (port_info[port].tty) { + if ((port_info[port].tty-> + flags & (1 << TTY_DO_WRITE_WAKEUP)) + && (port_info[port].tty->ldisc.write_wakeup)) + (port_info[port].tty->ldisc. + write_wakeup) (port_info[port].tty); + wake_up_interruptible(&port_info[port].tty-> + write_wait); + } + } + + vio_free_event_buffer(viomajorsubtype_chario, viochar); + spin_unlock_irqrestore(&consolelock, flags); + +} + +/* Our internal writer. Gets called both from the console device and + * the tty device. the tty pointer will be NULL if called from the console. + * + * NOTE: Don't use printk in here because it gets nastily recursive. hvlog can be + * used to log to the hypervisor buffer + */ +static int internal_write(struct tty_struct *tty, const char *buf, + size_t len, int userFlag) +{ + HvLpEvent_Rc hvrc; + size_t bleft = len; + size_t curlen; + const char *curbuf = buf; + struct viocharlpevent *viochar; + unsigned long flags; + struct port_info_tag *pi = NULL; + HvLpIndex lp; + u8 port; + + if (tty) { + pi = (struct port_info_tag *) tty->driver_data; + + if (!pi + || viotty_paranoia_check(pi, tty->device, + "viotty_internal_write")) + return -ENODEV; + + lp = pi->lp; + port = pi->port; + } else { + /* If this is the console device, use the lp from the first port entry + */ + port = 0; + lp = port_info[0].lp; + } + + /* Always put console output in the hypervisor console log + */ + if (port == 0) + HvCall_writeLogBuffer(buf, len); + + /* If the path to this LP is closed, don't bother doing anything more. + * just dump the data on the floor + */ + if (!viopath_isactive(lp)) + return len; + + /* + * If there is already data queued for this port, send it + */ + if (overflow[port].bufferUsed) + sendBuffers(port, lp); + + spin_lock_irqsave(&consolelock, flags); + + viochar = (struct viocharlpevent *) + vio_get_event_buffer(viomajorsubtype_chario); + /* Make sure we got a buffer + */ + if (viochar == NULL) { + hvlog("Yikes...can't get viochar buffer"); + spin_unlock_irqrestore(&consolelock, flags); + return -1; + } + + initDataEvent(viochar, lp); + + /* Got the lock, don't cause console output */ + while ((bleft > 0) && + (overflow[port].bufferUsed == 0) && + ((sndMsgSeq[port] - sndMsgAck[port]) < viochar_window)) { + if (bleft > VIOCHAR_MAX_DATA) + curlen = VIOCHAR_MAX_DATA; + else + curlen = bleft; + + viochar->immediateDataLen = curlen; + viochar->event.xCorrelationToken = sndMsgSeq[port]++; + + if (userFlag) + copy_from_user(viochar->immediateData, curbuf, + curlen); + else + memcpy(viochar->immediateData, curbuf, curlen); + + viochar->event.xSizeMinus1 = + offsetof(struct viocharlpevent, + immediateData) + curlen; + + hvrc = HvCallEvent_signalLpEvent(&viochar->event); + if (hvrc) { + /* + * MUST unlock the spinlock before doing a printk + */ + vio_free_event_buffer(viomajorsubtype_chario, + viochar); + spin_unlock_irqrestore(&consolelock, flags); + + hvlog("viocons: error sending event! %d\n", + (int) hvrc); + return len - bleft; + } + + curbuf += curlen; + bleft -= curlen; + } + + /* + * If we didn't send it all, buffer it + */ + if (bleft > 0) { + bleft -= bufferAdd(port, curbuf, bleft, userFlag); + } + vio_free_event_buffer(viomajorsubtype_chario, viochar); + spin_unlock_irqrestore(&consolelock, flags); + + return len - bleft; +} + +/* Initialize the common fields in a charLpEvent + */ +static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp) +{ + memset(viochar, 0x00, sizeof(struct viocharlpevent)); + + viochar->event.xFlags.xValid = 1; + viochar->event.xFlags.xFunction = HvLpEvent_Function_Int; + viochar->event.xFlags.xAckInd = HvLpEvent_AckInd_NoAck; + viochar->event.xFlags.xAckType = HvLpEvent_AckType_DeferredAck; + viochar->event.xType = HvLpEvent_Type_VirtualIo; + viochar->event.xSubtype = viomajorsubtype_chario | viochardata; + viochar->event.xSourceLp = HvLpConfig_getLpIndex(); + viochar->event.xTargetLp = lp; + viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent); + viochar->event.xSourceInstanceId = viopath_sourceinst(lp); + viochar->event.xTargetInstanceId = viopath_targetinst(lp); +} + + +/* console device write + */ +static void viocons_write(struct console *co, const char *s, + unsigned count) +{ + /* This parser will ensure that all single instances of either \n or \r are + * matched into carriage return/line feed combinations. It also allows for + * instances where there already exist \n\r combinations as well as the + * reverse, \r\n combinations. + */ + + int index; + char charptr[1]; + int foundcr; + int slicebegin; + int sliceend; + + foundcr = 0; + slicebegin = 0; + sliceend = 0; + + for (index = 0; index < count; index++) { + if (!foundcr && s[index] == 0x0a) { + if ((slicebegin - sliceend > 0) + && sliceend < count) { + internal_write(NULL, &s[slicebegin], + sliceend - slicebegin, 0); + slicebegin = sliceend; + } + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + if (foundcr && s[index] != 0x0a) { + if ((index - 2) >= 0) { + if (s[index - 2] != 0x0a) { + internal_write(NULL, + &s[slicebegin], + sliceend - + slicebegin, 0); + slicebegin = sliceend; + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, + 0); + } + } + } + sliceend++; + + if (s[index] == 0x0d) + foundcr = 1; + else + foundcr = 0; + } + + internal_write(NULL, &s[slicebegin], sliceend - slicebegin, 0); + + if (count > 1) { + if (foundcr == 1 && s[count - 1] != 0x0a) { + charptr[0] = '\n'; + internal_write(NULL, charptr, 1, 0); + } else if (s[count - 1] == 0x0a && s[count - 2] != 0x0d) { + + charptr[0] = '\r'; + internal_write(NULL, charptr, 1, 0); + } + } +} + +/* Work out a the device associate with this console + */ +static kdev_t viocons_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, c->index + viotty_driver.minor_start); +} + +/* console device read method + */ +static int viocons_read(struct console *co, const char *s, unsigned count) +{ + printk(KERN_DEBUG_VIO "viocons_read\n"); + // Implement me + interruptible_sleep_on(&viocons_wait_queue); + return 0; +} + +/* console device wait until a key is pressed + */ +static int viocons_wait_key(struct console *co) +{ + printk(KERN_DEBUG_VIO "In viocons_wait_key\n"); + // Implement me + interruptible_sleep_on(&viocons_wait_queue); + return 0; +} + +/* Do console device setup + */ +static int __init viocons_setup(struct console *co, char *options) +{ + return 0; +} + +/* console device I/O methods + */ +static struct console viocons = { + name:"ttyS", + write:viocons_write, + read:viocons_read, + device:viocons_device, + wait_key:viocons_wait_key, + setup:viocons_setup, + flags:CON_PRINTBUFFER, +}; + + +/* TTY Open method + */ +static int viotty_open(struct tty_struct *tty, struct file *filp) +{ + int port; + unsigned long flags; + MOD_INC_USE_COUNT; + port = MINOR(tty->device) - tty->driver.minor_start; + + if (port >= VIOTTY_SERIAL_START) + port -= VIOTTY_SERIAL_START; + + if ((port < 0) || (port >= VTTY_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + + spin_lock_irqsave(&consolelock, flags); + + /* + * If some other TTY is already connected here, reject the open + */ + if ((port_info[port].tty) && (port_info[port].tty != tty)) { + spin_unlock_irqrestore(&consolelock, flags); + MOD_DEC_USE_COUNT; + printk(KERN_WARNING_VIO + "console attempt to open device twice from different ttys\n"); + return -EBUSY; + } + tty->driver_data = &port_info[port]; + port_info[port].tty = tty; + spin_unlock_irqrestore(&consolelock, flags); + + return 0; +} + +/* TTY Close method + */ +static void viotty_close(struct tty_struct *tty, struct file *filp) +{ + unsigned long flags; + struct port_info_tag *pi = + (struct port_info_tag *) tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_close")) + return; + + spin_lock_irqsave(&consolelock, flags); + if (tty->count == 1) { + pi->tty = NULL; + } + + spin_unlock_irqrestore(&consolelock, flags); + + MOD_DEC_USE_COUNT; +} + +/* TTY Write method + */ +static int viotty_write(struct tty_struct *tty, int from_user, + const unsigned char *buf, int count) +{ + return internal_write(tty, buf, count, from_user); +} + +/* TTY put_char method + */ +static void viotty_put_char(struct tty_struct *tty, unsigned char ch) +{ + internal_write(tty, &ch, 1, 0); +} + +/* TTY flush_chars method + */ +static void viotty_flush_chars(struct tty_struct *tty) +{ +} + +/* TTY write_room method + */ +static int viotty_write_room(struct tty_struct *tty) +{ + int i; + int room = 0; + struct port_info_tag *pi = + (struct port_info_tag *) tty->driver_data; + + if (!pi + || viotty_paranoia_check(pi, tty->device, + "viotty_sendbuffers")) + return 0; + + // If no buffers are used, return the max size + if (overflow[pi->port].bufferUsed == 0) + return VIOCHAR_MAX_DATA * NUM_BUF; + + for (i = 0; ((i < NUM_BUF) && (room < VIOCHAR_MAX_DATA)); i++) { + room += + (OVERFLOW_SIZE - overflow[pi->port].bufferBytes[i]); + } + + if (room > VIOCHAR_MAX_DATA) + return VIOCHAR_MAX_DATA; + else + return room; +} + +/* TTY chars_in_buffer_room method + */ +static int viotty_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static void viotty_flush_buffer(struct tty_struct *tty) +{ +} + +static int viotty_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + /* the ioctls below read/set the flags usually shown in the leds */ + /* don't use them - they will go away without warning */ + case KDGETLED: + case KDGKBLED: + return put_user(0, (char *) arg); + + case KDSKBLED: + return 0; + } + + return n_tty_ioctl(tty, file, cmd, arg); +} + +static void viotty_throttle(struct tty_struct *tty) +{ +} + +static void viotty_unthrottle(struct tty_struct *tty) +{ +} + +static void viotty_set_termios(struct tty_struct *tty, + struct termios *old_termios) +{ +} + +static void viotty_stop(struct tty_struct *tty) +{ +} + +static void viotty_start(struct tty_struct *tty) +{ +} + +static void viotty_hangup(struct tty_struct *tty) +{ +} + +static void viotty_break(struct tty_struct *tty, int break_state) +{ +} + +static void viotty_send_xchar(struct tty_struct *tty, char ch) +{ +} + +static void viotty_wait_until_sent(struct tty_struct *tty, int timeout) +{ +} + +/* Handle an open charLpEvent. Could be either interrupt or ack + */ +static void vioHandleOpenEvent(struct HvLpEvent *event) +{ + unsigned long flags; + u8 eventRc; + u16 eventSubtypeRc; + struct viocharlpevent *cevent = (struct viocharlpevent *) event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Ack) { + if (port >= VTTY_PORTS) + return; + + spin_lock_irqsave(&consolelock, flags); + /* Got the lock, don't cause console output */ + + if (event->xRc == HvLpEvent_Rc_Good) { + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + port_info[port].lp = event->xTargetLp; + + spin_unlock_irqrestore(&consolelock, flags); + + if (event->xCorrelationToken != 0) { + unsigned long semptr = event->xCorrelationToken; + up((struct semaphore *) semptr); + } else + printk(KERN_WARNING_VIO + "console: wierd...got open ack without semaphore\n"); + } else { + /* This had better require an ack, otherwise complain + */ + if (event->xFlags.xAckInd != HvLpEvent_AckInd_DoAck) { + printk(KERN_WARNING_VIO + "console: viocharopen without ack bit!\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + /* Got the lock, don't cause console output */ + + /* Make sure this is a good virtual tty */ + if (port >= VTTY_PORTS) { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } + + /* If this is tty is already connected to a different + partition, fail */ + else if ((port_info[port].lp != HvLpIndexInvalid) && + (port_info[port].lp != event->xSourceLp)) { + eventRc = HvLpEvent_Rc_SubtypeError; + eventSubtypeRc = viorc_openRejected; + } else { + port_info[port].lp = event->xSourceLp; + eventRc = HvLpEvent_Rc_Good; + eventSubtypeRc = viorc_good; + sndMsgSeq[port] = sndMsgAck[port] = 0; + } + + spin_unlock_irqrestore(&consolelock, flags); + + /* Return the acknowledgement */ + HvCallEvent_ackLpEvent(event); + } +} + +/* Handle a close open charLpEvent. Could be either interrupt or ack + */ +static void vioHandleCloseEvent(struct HvLpEvent *event) +{ + unsigned long flags; + struct viocharlpevent *cevent = (struct viocharlpevent *) event; + u8 port = cevent->virtualDevice; + + if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + if (port >= VTTY_PORTS) + return; + + /* For closes, just mark the console partition invalid */ + spin_lock_irqsave(&consolelock, flags); + /* Got the lock, don't cause console output */ + + if (port_info[port].lp == event->xSourceLp) + port_info[port].lp = HvLpIndexInvalid; + + spin_unlock_irqrestore(&consolelock, flags); + printk(KERN_INFO_VIO + "console close from %d\n", event->xSourceLp); + } else { + printk(KERN_WARNING_VIO + "console got unexpected close acknowlegement\n"); + } +} + +/* Handle a config charLpEvent. Could be either interrupt or ack + */ +static void vioHandleConfig(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *) event; + int len; + + len = cevent->immediateDataLen; + HvCall_writeLogBuffer(cevent->immediateData, + cevent->immediateDataLen); + + if (cevent->immediateData[0] == 0x01) { + printk(KERN_INFO_VIO + "console window resized to %d: %d: %d: %d\n", + cevent->immediateData[1], + cevent->immediateData[2], + cevent->immediateData[3], cevent->immediateData[4]); + } else { + printk(KERN_WARNING_VIO "console unknown config event\n"); + } + return; +} + +/* Handle a data charLpEvent. + */ +static void vioHandleData(struct HvLpEvent *event) +{ + struct tty_struct *tty; + struct viocharlpevent *cevent = (struct viocharlpevent *) event; + struct port_info_tag *pi; + int len; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING_VIO + "console data on invalid virtual device %d\n", + port); + return; + } + + tty = port_info[port].tty; + + if (tty == NULL) { + printk(KERN_WARNING_VIO + "no tty for virtual device %d\n", port); + return; + } + + if (tty->magic != TTY_MAGIC) { + printk(KERN_WARNING_VIO "tty bad magic\n"); + return; + } + + /* + * Just to be paranoid, make sure the tty points back to this port + */ + pi = (struct port_info_tag *) tty->driver_data; + + if (!pi || viotty_paranoia_check(pi, tty->device, "vioHandleData")) + return; + + len = cevent->immediateDataLen; + + if (len == 0) + return; + + /* + * Log port 0 data to the hypervisor log + */ + if (port == 0) + HvCall_writeLogBuffer(cevent->immediateData, + cevent->immediateDataLen); + + /* Don't copy more bytes than there is room for in the buffer */ + if (tty->flip.count + len > TTY_FLIPBUF_SIZE) { + len = TTY_FLIPBUF_SIZE - tty->flip.count; + printk(KERN_WARNING_VIO + "console input buffer overflow!\n"); + } + + memcpy(tty->flip.char_buf_ptr, cevent->immediateData, len); + memset(tty->flip.flag_buf_ptr, TTY_NORMAL, len); + + /* Update the kernel buffer end */ + tty->flip.count += len; + tty->flip.char_buf_ptr += len; + + tty->flip.flag_buf_ptr += len; + + tty_flip_buffer_push(tty); +} + +/* Handle an ack charLpEvent. + */ +static void vioHandleAck(struct HvLpEvent *event) +{ + struct viocharlpevent *cevent = (struct viocharlpevent *) event; + unsigned long flags; + u8 port = cevent->virtualDevice; + + if (port >= VTTY_PORTS) { + printk(KERN_WARNING_VIO + "viocons: data on invalid virtual device\n"); + return; + } + + spin_lock_irqsave(&consolelock, flags); + sndMsgAck[port] = event->xCorrelationToken; + spin_unlock_irqrestore(&consolelock, flags); + + if (overflow[port].bufferUsed) + sendBuffers(port, port_info[port].lp); +} + +/* Handle charLpEvents and route to the appropriate routine + */ +static void vioHandleCharEvent(struct HvLpEvent *event) +{ + int charminor; + + if (event == NULL) { + return; + } + charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (charminor) { + case viocharopen: + vioHandleOpenEvent(event); + break; + case viocharclose: + vioHandleCloseEvent(event); + break; + case viochardata: + vioHandleData(event); + break; + case viocharack: + vioHandleAck(event); + break; + case viocharconfig: + vioHandleConfig(event); + break; + default: + if ((event->xFlags.xFunction == HvLpEvent_Function_Int) && + (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/* Send an open event + */ +static int viocons_sendOpen(HvLpIndex remoteLp, u8 port, void *sem) +{ + return HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_chario + | viocharopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (remoteLp), + viopath_targetinst + (remoteLp), + (u64) (unsigned long) + sem, VIOVERSION << 16, + ((u64) port << 48), 0, 0, 0); + +} + +int __init viocons_init2(void) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + + /* + * Now open to the primary LP + */ + printk(KERN_INFO_VIO "console open path to primary\n"); + rc = viopath_open(HvLpConfig_getPrimaryLpIndex(), viomajorsubtype_chario, viochar_window + 2); /* +2 for fudge */ + if (rc) { + printk(KERN_WARNING_VIO + "console error opening to primary %d\n", rc); + } + + if (viopath_hostLp == HvLpIndexInvalid) { + vio_set_hostlp(); + } + + /* + * And if the primary is not the same as the hosting LP, open to the + * hosting lp + */ + if ((viopath_hostLp != HvLpIndexInvalid) && + (viopath_hostLp != HvLpConfig_getPrimaryLpIndex())) { + printk(KERN_INFO_VIO + "console open path to hosting (%d)\n", + viopath_hostLp); + rc = viopath_open(viopath_hostLp, viomajorsubtype_chario, viochar_window + 2); /* +2 for fudge */ + if (rc) { + printk(KERN_WARNING_VIO + "console error opening to partition %d: %d\n", + viopath_hostLp, rc); + } + } + + if (vio_setHandler(viomajorsubtype_chario, vioHandleCharEvent) < 0) { + printk(KERN_WARNING_VIO + "Error seting handler for console events!\n"); + } + + printk(KERN_INFO_VIO "console major number is %d\n", TTY_MAJOR); + + /* First, try to open the console to the hosting lp. + * Wait on a semaphore for the response. + */ + if ((viopath_isactive(viopath_hostLp)) && + (viocons_sendOpen(viopath_hostLp, 0, &Semaphore) == 0)) { + printk(KERN_INFO_VIO + "opening console to hosting partition %d\n", + viopath_hostLp); + down(&Semaphore); + } + + /* + * If we don't have an active console, try the primary + */ + if ((!viopath_isactive(port_info[0].lp)) && + (viopath_isactive(HvLpConfig_getPrimaryLpIndex())) && + (viocons_sendOpen + (HvLpConfig_getPrimaryLpIndex(), 0, &Semaphore) == 0)) { + printk(KERN_INFO_VIO + "opening console to primary partition\n"); + down(&Semaphore); + } + + /* Initialize the tty_driver structure */ + memset(&viotty_driver, 0, sizeof(struct tty_driver)); + viotty_driver.magic = TTY_DRIVER_MAGIC; + viotty_driver.driver_name = "vioconsole"; +#if defined(CONFIG_DEVFS_FS) + viotty_driver.name = "tty%d"; +#else + viotty_driver.name = "tty"; +#endif + viotty_driver.major = TTY_MAJOR; + viotty_driver.minor_start = 1; + viotty_driver.name_base = 1; + viotty_driver.num = VTTY_PORTS; + viotty_driver.type = TTY_DRIVER_TYPE_CONSOLE; + viotty_driver.subtype = 1; + viotty_driver.init_termios = tty_std_termios; + viotty_driver.flags = + TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; + viotty_driver.refcount = &viotty_refcount; + viotty_driver.table = viotty_table; + viotty_driver.termios = viotty_termios; + viotty_driver.termios_locked = viotty_termios_locked; + + viotty_driver.open = viotty_open; + viotty_driver.close = viotty_close; + viotty_driver.write = viotty_write; + viotty_driver.put_char = viotty_put_char; + viotty_driver.flush_chars = viotty_flush_chars; + viotty_driver.write_room = viotty_write_room; + viotty_driver.chars_in_buffer = viotty_chars_in_buffer; + viotty_driver.flush_buffer = viotty_flush_buffer; + viotty_driver.ioctl = viotty_ioctl; + viotty_driver.throttle = viotty_throttle; + viotty_driver.unthrottle = viotty_unthrottle; + viotty_driver.set_termios = viotty_set_termios; + viotty_driver.stop = viotty_stop; + viotty_driver.start = viotty_start; + viotty_driver.hangup = viotty_hangup; + viotty_driver.break_ctl = viotty_break; + viotty_driver.send_xchar = viotty_send_xchar; + viotty_driver.wait_until_sent = viotty_wait_until_sent; + + viottyS_driver = viotty_driver; +#if defined(CONFIG_DEVFS_FS) + viottyS_driver.name = "ttyS%d"; +#else + viottyS_driver.name = "ttyS"; +#endif + viottyS_driver.major = TTY_MAJOR; + viottyS_driver.minor_start = VIOTTY_SERIAL_START; + viottyS_driver.type = TTY_DRIVER_TYPE_SERIAL; + viottyS_driver.table = viottyS_table; + viottyS_driver.termios = viottyS_termios; + viottyS_driver.termios_locked = viottyS_termios_locked; + + if (tty_register_driver(&viotty_driver)) { + printk(KERN_WARNING_VIO + "Couldn't register console driver\n"); + } + + if (tty_register_driver(&viottyS_driver)) { + printk(KERN_WARNING_VIO + "Couldn't register console S driver\n"); + } + /* Now create the vcs and vcsa devfs entries so mingetty works */ +#if defined(CONFIG_DEVFS_FS) + { + struct tty_driver temp_driver = viotty_driver; + int i; + + temp_driver.name = "vcs%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs(&temp_driver, + 0, i + temp_driver.minor_start); + + temp_driver.name = "vcsa%d"; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs(&temp_driver, + 0, i + temp_driver.minor_start); + + // For compatibility with some earlier code only! + // This will go away!!! + temp_driver.name = "viocons/%d"; + temp_driver.name_base = 0; + for (i = 0; i < VTTY_PORTS; i++) + tty_register_devfs(&temp_driver, + 0, i + temp_driver.minor_start); + } +#endif + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viocons_proc_init); + + return 0; +} + +void __init viocons_init(void) +{ + int i; + printk(KERN_INFO_VIO "registering console\n"); + + memset(&port_info, 0x00, sizeof(port_info)); + for (i = 0; i < VTTY_PORTS; i++) { + sndMsgSeq[i] = sndMsgAck[i] = 0; + port_info[i].port = i; + port_info[i].lp = HvLpIndexInvalid; + port_info[i].magic = VIOTTY_MAGIC; + } + + register_console(&viocons); + memset(overflow, 0x00, sizeof(overflow)); + debug = 0; + + HvCall_setLogBufferFormatAndCodepage(HvCall_LogBuffer_ASCII, 437); +} diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/viodasd.c linuxppc64_2_4/drivers/iseries/viodasd.c --- linux-2.4.9-ac10/drivers/iseries/viodasd.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/viodasd.c Thu Sep 27 14:00:26 2001 @@ -0,0 +1,1416 @@ +/* -*- linux-c -*- + * viodasd.c + * Authors: Dave Boutcher + * Ryan Arnold + * Colin Devilbiss + * + * (C) Copyright 2000 IBM Corporation + * + * 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 + *************************************************************************** + * This routine provides access to disk space (termed "DASD" in historical + * IBM terms) owned and managed by an OS/400 partition running on the + * same box as this Linux partition. + * + * All disk operations are performed by sending messages back and forth to + * the OS/400 partition. + * + * This device driver can either use it's own major number, or it can + * pretend to be an IDE drive (Major #3). Currently it doesn't + * emulate all the other IDE majors. This is controlled with a + * CONFIG option. You can either call this an elegant solution to the + * fact that a lot of software doesn't recognize a new disk major number... + * or you can call this a really ugly hack. Your choice. + */ + +#include +#include + +/* Decide if we are using our own major or pretending to be an IDE drive + * + * If we are using our own majors, we only support 3 partitions per physical + * disk....so with minor numbers 0-255 we get a maximum of 64 disks. If we + * are emulating IDE, we get 16 partitions per disk, with a maximum of 16 + * disks + */ +#ifdef CONFIG_VIODASD_IDE +#define MAJOR_NR IDE0_MAJOR +#define PARTITION_SHIFT 6 +#define do_viodasd_request do_hd_request +static int numdsk = 16; +static int viodasd_max_disk = 16; +#define VIOD_DEVICE_NAME "hd" +#define VIOD_GENHD_NAME "hd" +#else +#define MAJOR_NR VIODASD_MAJOR +#define PARTITION_SHIFT 3 +static int numdsk = 32; +static int viodasd_max_disk = 32; +#define VIOD_DEVICE_NAME "viod" +#ifdef CONFIG_DEVFS_FS +#define VIOD_GENHD_NAME "viod" +#else +#define VIOD_GENHD_NAME "iSeries/vd" +#endif /* CONFIG_DEVFS */ +#endif /* CONFIG_VIODASD_IDE */ + +#define VIODASD_VERS "1.02" +#define LOCAL_END_REQUEST + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "vio.h" +#include + +MODULE_DESCRIPTION("iSeries Virtual DASD"); +MODULE_AUTHOR("Dave Boutcher"); + +#define VIOMAXREQ 16 +#define VIOMAXBLOCKDMA 12 + +extern struct pci_dev * iSeries_vio_dev; + +struct vioblocklpevent { + struct HvLpEvent event; + u32 mReserved1; + u16 mVersion; + u16 mSubTypeRc; + u16 mDisk; + u16 mFlags; + union { + struct { // Used during open + u64 mDiskLen; + u16 mMaxDisks; + u16 mCylinders; + u16 mTracks; + u16 mSectors; + u16 mBytesPerSector; + } openData; + struct { // Used during rw + u64 mOffset; + struct { + u32 mToken; + u32 reserved; + u64 mLen; + } dmaInfo[VIOMAXBLOCKDMA]; + } rwData; + + struct { + u64 changed; + } check; + } u; +}; + +#define vioblockflags_ro 0x0001 + +enum vioblocksubtype { + vioblockopen = 0x0001, + vioblockclose = 0x0002, + vioblockread = 0x0003, + vioblockwrite = 0x0004, + vioblockflush = 0x0005, + vioblockcheck = 0x0007 +}; + +/* In a perfect world we will perform better if we get page-aligned I/O + * requests, in multiples of pages. At least peg our block size fo the + * actual page size. + */ +static int blksize = HVPAGESIZE; /* in bytes */ + +static DECLARE_WAIT_QUEUE_HEAD(viodasd_wait); +struct viodasd_waitevent { + struct semaphore *sem; + int rc; + int changed; /* Used only for check_change */ +}; + +/* All our disk-related global structures + */ +static struct hd_struct *viodasd_partitions; +static int *viodasd_sizes; +static int *viodasd_sectsizes; +static int *viodasd_maxsectors; +extern struct gendisk viodasd_gendsk; + +/* Figure out the biggest I/O request (in sectors) we can accept + */ +#define VIODASD_MAXSECTORS (4096 / 512 * VIOMAXBLOCKDMA) + +/* Keep some statistics on what's happening for the PROC file system + */ +static struct { + long tot; + long nobh; + long ntce[VIOMAXBLOCKDMA]; +} viod_stats[64][2]; + +/* Number of disk I/O requests we've sent to OS/400 + */ +static int numReqOut; + +/* This is our internal structure for keeping track of disk devices + */ +struct viodasd_device { + int useCount; + u16 cylinders; + u16 tracks; + u16 sectors; + u16 bytesPerSector; + u64 size; + int readOnly; +} *viodasd_devices; + +/* When we get a disk I/O request we take it off the general request queue + * and put it here. + */ +static LIST_HEAD(reqlist); + +/* Handle reads from the proc file system + */ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + int j; + +#if defined(MODULE) + len += + sprintf(buf + len, + "viod Module opened %d times. Major number %d\n", + MOD_IN_USE, MAJOR_NR); +#endif + len += sprintf(buf + len, "viod %d devices\n", numdsk); + + for (i = 0; i < 16; i++) { + if (viod_stats[i][0].tot || viod_stats[i][1].tot) { + len += + sprintf(buf + len, + "DISK %2.2d: rd %-10.10ld wr %-10.10ld (no buffer list rd %-10.10ld wr %-10.10ld\n", + i, viod_stats[i][0].tot, + viod_stats[i][1].tot, + viod_stats[i][0].nobh, + viod_stats[i][1].nobh); + + len += sprintf(buf + len, "rd DMA: "); + + for (j = 0; j < VIOMAXBLOCKDMA; j++) + len += sprintf(buf + len, " [%2.2d] %ld", + j, + viod_stats[i][0].ntce[j]); + + len += sprintf(buf + len, "\nwr DMA: "); + + for (j = 0; j < VIOMAXBLOCKDMA; j++) + len += sprintf(buf + len, " [%2.2d] %ld", + j, + viod_stats[i][1].ntce[j]); + len += sprintf(buf + len, "\n"); + } + } + + *eof = 1; + return len; +} + +/* Handle writes to our proc file system + */ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return count; +} + +/* setup our proc file system entries + */ +void viodasd_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = + create_proc_entry("viodasd", S_IFREG | S_IRUSR, iSeries_proc); + if (!ent) + return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/* clean up our proc file system entries + */ +void viodasd_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viodasd", iSeries_proc); +} + +/* End a request + */ +static void viodasd_end_request(struct request *req, int uptodate) +{ + + if (end_that_request_first(req, uptodate, VIOD_DEVICE_NAME)) + return; + + end_that_request_last(req); +} + +/* This rebuilds the partition information for a single disk device + */ +static int viodasd_revalidate(kdev_t dev) +{ + int i; + int device_no = DEVICE_NR(dev); + int part0 = (device_no << PARTITION_SHIFT); + int npart = (1 << PARTITION_SHIFT); + int minor; + kdev_t devp; + struct super_block *sb; + + if (viodasd_devices[device_no].size == 0) + return 0; + + for (i = npart - 1; i >= 0; i--) { + minor = part0 + i; + + if (viodasd_partitions[minor].nr_sects != 0) { + devp = MKDEV(MAJOR_NR, minor); + fsync_dev(devp); + + sb = get_super(devp); + if (sb) + invalidate_inodes(sb); + + invalidate_buffers(devp); + } + + viodasd_partitions[minor].start_sect = 0; + viodasd_partitions[minor].nr_sects = 0; + } + + grok_partitions(&viodasd_gendsk, device_no, npart, + viodasd_devices[device_no].size >> 9); + + return 0; +} + +/* This is the actual open code. It gets called from the external + * open entry point, as well as from the init code when we're figuring + * out what disks we have + */ +static int internal_open(int device_no) +{ + int i; + struct viodasd_waitevent we; + + HvLpEvent_Rc hvrc; + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) { + printk(KERN_WARNING_VIO "Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | + vioblockopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) &we, + VIOVERSION << 16, + ((u64) device_no << 48), 0, 0, + 0); + + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code */ + if (we.rc != 0) { + printk(KERN_WARNING_VIO "bad rc opening disk: %d\n", (int) we.rc); + return we.rc; + } + + /* If this is the first open of this device, update the device information */ + /* If this is NOT the first open, assume that it isn't changing */ + if (viodasd_devices[device_no].useCount == 0) { + if (viodasd_devices[device_no].size > 0) { + /* divide by 512 */ + u64 tmpint = viodasd_devices[device_no].size >> 9; + viodasd_partitions[device_no << PARTITION_SHIFT]. + nr_sects = tmpint; + /* Now the value divided by 1024 */ + tmpint = tmpint >> 1; + viodasd_sizes[device_no << PARTITION_SHIFT] = + tmpint; + + for (i = (device_no << PARTITION_SHIFT); + i < ((device_no + 1) << PARTITION_SHIFT); i++) + viodasd_sectsizes[i] = + viodasd_devices[device_no]. + bytesPerSector; + + } + } else { + /* If the size of the device changed, wierd things are happening! */ + if (viodasd_sizes[device_no << PARTITION_SHIFT] != + viodasd_devices[device_no].size >> 10) { + printk(KERN_WARNING_VIO + "disk size change (%dK to %dK) for device %d\n", + viodasd_sizes[device_no << PARTITION_SHIFT], + (int) viodasd_devices[device_no].size >> 10, + device_no); + } + } + + /* Bump the use count */ + viodasd_devices[device_no].useCount++; + + return 0; +} + +/* This is the actual release code. It gets called from the external + * release entry point, as well as from the init code when we're figuring + * out what disks we have + */ +static int internal_release(int device_no) +{ + /* Send the event to OS/400. We DON'T expect a response */ + HvLpEvent_Rc hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio + | vioblockclose, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + 0, + VIOVERSION << 16, + ((u64) device_no + << 48), + 0, 0, 0); + + viodasd_devices[device_no].useCount--; + + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc sending event to OS/400 %d\n", (int) hvrc); + return -EIO; + } + return 0; +} + +/* External open entry point. + */ +static int viodasd_open(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) { + printk(KERN_WARNING_VIO "no inode provided in open\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) { + printk(KERN_WARNING_VIO "Wierd error...wrong major number on open\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) { + printk(KERN_WARNING_VIO "Invalid minor device number %d in open\n", + device_no); + return -ENODEV; + } + + /* Call the actual open code */ + if (internal_open(device_no) == 0) { + if (fil && fil->f_mode) { + if (fil->f_mode & 2) { + if (viodasd_devices[device_no].readOnly) { + internal_release(device_no); + return -EROFS; + } + } + } + MOD_INC_USE_COUNT; + return 0; + } else { + return -EIO; + } +} + +/* External release entry point. + */ +static int viodasd_release(struct inode *ino, struct file *fil) +{ + int device_no; + + /* Do a bunch of sanity checks */ + if (!ino) { + printk(KERN_WARNING_VIO "no inode provided in release\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) { + printk(KERN_WARNING_VIO + "Wierd error...wrong major number on release\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) { + return -ENODEV; + } + + /* Just to be paranoid, sync the device */ + fsync_dev(ino->i_rdev); + + /* Call the actual release code */ + internal_release(device_no); + + MOD_DEC_USE_COUNT; + return 0; +} + +/* External ioctl entry point. + */ +static int viodasd_ioctl(struct inode *ino, struct file *fil, + unsigned int cmd, unsigned long arg) +{ + int device_no; + int err; + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Sanity checks */ + if (!ino) { + printk(KERN_WARNING_VIO "no inode provided in ioctl\n"); + return -ENODEV; + } + + if (MAJOR(ino->i_rdev) != MAJOR_NR) { + printk(KERN_WARNING_VIO "Wierd error...wrong major number on ioctl\n"); + return -ENODEV; + } + + device_no = DEVICE_NR(ino->i_rdev); + if (device_no > numdsk) { + printk(KERN_WARNING_VIO "Invalid minor device number %d in ioctl\n", + device_no); + return -ENODEV; + } + + switch (cmd) { + case BLKGETSIZE: + /* return the device size in sectors */ + if (!arg) + return -EINVAL; + err = + verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + + put_user(viodasd_partitions[MINOR(ino->i_rdev)].nr_sects, + (long *) arg); + return 0; + + case FDFLUSH: + case BLKFLSBUF: + if (!suser()) + return -EACCES; + fsync_dev(ino->i_rdev); + invalidate_buffers(ino->i_rdev); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio + | vioblockflush, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) + &Semaphore, + VIOVERSION << 16, + ((u64) device_no << + 48), 0, 0, 0); + + + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on sync signalLpEvent %d\n", + (int) hvrc); + return -EIO; + } + + down(&Semaphore); + + return 0; + + case BLKRAGET: + if (!arg) + return -EINVAL; + err = + verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + put_user(read_ahead[MAJOR_NR], (long *) arg); + return 0; + + case BLKRASET: + if (!suser()) + return -EACCES; + if (arg > 0x00ff) + return -EINVAL; + read_ahead[MAJOR_NR] = arg; + return 0; + + case BLKRRPART: + viodasd_revalidate(ino->i_rdev); + return 0; + + case HDIO_GETGEO: + { + unsigned char sectors; + unsigned char heads; + unsigned short cylinders; + + struct hd_geometry *geo = + (struct hd_geometry *) arg; + if (geo == NULL) + return -EINVAL; + + err = verify_area(VERIFY_WRITE, geo, sizeof(*geo)); + if (err) + return err; + + sectors = viodasd_devices[device_no].sectors; + if (sectors == 0) + sectors = 32; + + heads = viodasd_devices[device_no].tracks; + if (heads == 0) + heads = 64; + + cylinders = viodasd_devices[device_no].cylinders; + if (cylinders == 0) + cylinders = + viodasd_partitions[MINOR(ino->i_rdev)]. + nr_sects / (sectors * heads); + + put_user(sectors, &geo->sectors); + put_user(heads, &geo->heads); + put_user(cylinders, &geo->cylinders); + + put_user(viodasd_partitions[MINOR(ino->i_rdev)]. + start_sect, (long *) &geo->start); + + return 0; + } + +#define PRTIOC(x) case x: printk(KERN_WARNING_VIO "got unsupported FD ioctl " #x "\n"); \ + return -EINVAL; + + PRTIOC(FDCLRPRM); + PRTIOC(FDSETPRM); + PRTIOC(FDDEFPRM); + PRTIOC(FDGETPRM); + PRTIOC(FDMSGON); + PRTIOC(FDMSGOFF); + PRTIOC(FDFMTBEG); + PRTIOC(FDFMTTRK); + PRTIOC(FDFMTEND); + PRTIOC(FDSETEMSGTRESH); + PRTIOC(FDSETMAXERRS); + PRTIOC(FDGETMAXERRS); + PRTIOC(FDGETDRVTYP); + PRTIOC(FDSETDRVPRM); + PRTIOC(FDGETDRVPRM); + PRTIOC(FDGETDRVSTAT); + PRTIOC(FDPOLLDRVSTAT); + PRTIOC(FDRESET); + PRTIOC(FDGETFDCSTAT); + PRTIOC(FDWERRORCLR); + PRTIOC(FDWERRORGET); + PRTIOC(FDRAWCMD); + PRTIOC(FDEJECT); + PRTIOC(FDTWADDLE); + + } + + return -EINVAL; +} + +/* Send an actual I/O request to OS/400 + */ +static int send_request(struct request *req) +{ + u64 sect_size; + u64 start; + u64 len; + int direction; + int nsg; + u16 viocmd; + HvLpEvent_Rc hvrc; + struct vioblocklpevent *bevent; + struct scatterlist sg[VIOMAXBLOCKDMA]; + struct buffer_head *bh; + int sgindex; + int device_no = DEVICE_NR(req->rq_dev); + int statindex; + + /* Note that this SHOULD always be 512...but lets be architecturally correct */ + sect_size = hardsect_size[MAJOR_NR][device_no]; + + /* Figure out teh starting sector and length */ + start = + (req->sector + + viodasd_partitions[MINOR(req->rq_dev)].start_sect) * + sect_size; + len = req->nr_sectors * sect_size; + + /* More paranoia checks */ + if ((req->sector + req->nr_sectors) > + (viodasd_partitions[MINOR(req->rq_dev)].start_sect + + viodasd_partitions[MINOR(req->rq_dev)].nr_sects)) { + printk(KERN_WARNING_VIO "Invalid request offset & length\n"); + printk(KERN_WARNING_VIO "req->sector: %ld, req->nr_sectors: %ld\n", + req->sector, req->nr_sectors); + printk(KERN_WARNING_VIO "RQ_DEV: %d, minor: %d\n", req->rq_dev, + MINOR(req->rq_dev)); + return -1; + } + + if (req->cmd == READ) { + direction = PCI_DMA_FROMDEVICE; + viocmd = viomajorsubtype_blockio | vioblockread; + statindex = 0; + } else { + direction = PCI_DMA_TODEVICE; + viocmd = viomajorsubtype_blockio | vioblockwrite; + statindex = 1; + } + + /* Update totals */ + viod_stats[device_no][statindex].tot++; + + /* Now build the scatter-gather list */ + memset(&sg, 0x00, sizeof(sg)); + sgindex = 0; + + /* See if this is a swap I/O (without a bh pointer) or a regular I/O */ + if (req->bh) { + /* OK...this loop takes buffers from the request and adds them to the SG + until we're done, or until we hit a maximum. If we hit a maximum we'll + just finish this request later */ + bh = req->bh; + while ((bh) && (sgindex < VIOMAXBLOCKDMA)) { + sg[sgindex].address = bh->b_data; + sg[sgindex].length = bh->b_size; + + sgindex++; + bh = bh->b_reqnext; + } + nsg = pci_map_sg(iSeries_vio_dev, sg, sgindex, direction); + if ((nsg == 0) || (sg[0].dma_length == 0) + || (sg[0].dma_address == 0xFFFFFFFF)) { + printk(KERN_WARNING_VIO "error getting sg tces\n"); + return -1; + } + + } else { + /* Update stats */ + viod_stats[device_no][statindex].nobh++; + + sg[0].dma_address = pci_map_single(iSeries_vio_dev, req->buffer, + len, direction); + if (sg[0].dma_address == 0xFFFFFFFF) { + printk(KERN_WARNING_VIO + "error allocating tce for address %p len %ld\n", + req->buffer, (long) len); + return -1; + } + sg[0].dma_length = len; + nsg = 1; + } + + /* Update stats */ + viod_stats[device_no][statindex].ntce[sgindex]++; + + /* This optimization handles a single DMA block */ + if (sgindex == 1) { + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio + | viocmd, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) + req->buffer, + VIOVERSION << 16, + ((u64) device_no << + 48), start, + ((u64) sg[0]. + dma_address) << 32, + sg[0].dma_length); + } else { + bevent = (struct vioblocklpevent *) vio_get_event_buffer(viomajorsubtype_blockio); + if (bevent == NULL) { + printk(KERN_WARNING_VIO + "error allocating disk event buffer\n"); + return -1; + } + + /* Now build up the actual request. Note that we store the pointer */ + /* to the request buffer in the correlation token so we can match */ + /* this response up later */ + memset(bevent, 0x00, sizeof(struct vioblocklpevent)); + bevent->event.xFlags.xValid = 1; + bevent->event.xFlags.xFunction = HvLpEvent_Function_Int; + bevent->event.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + bevent->event.xFlags.xAckType = + HvLpEvent_AckType_ImmediateAck; + bevent->event.xType = HvLpEvent_Type_VirtualIo; + bevent->event.xSubtype = viocmd; + bevent->event.xSourceLp = HvLpConfig_getLpIndex(); + bevent->event.xTargetLp = viopath_hostLp; + bevent->event.xSizeMinus1 = + offsetof(struct vioblocklpevent, + u.rwData.dmaInfo) + + (sizeof(bevent->u.rwData.dmaInfo[0]) * (sgindex)) - 1; + bevent->event.xSizeMinus1 = + sizeof(struct vioblocklpevent) - 1; + bevent->event.xSourceInstanceId = + viopath_sourceinst(viopath_hostLp); + bevent->event.xTargetInstanceId = + viopath_targetinst(viopath_hostLp); + bevent->event.xCorrelationToken = + (u64) (unsigned long) req->buffer; + bevent->mVersion = VIOVERSION; + bevent->mDisk = device_no; + bevent->u.rwData.mOffset = start; + + /* Copy just the dma information from the sg list into the request */ + for (sgindex = 0; sgindex < nsg; sgindex++) { + bevent->u.rwData.dmaInfo[sgindex].mToken = + sg[sgindex].dma_address; + bevent->u.rwData.dmaInfo[sgindex].mLen = + sg[sgindex].dma_length; + } + + /* Send the request */ + hvrc = HvCallEvent_signalLpEvent(&bevent->event); + vio_free_event_buffer(viomajorsubtype_blockio, bevent); + } + + if (hvrc != HvLpEvent_Rc_Good) { + printk(KERN_WARNING_VIO "error sending disk event to OS/400 (rcp %d)\n", (int) hvrc); + return -1; + } else { + /* If the request was successful, bump the number of outstanding */ + numReqOut++; + } + return 0; +} + +/* This is the external request processing routine + */ +static void do_viodasd_request(request_queue_t * q) +{ + int device_no; + struct request *req; + for (;;) { + + INIT_REQUEST; + + device_no = CURRENT_DEV; + if (device_no > numdsk) { + printk(KERN_WARNING_VIO "Invalid device # %d\n", CURRENT_DEV); + viodasd_end_request(CURRENT, 0); + continue; + } + + if (viodasd_gendsk.sizes == NULL) { + printk(KERN_WARNING_VIO + "Ouch! viodasd_gendsk.sizes is NULL\n"); + viodasd_end_request(CURRENT, 0); + continue; + } + + /* If the queue is plugged, don't dequeue anything right now */ + if ((q) && (q->plugged)) { + return; + } + + /* If we already have the maximum number of requests outstanding to OS/400 + just bail out. We'll come back later */ + if (numReqOut >= VIOMAXREQ) + return; + + /* get the current request, then dequeue it from the queue */ + req = CURRENT; + blkdev_dequeue_request(req); + + /* Try sending the request */ + if (send_request(req) == 0) { + list_add_tail(&req->queue, &reqlist); + } else { + viodasd_end_request(req, 0); + } + } +} + +/* Check for changed disks + */ +static int viodasd_check_change(kdev_t dev) +{ + struct viodasd_waitevent we; + HvLpEvent_Rc hvrc; + int device_no = DEVICE_NR(dev); + + /* This semaphore is raised in the interrupt handler */ + DECLARE_MUTEX_LOCKED(Semaphore); + + /* Check that we are dealing with a valid hosting partition */ + if (viopath_hostLp == HvLpIndexInvalid) { + printk(KERN_WARNING_VIO "Invalid hosting partition\n"); + return -EIO; + } + + we.sem = &Semaphore; + + /* Send the open event to OS/400 */ + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_blockio | + vioblockcheck, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) &we, + VIOVERSION << 16, + ((u64) device_no << 48), 0, 0, + 0); + + if (hvrc != 0) { + printk(KERN_WARNING_VIO "bad rc on signalLpEvent %d\n", (int) hvrc); + return -EIO; + } + + /* Wait for the interrupt handler to get the response */ + down(&Semaphore); + + /* Check the return code. If bad, assume no change */ + if (we.rc != 0) { + printk(KERN_WARNING_VIO "bad rc on check_change. Assuming no change\n"); + return 0; + } + + return we.changed; +} + +/* Our file operations table + */ +static struct block_device_operations viodasd_fops = { + open:viodasd_open, + release:viodasd_release, + ioctl:viodasd_ioctl, + check_media_change:viodasd_check_change, + revalidate:viodasd_revalidate +}; + +/* Our gendisk table + */ +struct gendisk viodasd_gendsk = { + 0, /* major - fill in later */ + "viodasd", + PARTITION_SHIFT, + 1 << PARTITION_SHIFT, + NULL, /* partition array - fill in later */ + NULL, /* block sizes - fill in later */ + 0, /* # units */ + NULL, /* "real device" pointer */ + NULL, /* next */ + &viodasd_fops /* operations */ +}; + +/* This routine handles incoming block LP events + */ +static void vioHandleBlockEvent(struct HvLpEvent *event) +{ + struct scatterlist sg[VIOMAXBLOCKDMA]; + struct vioblocklpevent *bevent = (struct vioblocklpevent *) event; + int nsect; + struct request *req; + int i; + struct viodasd_waitevent *pwe; + unsigned long flags; + int maxsg; + + if (event == NULL) { + /* Notification that a partition went away! */ + return; + } + // First, we should NEVER get an int here...only acks + if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + printk(KERN_WARNING_VIO + "Yikes! got an int in viodasd event handler!\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + + switch (event->xSubtype & VIOMINOR_SUBTYPE_MASK) { + + /* Handle a response to an open request. We get all the disk information + * in the response, so update it. The correlation token contains a pointer to + * a waitevent structure that has a semaphore in it. update the return code + * in the waitevent structure and post the semaphore to wake up the guy who + * sent the request */ + case vioblockopen: + pwe = + (struct viodasd_waitevent *) (unsigned long) event-> + xCorrelationToken; + pwe->rc = event->xRc; + if (event->xRc == HvLpEvent_Rc_Good) { + viodasd_devices[bevent->mDisk].size = + bevent->u.openData.mDiskLen; + viodasd_devices[bevent->mDisk].cylinders = + bevent->u.openData.mCylinders; + viodasd_devices[bevent->mDisk].tracks = + bevent->u.openData.mTracks; + viodasd_devices[bevent->mDisk].sectors = + bevent->u.openData.mSectors; + viodasd_devices[bevent->mDisk].bytesPerSector = + bevent->u.openData.mBytesPerSector; + viodasd_devices[bevent->mDisk].readOnly = + bevent->mFlags & vioblockflags_ro; + + if (viodasd_max_disk != + bevent->u.openData.mMaxDisks) { + viodasd_max_disk = + bevent->u.openData.mMaxDisks; + } + } + up(pwe->sem); + break; + + case vioblockclose: + break; + + /* For read and write requests, decrement the number of outstanding requests, + * Free the DMA buffers we allocated, and find the matching request by + * using the buffer pointer we stored in the correlation token. + */ + case vioblockread: + case vioblockwrite: + + /* Free the DMA buffers */ + i = 0; + nsect = 0; + memset(sg, 0x00, sizeof(sg)); + + maxsg = (((bevent->event.xSizeMinus1 + 1) - + offsetof(struct vioblocklpevent, + u.rwData.dmaInfo)) / + sizeof(bevent->u.rwData.dmaInfo[0])); + + + while ((i < maxsg) && + (bevent->u.rwData.dmaInfo[i].mLen > 0) && + (i < VIOMAXBLOCKDMA)) { + sg[i].dma_address = + bevent->u.rwData.dmaInfo[i].mToken; + sg[i].dma_length = + bevent->u.rwData.dmaInfo[i].mLen; + nsect += bevent->u.rwData.dmaInfo[i].mLen; + i++; + } + + pci_unmap_sg(iSeries_vio_dev, + sg, + i, + (bevent->event.xSubtype == + (viomajorsubtype_blockio | vioblockread)) ? + PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + + + /* Since this is running in interrupt mode, we need to make sure we're not + * stepping on any global I/O operations + */ + spin_lock_irqsave(&io_request_lock, flags); + + /* Decrement the number of outstanding requests */ + numReqOut--; + + /* Now find the matching request in OUR list (remember we moved the request + * from the global list to our list when we got it) + */ + req = blkdev_entry_to_request(reqlist.next); + while ((&req->queue != &reqlist) && + ((u64) (unsigned long) req->buffer != + bevent->event.xCorrelationToken)) + req = blkdev_entry_to_request(req->queue.next); + + if (&req->queue == &reqlist) { + printk(KERN_WARNING_VIO + "Yikes! Could not find matching buffer %p in reqlist\n", + req->buffer); + break; + } + + /* Remove the request from our list */ + list_del(&req->queue); + + /* Calculate the number of sectors from the length in bytes */ + nsect = nsect >> 9; + if (!req->bh) { + if (event->xRc != HvLpEvent_Rc_Good) { + printk(KERN_WARNING_VIO "read/wrute error %d:%d\n", event->xRc, + bevent->mSubTypeRc); + viodasd_end_request(req, 0); + } else { + if (nsect != req->current_nr_sectors) { + printk(KERN_WARNING_VIO + "Yikes...non bh i/o # sect doesn't match!!!\n"); + } + viodasd_end_request(req, 1); + } + } else { + while ((nsect > 0) && (req->bh)) { + nsect -= req->current_nr_sectors; + viodasd_end_request(req, 1); + } + if (nsect) { + printk(KERN_WARNING_VIO + "Yikes...sectors left over on a request!!!\n"); + } + + /* If the original request could not handle all the buffers, re-send + * the request + */ + if (req->bh) { + if (send_request(req) == 0) { + list_add_tail(&req->queue, + &reqlist); + } else { + viodasd_end_request(req, 0); + } + } + + } + + /* Finally, send more requests */ + do_viodasd_request(NULL); + + spin_unlock_irqrestore(&io_request_lock, flags); + break; + + case vioblockflush: + up((void *) (unsigned long) event->xCorrelationToken); + break; + + case vioblockcheck: + pwe = + (struct viodasd_waitevent *) (unsigned long) event-> + xCorrelationToken; + pwe->rc = event->xRc; + pwe->changed = bevent->u.check.changed; + up(pwe->sem); + break; + + default: + printk(KERN_WARNING_VIO "invalid subtype!"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + +/* This routine tries to clean up anything we allocated/registered + */ +static void cleanup2(void) +{ + int i; + +#define CLEANIT(x) if (x) {kfree(x); x=NULL;} + + for (i = 0; i < numdsk; i++) + fsync_dev(MKDEV(MAJOR_NR, i)); + + read_ahead[MAJOR_NR] = 0; + + CLEANIT(viodasd_devices); + CLEANIT(blk_size[MAJOR_NR]); + CLEANIT(blksize_size[MAJOR_NR]); + CLEANIT(hardsect_size[MAJOR_NR]); + CLEANIT(max_sectors[MAJOR_NR]); + CLEANIT(viodasd_gendsk.part); + blk_size[MAJOR_NR] = NULL; + blksize_size[MAJOR_NR] = NULL; + + devfs_unregister_blkdev(MAJOR_NR, VIOD_DEVICE_NAME); +} + +/* Initialize the whole device driver. Handle module and non-module + * versions + */ +__init int viodasd_init(void) +{ + int i, j; + int rc; + int *viodasd_blksizes; + int numpart = numdsk << PARTITION_SHIFT; + + /* Try to open to our host lp + */ + if (viopath_hostLp == HvLpIndexInvalid) { + vio_set_hostlp(); + } + + if (viopath_hostLp == HvLpIndexInvalid) { + printk(KERN_WARNING_VIO "%s: invalid hosting partition\n", + VIOD_DEVICE_NAME); + return -1; + } + + /* + * Do the devfs_register. This works even if devfs is not + * configured + */ + if (devfs_register_blkdev + (MAJOR_NR, VIOD_DEVICE_NAME, &viodasd_fops)) { + printk(KERN_WARNING_VIO "%s: unable to get major number %d\n", + VIOD_DEVICE_NAME, MAJOR_NR); + return -1; + } + + printk(KERN_INFO_VIO + "%s: Disk vers %s, major %d, max disks %d, hosting partition %d\n", + VIOD_DEVICE_NAME, VIODASD_VERS, MAJOR_NR, numdsk, + viopath_hostLp); + + if (ROOT_DEV == NODEV) { + ROOT_DEV = MKDEV(MAJOR_NR,1); + + printk(KERN_INFO_VIO + "Claiming root file system as first partition of first virtual disk"); + } + + /* Do the blk device initialization */ + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); + + read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read ahead */ + + /* Start filling in gendsk structure */ + viodasd_gendsk.major = MAJOR_NR; + viodasd_gendsk.major_name = VIOD_GENHD_NAME; + viodasd_gendsk.nr_real = numdsk; + viodasd_gendsk.next = gendisk_head; + gendisk_head = &viodasd_gendsk; + + /* Actually open the path to the hosting partition */ + rc = viopath_open(viopath_hostLp, viomajorsubtype_blockio, VIOMAXREQ+2); + if (rc) { + printk(KERN_WARNING_VIO "error opening path to host partition %d\n", + viopath_hostLp); + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + return -1; + } else { + printk("%s: opened path to hosting partition %d\n", + VIOD_DEVICE_NAME, viopath_hostLp); + } + + /* + * Initialize our request handler + */ + vio_setHandler(viomajorsubtype_blockio, vioHandleBlockEvent); + + /* + * Now fill in all the device driver info + */ + viodasd_devices = + kmalloc(numdsk * sizeof(struct viodasd_device), GFP_KERNEL); + if (!viodasd_devices) { + cleanup2(); + return -ENOMEM; + } + memset(viodasd_devices, 0x00, + numdsk * sizeof(struct viodasd_device)); + + viodasd_sizes = kmalloc(numpart * sizeof(int), GFP_KERNEL); + if (!viodasd_sizes) { + cleanup2(); + return -ENOMEM; + } + memset(viodasd_sizes, 0x00, numpart * sizeof(int)); + blk_size[MAJOR_NR] = viodasd_gendsk.sizes = viodasd_sizes; + + viodasd_partitions = + kmalloc(numpart * sizeof(struct hd_struct), GFP_KERNEL); + if (!viodasd_partitions) { + cleanup2(); + return -ENOMEM; + } + memset(viodasd_partitions, 0x00, + numpart * sizeof(struct hd_struct)); + viodasd_gendsk.part = viodasd_partitions; + + viodasd_blksizes = kmalloc(numpart * sizeof(int), GFP_KERNEL); + if (!viodasd_blksizes) { + cleanup2(); + return -ENOMEM; + } + for (i = 0; i < numpart; i++) + viodasd_blksizes[i] = blksize; + blksize_size[MAJOR_NR] = viodasd_blksizes; + + viodasd_sectsizes = kmalloc(numpart * sizeof(int), GFP_KERNEL); + if (!viodasd_sectsizes) { + cleanup2(); + return -ENOMEM; + } + for (i = 0; i < numpart; i++) + viodasd_sectsizes[i] = 0; + hardsect_size[MAJOR_NR] = viodasd_sectsizes; + + viodasd_maxsectors = kmalloc(numpart * sizeof(int), GFP_KERNEL); + if (!viodasd_maxsectors) { + cleanup2(); + return -ENOMEM; + } + for (i = 0; i < numpart; i++) + viodasd_maxsectors[i] = VIODASD_MAXSECTORS; + max_sectors[MAJOR_NR] = viodasd_maxsectors; + + viodasd_max_disk = numdsk; + for (i = 0; i <= viodasd_max_disk; i++) { + // Note that internal_open has two side effects: + // a) it updates the size of the disk + // b) it updates viodasd_max_disk + if (internal_open(i) == 0) { + if (i == 0) + printk(KERN_INFO_VIO + "%s: Currently %d disks connected\n", + VIOD_DEVICE_NAME, + (int) viodasd_max_disk + 1); + + register_disk(&viodasd_gendsk, + MKDEV(MAJOR_NR, + i << PARTITION_SHIFT), + 1 << PARTITION_SHIFT, &viodasd_fops, + viodasd_partitions[i << + PARTITION_SHIFT]. + nr_sects); + + printk(KERN_INFO_VIO + "%s: Disk %2.2d size %dM, sectors %d, heads %d, cylinders %d, sectsize %d\n", + VIOD_DEVICE_NAME, i, + (int) (viodasd_devices[i].size / + (1024 * 1024)), + (int) viodasd_devices[i].sectors, + (int) viodasd_devices[i].tracks, + (int) viodasd_devices[i].cylinders, + (int) viodasd_sectsizes[i << + PARTITION_SHIFT]); + + for (j = (i << PARTITION_SHIFT) + 1; + j < ((i + 1) << PARTITION_SHIFT); j++) { + if (viodasd_gendsk.part[j].nr_sects) + printk(KERN_INFO_VIO + "%s: Disk %2.2d partition %2.2d start sector %ld, # sector %ld\n", + VIOD_DEVICE_NAME, i, + j - (i << PARTITION_SHIFT), + viodasd_gendsk.part[j]. + start_sect, + viodasd_gendsk.part[j]. + nr_sects); + } + + internal_release(i); + } + } + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viodasd_proc_init); + + return 0; +} + +#ifdef MODULE +void viodasd_exit(void) +{ + int i; + for (i = 0; i < numdsk << PARTITION_SHIFT; i++) + fsync_dev(MKDEV(MAJOR_NR, i)); + + blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); + + iSeries_proc_callback(&viodasd_proc_delete); + + cleanup2(); +} +#endif + +#ifdef MODULE +module_init(viodasd_init); +module_exit(viodasd_exit); +#endif diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/viopath.c linuxppc64_2_4/drivers/iseries/viopath.c --- linux-2.4.9-ac10/drivers/iseries/viopath.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/viopath.c Thu Sep 27 14:00:26 2001 @@ -0,0 +1,612 @@ +/* -*- linux-c -*- + * arch/ppc64/viopath.c + * + * iSeries Virtual I/O Message Path code + * + * Authors: Dave Boutcher + * Ryan Arnold + * Colin Devilbiss + * + * (C) Copyright 2000 IBM Corporation + * + * This code is used by the iSeries virtual disk, cd, + * tape, and console to communicate with OS/400 in another + * partition. + * + * 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) anyu 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vio.h" + +extern struct pci_dev * iSeries_vio_dev; + +/* Status of the path to each other partition in the system. + * This is overkill, since we will only ever establish connections + * to our hosting partition and the primary partition on the system. + * But this allows for other support in the future. + */ +static struct viopathStatus { + int isOpen:1; /* Did we open the path? */ + int isActive:1; /* Do we have a mon msg outstanding */ + int users[VIO_MAX_SUBTYPES]; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + int numberAllocated; +} viopathStatus[HVMAXARCHITECTEDLPS]; + +static spinlock_t statuslock = SPIN_LOCK_UNLOCKED; + +/* + * For each kind of event we allocate a buffer that is + * guaranteed not to cross a page boundary + */ +static void *event_buffer[VIO_MAX_SUBTYPES]; +static atomic_t event_buffer_available[VIO_MAX_SUBTYPES]; + +static void handleMonitorEvent(struct HvLpEvent *event); + +/* We use this structure to handle asynchronous responses. The caller + * blocks on the semaphore and the handler posts the semaphore. + */ +struct doneAllocParms_t { + struct semaphore *sem; + int number; +}; + +/* Put a sequence number in each mon msg. The value is not + * important. Start at something other than 0 just for + * readability. wrapping this is ok. + */ +static u8 viomonseq = 22; + +/* Our hosting logical partition. We get this at startup + * time, and different modules access this variable directly. + */ +HvLpIndex viopath_hostLp = 0xff; /* HvLpIndexInvalid */ + +/* For each kind of incoming event we set a pointer to a + * routine to call. + */ +static vio_event_handler_t *vio_handler[VIO_MAX_SUBTYPES]; + +/* A page to build an lp event in + */ +static unsigned long VIOReqPage; + +/* Handle reads from the proc file system + */ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + dma_addr_t dmaa = + pci_map_single(iSeries_vio_dev, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); + int len = PAGE_SIZE; + + if (len > blen) + len = blen; + + memset(buf, 0x00, len); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_config | + vioconfigget, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) + &Semaphore, VIOVERSION << 16, + ((u64) dmaa) << 32, len, 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk("viopath hv error on op %d\n", (int) hvrc); + } + + down(&Semaphore); + + pci_unmap_single(iSeries_vio_dev, dmaa, PAGE_SIZE, PCI_DMA_FROMDEVICE); + + *eof = 1; + return strlen(buf); +} + +/* Handle writes to our proc file system + */ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + /* Doesn't do anything today!!! + */ + return count; +} + +/* setup our proc file system entries + */ +static void vio_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("config", S_IFREG | S_IRUSR, iSeries_proc); + if (!ent) + return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/* See if a given LP is active. Allow for invalid lps to be passed in + * and just return invalid + */ +int viopath_isactive(HvLpIndex lp) +{ + if (lp == HvLpIndexInvalid) + return 0; + if (lp < HVMAXARCHITECTEDLPS) + return viopathStatus[lp].isActive; + else + return 0; +} + +/* We cache the source and target instance ids for each + * partition. + */ +HvLpInstanceId viopath_sourceinst(HvLpIndex lp) +{ + return viopathStatus[lp].mSourceInst; +} + +HvLpInstanceId viopath_targetinst(HvLpIndex lp) +{ + return viopathStatus[lp].mTargetInst; +} + +/* Send a monitor message. This is a message with the acknowledge + * bit on that the other side will NOT explicitly acknowledge. WHen + * the other side goes down, the hypervisor will acknowledge any + * outstanding messages....so we will know when the other side dies. + */ +static void sendMonMsg(HvLpIndex remoteLp) +{ + HvLpEvent_Rc hvrc; + + viopathStatus[remoteLp].mSourceInst = + HvCallEvent_getSourceLpInstanceId(remoteLp, + HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].mTargetInst = + HvCallEvent_getTargetLpInstanceId(remoteLp, + HvLpEvent_Type_VirtualIo); + + /* Deliberately ignore the return code here. if we call this + * more than once, we don't care. + */ + vio_setHandler(viomajorsubtype_monitor, handleMonitorEvent); + + hvrc = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_monitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + viopathStatus[remoteLp]. + mSourceInst, + viopathStatus[remoteLp]. + mTargetInst, viomonseq++, + 0, 0, 0, 0, 0); + + if (hvrc == HvLpEvent_Rc_Good) { + viopathStatus[remoteLp].isActive = 1; + } else { + printk(KERN_WARNING_VIO + "could not connect ot partition %d\n", remoteLp); + viopathStatus[remoteLp].isActive = 0; + } +} + +static void handleMonitorEvent(struct HvLpEvent *event) +{ + HvLpIndex remoteLp; + int i; + + /* First see if this is just a normal monitor message from the + * other partition + */ + if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + remoteLp = event->xSourceLp; + if (!viopathStatus[remoteLp].isActive) + sendMonMsg(remoteLp); + return; + } + + /* This path is for an acknowledgement; the other partition + * died + */ + remoteLp = event->xTargetLp; + if ((event->xSourceInstanceId != + viopathStatus[remoteLp].mSourceInst) + || (event->xTargetInstanceId != + viopathStatus[remoteLp].mTargetInst)) { + printk(KERN_WARNING_VIO + "ignoring ack....mismatched instances\n"); + return; + } + + printk(KERN_WARNING_VIO "partition %d ended\n", remoteLp); + + viopathStatus[remoteLp].isActive = 0; + + /* For each active handler, pass them a NULL + * message to indicate that the other partition + * died + */ + for (i = 0; i < VIO_MAX_SUBTYPES; i++) { + if (vio_handler[i] != NULL) + (*vio_handler[i]) (NULL); + } +} + +int vio_setHandler(int subtype, vio_event_handler_t * beh) +{ + subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; + + if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) + return -EINVAL; + + if (vio_handler[subtype] != NULL) + return -EBUSY; + + vio_handler[subtype] = beh; + return 0; +} + +int vio_clearHandler(int subtype) +{ + subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; + + if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) + return -EINVAL; + + if (vio_handler[subtype] == NULL) + return -EAGAIN; + + vio_handler[subtype] = NULL; + return 0; +} + +static void handleConfig(struct HvLpEvent *event) +{ + if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + printk(KERN_WARNING_VIO + "unexpected config request from partition %d", + event->xSourceLp); + + if ((event->xFlags.xFunction == HvLpEvent_Function_Int) && + (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + return; + } + + up((struct semaphore *) event->xCorrelationToken); +} + +/* Initialization of the hosting partition + */ +void vio_set_hostlp(void) +{ + /* If this has already been set then we DON'T want to either change + * it or re-register the proc file system + */ + if (viopath_hostLp != HvLpIndexInvalid) + return; + + /* Figure out our hosting partition. This isn't allowed to change + * while we're active + */ + viopath_hostLp = + HvCallCfg_getHostingLpIndex(HvLpConfig_getLpIndex()); + + /* If we have a valid hosting LP, create a proc file system entry + * for config information + */ + if (viopath_hostLp != HvLpIndexInvalid) { + iSeries_proc_callback(&vio_proc_init); + vio_setHandler(viomajorsubtype_monitor, handleConfig); + } +} + +static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + HvLpIndex remoteLp; + int subtype = + (event-> + xSubtype & VIOMAJOR_SUBTYPE_MASK) >> VIOMAJOR_SUBTYPE_SHIFT; + + if (event->xFlags.xFunction == HvLpEvent_Function_Int) { + remoteLp = event->xSourceLp; + if (event->xSourceInstanceId != + viopathStatus[remoteLp].mTargetInst) { + printk(KERN_WARNING_VIO + "message from invalid partition. " + "int msg rcvd, source inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mTargetInst, + event->xSourceInstanceId); + return; + } + + if (event->xTargetInstanceId != + viopathStatus[remoteLp].mSourceInst) { + printk(KERN_WARNING_VIO + "message from invalid partition. " + "int msg rcvd, target inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mSourceInst, + event->xTargetInstanceId); + return; + } + } else { + remoteLp = event->xTargetLp; + if (event->xSourceInstanceId != + viopathStatus[remoteLp].mSourceInst) { + printk(KERN_WARNING_VIO + "message from invalid partition. " + "ack msg rcvd, source inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mSourceInst, + event->xSourceInstanceId); + return; + } + + if (event->xTargetInstanceId != + viopathStatus[remoteLp].mTargetInst) { + printk(KERN_WARNING_VIO + "message from invalid partition. " + "viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mTargetInst, + event->xTargetInstanceId); + return; + } + } + + if (vio_handler[subtype] == NULL) { + printk(KERN_WARNING_VIO + "unexpected virtual io event subtype %d from partition %d\n", + event->xSubtype, remoteLp); + /* No handler. Ack if necessary + */ + if ((event->xFlags.xFunction == HvLpEvent_Function_Int) && + (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck)) { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + return; + } + + /* This inocuous little line is where all the real work happens + */ + (*vio_handler[subtype]) (event); +} + +static void viopath_donealloc(void *parm, int number) +{ + struct doneAllocParms_t *doneAllocParmsp = + (struct doneAllocParms_t *) parm; + doneAllocParmsp->number = number; + up(doneAllocParmsp->sem); +} + +static int allocateEvents(HvLpIndex remoteLp, int numEvents) +{ + struct doneAllocParms_t doneAllocParms; + DECLARE_MUTEX_LOCKED(Semaphore); + doneAllocParms.sem = &Semaphore; + + mf_allocateLpEvents(remoteLp, HvLpEvent_Type_VirtualIo, 250, /* It would be nice to put a real number here! */ + numEvents, + &viopath_donealloc, &doneAllocParms); + + down(&Semaphore); + + return doneAllocParms.number; +} + +int viopath_open(HvLpIndex remoteLp, int subtype, int numReq) +{ + int i; + unsigned long flags; + + if ((remoteLp >= HvMaxArchitectedLps) + || (remoteLp == HvLpIndexInvalid)) + return -EINVAL; + + subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; + if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) + return -EINVAL; + + spin_lock_irqsave(&statuslock, flags); + + /* OK...we can fit 4 maximum-sized events (256 bytes) in + * each page (4096). Get a new page every 4 + */ + if (event_buffer[0] == NULL) { + for (i = 0; i < VIO_MAX_SUBTYPES; i++) { + if ((i % 4) == 0) { + event_buffer[i] = + (void *) get_free_page(GFP_KERNEL); + if (event_buffer[i] == NULL) { + spin_unlock_irqrestore(&statuslock, flags); + return -ENOMEM; + } + } else { + event_buffer[i] = + event_buffer[i - 1] + 256; + } + atomic_set(&event_buffer_available[i], 1); + } + } + + viopathStatus[remoteLp].users[subtype]++; + + if (!viopathStatus[remoteLp].isOpen) { + HvCallEvent_openLpEventPath(remoteLp, + HvLpEvent_Type_VirtualIo); + + viopathStatus[remoteLp].numberAllocated += + allocateEvents(remoteLp, 1); + + if (viopathStatus[remoteLp].numberAllocated == 0) { + HvCallEvent_closeLpEventPath(remoteLp, + HvLpEvent_Type_VirtualIo); + + spin_unlock_irqrestore(&statuslock, flags); + return -ENOMEM; + } + + viopathStatus[remoteLp].mSourceInst = + HvCallEvent_getSourceLpInstanceId(remoteLp, + HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].mTargetInst = + HvCallEvent_getTargetLpInstanceId(remoteLp, + HvLpEvent_Type_VirtualIo); + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo, + &vio_handleEvent); + + viopathStatus[remoteLp].isOpen = 1; + + sendMonMsg(remoteLp); + + printk(KERN_INFO_VIO + "Opening connection to partition %d, setting sinst %d, tinst %d\n", + remoteLp, + viopathStatus[remoteLp].mSourceInst, + viopathStatus[remoteLp].mTargetInst); + } + + viopathStatus[remoteLp].numberAllocated += + allocateEvents(remoteLp, numReq); + spin_unlock_irqrestore(&statuslock, flags); + + return 0; +} + +int viopath_close(HvLpIndex remoteLp, int subtype, int numReq) +{ + unsigned long flags; + int i; + int numOpen; + struct doneAllocParms_t doneAllocParms; + DECLARE_MUTEX_LOCKED(Semaphore); + doneAllocParms.sem = &Semaphore; + + if ((remoteLp >= HvMaxArchitectedLps) + || (remoteLp == HvLpIndexInvalid)) + return -EINVAL; + + subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; + if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) + return -EINVAL; + + spin_lock_irqsave(&statuslock, flags); + + viopathStatus[remoteLp].users[subtype]--; + + mf_deallocateLpEvents( remoteLp,HvLpEvent_Type_VirtualIo, + numReq, + &viopath_donealloc, + &doneAllocParms ); + down(&Semaphore); + + for (i = 0, numOpen = 0; i < VIO_MAX_SUBTYPES; i++) { + numOpen += viopathStatus[remoteLp].users[i]; + } + + if ((viopathStatus[remoteLp].isOpen) && (numOpen == 0)) { + printk(KERN_INFO_VIO + "Closing connection to partition %d", remoteLp); + + HvCallEvent_closeLpEventPath(remoteLp, + HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].isOpen = 0; + viopathStatus[remoteLp].isActive = 0; + + for (i = 0; i < VIO_MAX_SUBTYPES; i++) { + atomic_set(&event_buffer_available[i], 0); + + for (i = 0; i < VIO_MAX_SUBTYPES; i += 4) { + free_page((unsigned long) event_buffer[i]); + } + } + + } + spin_unlock_irqrestore(&statuslock, flags); + return 0; +} + +void *vio_get_event_buffer(int subtype) +{ + subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; + if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) + return NULL; + + if (atomic_dec_if_positive(&event_buffer_available[subtype]) == 0) + return event_buffer[subtype]; + else + return NULL; +} + +void vio_free_event_buffer(int subtype, void *buffer) +{ + subtype = subtype >> VIOMAJOR_SUBTYPE_SHIFT; + if ((subtype < 0) || (subtype >= VIO_MAX_SUBTYPES)) { + printk(KERN_WARNING_VIO + "unexpected subtype %d freeing event buffer\n", + subtype); + return; + } + + if (atomic_read(&event_buffer_available[subtype]) != 0) { + printk(KERN_WARNING_VIO + "freeing unallocated event buffer, subtype %d\n", + subtype); + return; + } + + if (buffer != event_buffer[subtype]) { + printk(KERN_WARNING_VIO + "freeing invalid event buffer, subtype %d\n", + subtype); + } + + atomic_set(&event_buffer_available[subtype], 1); +} diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/iseries/viotape.c linuxppc64_2_4/drivers/iseries/viotape.c --- linux-2.4.9-ac10/drivers/iseries/viotape.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/iseries/viotape.c Thu Sep 27 14:00:26 2001 @@ -0,0 +1,1295 @@ +/* -*- linux-c -*- + * drivers/char/viotape.c + * + * iSeries Virtual Tape + *************************************************************************** + * + * Authors: Dave Boutcher + * Ryan Arnold + * Colin Devilbiss + * + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu 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 + * + *************************************************************************** + * This routine provides access to tape drives owned and managed by an OS/400 + * partition running on the same box as this Linux partition. + * + * All tape operations are performed by sending messages back and forth to + * the OS/400 partition. The format of the messages is defined in + * iSeries/vio.h + * + */ + + +#undef VIOT_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vio.h" +#include +#include "asm/iSeries/HvCallEvent.h" +#include "asm/iSeries/HvLpConfig.h" +#include + +extern struct pci_dev * iSeries_vio_dev; + +static int viotape_major = 230; +static int viotape_numdev = 0; + +#define VIOTAPE_MAXREQ 1 + +/* version number for viotape driver */ +static unsigned int version_major = 1; +static unsigned int version_minor = 0; + +static u64 sndMsgSeq; +static u64 sndMsgAck; +static u64 rcvMsgSeq; +static u64 rcvMsgAck; + +/*************************************************************************** + * The minor number follows the conventions of the SCSI tape drives. The + * rewind and mode are encoded in the minor #. We use this struct to break + * them out + ***************************************************************************/ +struct viot_devinfo_struct { + int major; + int minor; + int devno; + int mode; + int rewind; +}; + +#define VIOTAPOP_RESET 0 +#define VIOTAPOP_FSF 1 +#define VIOTAPOP_BSF 2 +#define VIOTAPOP_FSR 3 +#define VIOTAPOP_BSR 4 +#define VIOTAPOP_WEOF 5 +#define VIOTAPOP_REW 6 +#define VIOTAPOP_NOP 7 +#define VIOTAPOP_EOM 8 +#define VIOTAPOP_ERASE 9 +#define VIOTAPOP_SETBLK 10 +#define VIOTAPOP_SETDENSITY 11 +#define VIOTAPOP_SETPOS 12 +#define VIOTAPOP_GETPOS 13 +#define VIOTAPOP_SETPART 14 + +struct viotapelpevent { + struct HvLpEvent event; + u32 mReserved1; + u16 mVersion; + u16 mSubTypeRc; + u16 mTape; + u16 mFlags; + u32 mToken; + u64 mLen; + union { + struct { + u32 mTapeOp; + u32 mCount; + } tapeOp; + struct { + u32 mType; + u32 mResid; + u32 mDsreg; + u32 mGstat; + u32 mErreg; + u32 mFileNo; + u32 mBlkNo; + } getStatus; + struct { + u32 mBlkNo; + } getPos; + } u; +}; +enum viotapesubtype { + viotapeopen = 0x0001, + viotapeclose = 0x0002, + viotaperead = 0x0003, + viotapewrite = 0x0004, + viotapegetinfo = 0x0005, + viotapeop = 0x0006, + viotapegetpos = 0x0007, + viotapesetpos = 0x0008, + viotapegetstatus = 0x0009 +}; + +enum viotapeRc { + viotape_InvalidRange = 0x0601, + viotape_InvalidToken = 0x0602, + viotape_DMAError = 0x0603, + viotape_UseError = 0x0604, + viotape_ReleaseError = 0x0605, + viotape_InvalidTape = 0x0606, + viotape_InvalidOp = 0x0607, + viotape_TapeErr = 0x0608, + + viotape_AllocTimedOut = 0x0640, + viotape_BOTEnc = 0x0641, + viotape_BlankTape = 0x0642, + viotape_BufferEmpty = 0x0643, + viotape_CleanCartFound = 0x0644, + viotape_CmdNotAllowed = 0x0645, + viotape_CmdNotSupported = 0x0646, + viotape_DataCheck = 0x0647, + viotape_DecompressErr = 0x0648, + viotape_DeviceTimeout = 0x0649, + viotape_DeviceUnavail = 0x064a, + viotape_DeviceBusy = 0x064b, + viotape_EndOfMedia = 0x064c, + viotape_EndOfTape = 0x064d, + viotape_EquipCheck = 0x064e, + viotape_InsufficientRs = 0x064f, + viotape_InvalidLogBlk = 0x0650, + viotape_LengthError = 0x0651, + viotape_LibDoorOpen = 0x0652, + viotape_LoadFailure = 0x0653, + viotape_NotCapable = 0x0654, + viotape_NotOperational = 0x0655, + viotape_NotReady = 0x0656, + viotape_OpCancelled = 0x0657, + viotape_PhyLinkErr = 0x0658, + viotape_RdyNotBOT = 0x0659, + viotape_TapeMark = 0x065a, + viotape_WriteProt = 0x065b +}; + +/* Maximum # tapes we support + */ +#define VIOTAPE_MAX_TAPE 8 +#define MAX_PARTITIONS 4 + +/* defines for current tape state */ +#define VIOT_IDLE 0 +#define VIOT_READING 1 +#define VIOT_WRITING 2 + +/* Our info on the tapes + */ +struct tape_descr { + char rsrcname[10]; + char type[4]; + char model[3]; +}; + +static struct tape_descr *viotape_unitinfo = NULL; + +static char *lasterr[VIOTAPE_MAX_TAPE]; + +static struct mtget viomtget[VIOTAPE_MAX_TAPE]; + +/* maintain the current state of each tape (and partition) + so that we know when to write EOF marks. +*/ +static struct { + unsigned char cur_part; + devfs_handle_t dev_handle; + struct { + unsigned char rwi; + } part_stat[MAX_PARTITIONS]; +} state[VIOTAPE_MAX_TAPE]; + +/* We single-thread + */ +static struct semaphore reqSem; + +/* When we send a request, we use this struct to get the response back + * from the interrupt handler + */ +struct opStruct { + void *buffer; + dma_addr_t dmaaddr; + size_t count; + int rc; + struct semaphore *sem; + struct opStruct *free; +}; + +static spinlock_t opStructListLock; +static struct opStruct *opStructList; + +/* forward declaration to resolve interdependence */ +static int chg_state(int index, unsigned char new_state, + struct file *file); + +/* Decode the kdev_t into its parts + */ +void getDevInfo(kdev_t dev, struct viot_devinfo_struct *devi) +{ + devi->major = MAJOR(dev); + devi->minor = MINOR(dev); + devi->devno = devi->minor & 0x1F; + devi->mode = (devi->minor & 0x60) >> 5; + /* if bit is set in the minor, do _not_ rewind automatically */ + devi->rewind = !(devi->minor & 0x80); +} + + +/* Allocate an op structure from our pool + */ +static struct opStruct *getOpStruct(void) +{ + struct opStruct *newOpStruct; + spin_lock(&opStructListLock); + + if (opStructList == NULL) { + newOpStruct = kmalloc(sizeof(struct opStruct), GFP_KERNEL); + } else { + newOpStruct = opStructList; + opStructList = opStructList->free; + } + + if (newOpStruct) + memset(newOpStruct, 0x00, sizeof(struct opStruct)); + + spin_unlock(&opStructListLock); + + return newOpStruct; +} + +/* Return an op structure to our pool + */ +static void freeOpStruct(struct opStruct *opStruct) +{ + spin_lock(&opStructListLock); + opStruct->free = opStructList; + opStructList = opStruct; + spin_unlock(&opStructListLock); +} + +/* Map our tape return codes to errno values + */ +int tapeRcToErrno(int tapeRc, char *operation, int tapeno) +{ + int terrno; + char *tmsg; + + switch (tapeRc) { + case 0: + return 0; + case viotape_InvalidRange: + terrno = EIO; + tmsg = "Internal error"; + break; + case viotape_InvalidToken: + terrno = EIO; + tmsg = "Internal error"; + break; + case viotape_DMAError: + terrno = EIO; + tmsg = "DMA error"; + break; + case viotape_UseError: + terrno = EIO; + tmsg = "Internal error"; + break; + case viotape_ReleaseError: + terrno = EIO; + tmsg = "Internal error"; + break; + case viotape_InvalidTape: + terrno = EIO; + tmsg = "Invalid tape device"; + break; + case viotape_InvalidOp: + terrno = EIO; + tmsg = "Invalid operation"; + break; + case viotape_TapeErr: + terrno = EIO; + tmsg = "Tape error"; + break; + + case viotape_AllocTimedOut: + terrno = EBUSY; + tmsg = "Allocate timed out"; + break; + case viotape_BOTEnc: + terrno = EIO; + tmsg = "Beginning of tape encountered"; + break; + case viotape_BlankTape: + terrno = EIO; + tmsg = "Blank tape"; + break; + case viotape_BufferEmpty: + terrno = EIO; + tmsg = "Buffer empty"; + break; + case viotape_CleanCartFound: + terrno = ENOMEDIUM; + tmsg = "Cleaning cartridge found"; + break; + case viotape_CmdNotAllowed: + terrno = EIO; + tmsg = "Command not allowed"; + break; + case viotape_CmdNotSupported: + terrno = EIO; + tmsg = "Command not supported"; + break; + case viotape_DataCheck: + terrno = EIO; + tmsg = "Data check"; + break; + case viotape_DecompressErr: + terrno = EIO; + tmsg = "Decompression error"; + break; + case viotape_DeviceTimeout: + terrno = EBUSY; + tmsg = "Device timeout"; + break; + case viotape_DeviceUnavail: + terrno = EIO; + tmsg = "Device unavailable"; + break; + case viotape_DeviceBusy: + terrno = EBUSY; + tmsg = "Device busy"; + break; + case viotape_EndOfMedia: + terrno = ENOSPC; + tmsg = "End of media"; + break; + case viotape_EndOfTape: + terrno = ENOSPC; + tmsg = "End of tape"; + break; + case viotape_EquipCheck: + terrno = EIO; + tmsg = "Equipment check"; + break; + case viotape_InsufficientRs: + terrno = EOVERFLOW; + tmsg = "Insufficient tape resources"; + break; + case viotape_InvalidLogBlk: + terrno = EIO; + tmsg = "Invalid logical block location"; + break; + case viotape_LengthError: + terrno = EOVERFLOW; + tmsg = "Length error"; + break; + case viotape_LibDoorOpen: + terrno = EBUSY; + tmsg = "Door open"; + break; + case viotape_LoadFailure: + terrno = ENOMEDIUM; + tmsg = "Load failure"; + break; + case viotape_NotCapable: + terrno = EIO; + tmsg = "Not capable"; + break; + case viotape_NotOperational: + terrno = EIO; + tmsg = "Not operational"; + break; + case viotape_NotReady: + terrno = EIO; + tmsg = "Not ready"; + break; + case viotape_OpCancelled: + terrno = EIO; + tmsg = "Operation cancelled"; + break; + case viotape_PhyLinkErr: + terrno = EIO; + tmsg = "Physical link error"; + break; + case viotape_RdyNotBOT: + terrno = EIO; + tmsg = "Ready but not beginning of tape"; + break; + case viotape_TapeMark: + terrno = EIO; + tmsg = "Tape mark"; + break; + case viotape_WriteProt: + terrno = EROFS; + tmsg = "Write protection error"; + break; + default: + terrno = EIO; + tmsg = "I/O error"; + } + + printk(KERN_WARNING_VIO "tape error on Device %d (%10.10s): %s\n", + tapeno, viotape_unitinfo[tapeno].rsrcname, tmsg); + + lasterr[tapeno] = tmsg; + + return -terrno; +} + +/* Handle reads from the proc file system. + */ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + int len = 0; + int i; + + len += sprintf(buf + len, "viotape driver version %d.%d\n", + version_major, version_minor); + + for (i = 0; i < viotape_numdev; i++) { + + len += + sprintf(buf + len, + "viotape device %d is iSeries resource %10.10s type %4.4s, model %3.3s\n", + i, viotape_unitinfo[i].rsrcname, + viotape_unitinfo[i].type, + viotape_unitinfo[i].model); + if (lasterr[i]) + len += + sprintf(buf + len, " last error: %s\n", + lasterr[i]); + } + + *eof = 1; + return len; +} + +/* setup our proc file system entries + */ +void viotape_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = + create_proc_entry("viotape", S_IFREG | S_IRUSR, iSeries_proc); + if (!ent) + return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; +} + +/* clean up our proc file system entries + */ +void viotape_proc_delete(struct proc_dir_entry *iSeries_proc) +{ + remove_proc_entry("viotape", iSeries_proc); +} + + +/* Get info on all tapes from OS/400 + */ +static void get_viotape_info(void) +{ + dma_addr_t dmaaddr; + HvLpEvent_Rc hvrc; + int i; + struct opStruct *op = getOpStruct(); + DECLARE_MUTEX_LOCKED(Semaphore); + if (op == NULL) + return; + + if (viotape_unitinfo == NULL) { + viotape_unitinfo = + kmalloc(sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE, + GFP_KERNEL); + } + memset(viotape_unitinfo, 0x00, + sizeof(struct tape_descr) * VIOTAPE_MAX_TAPE); + memset(lasterr, 0x00, sizeof(lasterr)); + + op->sem = &Semaphore; + + dmaaddr = pci_map_single(iSeries_vio_dev, viotape_unitinfo, + sizeof(struct tape_descr) * + VIOTAPE_MAX_TAPE, PCI_DMA_FROMDEVICE); + if (dmaaddr == 0xFFFFFFFF) { + printk(KERN_WARNING_VIO "viotape error allocating tce\n"); + return; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotapegetinfo, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) op, + VIOVERSION << 16, dmaaddr, + sizeof(struct tape_descr) * + VIOTAPE_MAX_TAPE, 0, 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk("viotape hv error on op %d\n", (int) hvrc); + } + + down(&Semaphore); + + freeOpStruct(op); + + + for (i = 0; + ((i < VIOTAPE_MAX_TAPE) && (viotape_unitinfo[i].rsrcname[0])); + i++) { + printk("found a tape %10.10s\n", + viotape_unitinfo[i].rsrcname); + viotape_numdev++; + } +} + + +/* Write + */ +static ssize_t viotap_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + struct viot_devinfo_struct devi; + DECLARE_MUTEX_LOCKED(Semaphore); + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) { + if (down_trylock(&reqSem)) { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } else { + down(&reqSem); + } + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(iSeries_vio_dev, count, &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) { + printk(KERN_WARNING_VIO + "tape error allocating dma buffer for len %ld\n", + count); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + /* Copy the data into the buffer */ + err = copy_from_user(op->buffer, (const void *) buf, count); + if (err) { + printk(KERN_WARNING_VIO + "tape: error on copy from user\n"); + pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + if (noblock) { + op->sem = NULL; + } else { + op->sem = &Semaphore; + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotapewrite, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) op, + VIOVERSION << 16, + ((u64) devi. + devno << 48) | op->dmaaddr, + count, 0, 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk("viotape hv error on op %d\n", (int) hvrc); + pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + if (noblock) + return count; + + down(&Semaphore); + + err = op->rc; + + /* Free the buffer */ + pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr); + + count = op->count; + + freeOpStruct(op); + up(&reqSem); + if (err) + return tapeRcToErrno(err, "write", devi.devno); + else { + chg_state(devi.devno, VIOT_WRITING, file); + return count; + } +} + +/* read + */ +static ssize_t viotap_read(struct file *file, char *buf, size_t count, + loff_t * ptr) +{ + HvLpEvent_Rc hvrc; + kdev_t dev = file->f_dentry->d_inode->i_rdev; + unsigned short flags = file->f_flags; + struct opStruct *op = getOpStruct(); + int noblock = ((flags & O_NONBLOCK) != 0); + int err; + struct viot_devinfo_struct devi; + DECLARE_MUTEX_LOCKED(Semaphore); + + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + /* We need to make sure we can send a request. We use + * a semaphore to keep track of # requests in use. If + * we are non-blocking, make sure we don't block on the + * semaphore + */ + if (noblock) { + if (down_trylock(&reqSem)) { + freeOpStruct(op); + return -EWOULDBLOCK; + } + } else { + down(&reqSem); + } + + chg_state(devi.devno, VIOT_READING, file); + + /* Allocate a DMA buffer */ + op->buffer = pci_alloc_consistent(iSeries_vio_dev, count, &op->dmaaddr); + + if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + op->count = count; + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotaperead, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) op, + VIOVERSION << 16, + ((u64) devi. + devno << 48) | op->dmaaddr, + count, 0, 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk(KERN_WARNING_VIO + "tape hv error on op %d\n", (int) hvrc); + pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + + down(&Semaphore); + + if (op->rc == 0) { + /* If we got data back */ + if (op->count) { + /* Copy the data into the buffer */ + err = copy_to_user(buf, op->buffer, count); + if (err) { + printk("error on copy_to_user\n"); + pci_free_consistent(iSeries_vio_dev, count, + op->buffer, + op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + } + } + + err = op->rc; + + /* Free the buffer */ + pci_free_consistent(iSeries_vio_dev, count, op->buffer, op->dmaaddr); + count = op->count; + + freeOpStruct(op); + up(&reqSem); + if (err) + return tapeRcToErrno(err, "read", devi.devno); + else + return count; +} + +/* read + */ +static int viotap_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + HvLpEvent_Rc hvrc; + int err; + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + struct opStruct *op = getOpStruct(); + struct viot_devinfo_struct devi; + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + + down(&reqSem); + + switch (cmd) { + case MTIOCTOP:{ + struct mtop mtc; + u32 myOp; + + /* inode is null if and only if we (the kernel) made the request */ + if (inode == NULL) + memcpy(&mtc, (void *) arg, + sizeof(struct mtop)); + else if (copy_from_user + ((char *) &mtc, (char *) arg, + sizeof(struct mtop))) { + freeOpStruct(op); + up(&reqSem); + return -EFAULT; + } + + switch (mtc.mt_op) { + case MTRESET: + myOp = VIOTAPOP_RESET; + break; + case MTFSF: + myOp = VIOTAPOP_FSF; + break; + case MTBSF: + myOp = VIOTAPOP_BSF; + break; + case MTFSR: + myOp = VIOTAPOP_FSR; + break; + case MTBSR: + myOp = VIOTAPOP_BSR; + break; + case MTWEOF: + myOp = VIOTAPOP_WEOF; + break; + case MTREW: + myOp = VIOTAPOP_REW; + break; + case MTNOP: + myOp = VIOTAPOP_NOP; + break; + case MTEOM: + myOp = VIOTAPOP_EOM; + break; + case MTERASE: + myOp = VIOTAPOP_ERASE; + break; + case MTSETBLK: + myOp = VIOTAPOP_SETBLK; + break; + case MTSETDENSITY: + myOp = VIOTAPOP_SETDENSITY; + break; + case MTTELL: + myOp = VIOTAPOP_GETPOS; + break; + case MTSEEK: + myOp = VIOTAPOP_SETPOS; + break; + case MTSETPART: + myOp = VIOTAPOP_SETPART; + break; + default: + return -EIO; + } + +/* if we moved the head, we are no longer reading or writing */ + switch (mtc.mt_op) { + case MTFSF: + case MTBSF: + case MTFSR: + case MTBSR: + case MTTELL: + case MTSEEK: + case MTREW: + chg_state(devi.devno, VIOT_IDLE, file); + } + + op->sem = &Semaphore; + hvrc = + HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape + | viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned + long) op, + VIOVERSION << 16, + ((u64) devi. + devno << 48), 0, + (((u64) myOp) << + 32) | mtc. + mt_count, 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk("viotape hv error on op %d\n", + (int) hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + if (op->rc) { + freeOpStruct(op); + up(&reqSem); + return tapeRcToErrno(op->rc, + "tape operation", + devi.devno); + } else { + freeOpStruct(op); + up(&reqSem); + return 0; + } + break; + } + + case MTIOCGET: + op->sem = &Semaphore; + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotapegetstatus, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) + op, VIOVERSION << 16, + ((u64) devi. + devno << 48), 0, 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) { + printk("viotape hv error on op %d\n", (int) hvrc); + freeOpStruct(op); + up(&reqSem); + return -EIO; + } + down(&Semaphore); + up(&reqSem); + if (op->rc) { + freeOpStruct(op); + return tapeRcToErrno(op->rc, "get status", + devi.devno); + } else { + freeOpStruct(op); + err = + copy_to_user((void *) arg, &viomtget[dev], + sizeof(viomtget[0])); + if (err) { + freeOpStruct(op); + return -EFAULT; + } + return 0; + } + break; + case MTIOCPOS: + printk("Got an MTIOCPOS\n"); + default: + return -ENOSYS; + } + return 0; +} + +/* Open + */ +static int viotap_open(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct opStruct *op = getOpStruct(); + struct viot_devinfo_struct devi; + if (op == NULL) + return -ENOMEM; + + getDevInfo(dev, &devi); + +// Note: We currently only support one mode! + if ((devi.devno >= viotape_numdev) || (devi.mode)) { + freeOpStruct(op); + return -ENODEV; + } + + op->sem = &Semaphore; + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotapeopen, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) op, + VIOVERSION << 16, + ((u64) devi.devno << 48), 0, + 0, 0); + + + if (hvrc != 0) { + printk("viotape bad rc on signalLpEvent %d\n", (int) hvrc); + freeOpStruct(op); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) { + freeOpStruct(op); + return tapeRcToErrno(op->rc, "open", devi.devno); + } else { + freeOpStruct(op); + MOD_INC_USE_COUNT; + return 0; + } +} + + +/* Release + */ +static int viotap_release(struct inode *inode, struct file *file) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + kdev_t dev = file->f_dentry->d_inode->i_rdev; + HvLpEvent_Rc hvrc; + struct viot_devinfo_struct devi; + struct opStruct *op = getOpStruct(); + + if (op == NULL) + return -ENOMEM; + op->sem = &Semaphore; + + getDevInfo(dev, &devi); + + if (devi.devno >= viotape_numdev) { + freeOpStruct(op); + return -ENODEV; + } + + chg_state(devi.devno, VIOT_IDLE, file); + + if (devi.rewind) { + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotapeop, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) + op, VIOVERSION << 16, + ((u64) devi. + devno << 48), 0, + ((u64) VIOTAPOP_REW) + << 32, 0); + down(&Semaphore); + + if (op->rc) { + tapeRcToErrno(op->rc, "rewind", devi.devno); + } + } + + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_tape | + viotapeclose, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst + (viopath_hostLp), + viopath_targetinst + (viopath_hostLp), + (u64) (unsigned long) op, + VIOVERSION << 16, + ((u64) devi.devno << 48), 0, + 0, 0); + + + if (hvrc != 0) { + printk("viotape: bad rc on signalLpEvent %d\n", + (int) hvrc); + return -EIO; + } + + down(&Semaphore); + + if (op->rc) { + printk("viotape: close failed\n"); + } + MOD_DEC_USE_COUNT; + return 0; +} + +struct file_operations viotap_fops = { + owner:THIS_MODULE, + read:viotap_read, + write:viotap_write, + ioctl:viotap_ioctl, + open:viotap_open, + release:viotap_release, +}; + +/* Handle interrupt events for tape + */ +static void vioHandleTapeEvent(struct HvLpEvent *event) +{ + int tapeminor; + struct opStruct *op; + struct viotapelpevent *tevent = (struct viotapelpevent *) event; + + if (event == NULL) { + /* Notification that a partition went away! */ + if (!viopath_isactive(viopath_hostLp)) { + /* TODO! Clean up */ + } + return; + } + + tapeminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; + switch (tapeminor) { + case viotapegetinfo: + case viotapeopen: + case viotapeclose: + op = (struct opStruct *) (unsigned long) event-> + xCorrelationToken; + op->rc = tevent->mSubTypeRc; + up(op->sem); + break; + case viotaperead: + case viotapewrite: + op = (struct opStruct *) (unsigned long) event-> + xCorrelationToken; + op->rc = tevent->mSubTypeRc;; + op->count = tevent->mLen; + + if (op->sem) { + up(op->sem); + } else { + freeOpStruct(op); + up(&reqSem); + } + break; + case viotapeop: + case viotapegetpos: + case viotapesetpos: + case viotapegetstatus: + op = (struct opStruct *) (unsigned long) event-> + xCorrelationToken; + if (op) { + op->count = tevent->u.tapeOp.mCount; + op->rc = tevent->mSubTypeRc;; + + if (op->sem) { + up(op->sem); + } + } + break; + default: + printk("viotape: wierd ack\n"); + } +} + + +/* Do initialization + */ +int __init viotap_init(void) +{ + DECLARE_MUTEX_LOCKED(Semaphore); + int rc; + char tapename[32]; + int i; + + printk("viotape driver version %d.%d\n", version_major, + version_minor); + + sndMsgSeq = sndMsgAck = 0; + rcvMsgSeq = rcvMsgAck = 0; + opStructList = NULL; + spin_lock_init(&opStructListLock); + + sema_init(&reqSem, VIOTAPE_MAXREQ); + + if (viopath_hostLp == HvLpIndexInvalid) + vio_set_hostlp(); + + /* + * Open to our hosting lp + */ + if (viopath_hostLp == HvLpIndexInvalid) + return -1; + + printk("viotape: init - open path to hosting (%d)\n", + viopath_hostLp); + + rc = viopath_open(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); + if (rc) { + printk("viotape: error on viopath_open to hostlp %d\n", + rc); + } + + vio_setHandler(viomajorsubtype_tape, vioHandleTapeEvent); + + printk("viotape major is %d\n", viotape_major); + + get_viotape_info(); + + if (devfs_register_chrdev(viotape_major, "viotape", &viotap_fops)) { + printk("Error registering viotape device\n"); + return -1; + } + + for (i = 0; i < viotape_numdev; i++) { + int j; + state[i].cur_part = 0; + for (j = 0; j < MAX_PARTITIONS; ++j) + state[i].part_stat[j].rwi = VIOT_IDLE; + sprintf(tapename, "viotape%d", i); + state[i].dev_handle = + devfs_register(NULL, tapename, DEVFS_FL_DEFAULT, + viotape_major, i, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | + S_IWGRP, &viotap_fops, NULL); + printk + ("viotape device %s is iSeries resource %10.10s type %4.4s, model %3.3s\n", + tapename, viotape_unitinfo[i].rsrcname, + viotape_unitinfo[i].type, viotape_unitinfo[i].model); + } + + /* + * Create the proc entry + */ + iSeries_proc_callback(&viotape_proc_init); + + return 0; +} + +/* Give a new state to the tape object + */ +static int chg_state(int index, unsigned char new_state, struct file *file) +{ + unsigned char *cur_state = + &state[index].part_stat[state[index].cur_part].rwi; + int rc = 0; + + /* if the same state, don't bother */ + if (*cur_state == new_state) + return 0; + + /* write an EOF if changing from writing to some other state */ + if (*cur_state == VIOT_WRITING) { + struct mtop write_eof = { MTWEOF, 1 }; + rc = viotap_ioctl(NULL, file, MTIOCTOP, + (unsigned long) &write_eof); + } + *cur_state = new_state; + return rc; +} + +/* Cleanup + */ +static void __exit viotap_exit(void) +{ + int i, ret; + for (i = 0; i < viotape_numdev; ++i) + devfs_unregister(state[i].dev_handle); + ret = devfs_unregister_chrdev(viotape_major, "viotape"); + if (ret < 0) + printk("Error unregistering device: %d\n", ret); + iSeries_proc_callback(&viotape_proc_delete); + if (viotape_unitinfo != NULL) { + kfree(viotape_unitinfo); + viotape_unitinfo = NULL; + } + viopath_close(viopath_hostLp, viomajorsubtype_tape, VIOTAPE_MAXREQ + 2); + vio_clearHandler(viomajorsubtype_tape); +} + +module_init(viotap_init); +module_exit(viotap_exit); diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/net/Config.in linuxppc64_2_4/drivers/net/Config.in --- linux-2.4.9-ac10/drivers/net/Config.in Wed Oct 3 12:11:19 2001 +++ linuxppc64_2_4/drivers/net/Config.in Thu Sep 27 14:00:27 2001 @@ -236,10 +236,6 @@ endmenu -if [ "$CONFIG_PPC_ISERIES" = "y" ]; then - dep_tristate 'iSeries Virtual Ethernet driver support' CONFIG_VETH $CONFIG_PPC_ISERIES -fi - bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" ]; then diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/net/Makefile linuxppc64_2_4/drivers/net/Makefile --- linux-2.4.9-ac10/drivers/net/Makefile Wed Oct 3 12:11:19 2001 +++ linuxppc64_2_4/drivers/net/Makefile Thu Sep 27 14:00:27 2001 @@ -73,7 +73,6 @@ obj-$(CONFIG_DM9102) += dmfe.o obj-$(CONFIG_YELLOWFIN) += yellowfin.o obj-$(CONFIG_ACENIC) += acenic.o -obj-$(CONFIG_VETH) += veth.o obj-$(CONFIG_NATSEMI) += natsemi.o obj-$(CONFIG_NS83820) += ns83820.o obj-$(CONFIG_STNIC) += stnic.o 8390.o diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/net/Space.c linuxppc64_2_4/drivers/net/Space.c --- linux-2.4.9-ac10/drivers/net/Space.c Wed Oct 3 12:11:19 2001 +++ linuxppc64_2_4/drivers/net/Space.c Tue Sep 4 14:09:18 2001 @@ -537,9 +537,10 @@ -#ifdef CONFIG_TR +#if 0 /* ifdef CONFIG_TR */ /* Token-ring device probe */ extern int ibmtr_probe(struct net_device *); +extern int olympic_probe(struct net_device *); extern int smctr_probe(struct net_device *); static int @@ -548,6 +549,9 @@ if (1 #ifdef CONFIG_IBMTR && ibmtr_probe(dev) +#endif +#ifdef CONFIG_IBMOL + && olympic_probe(dev) #endif #ifdef CONFIG_SMCTR && smctr_probe(dev) diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/net/acenic.c linuxppc64_2_4/drivers/net/acenic.c --- linux-2.4.9-ac10/drivers/net/acenic.c Wed Oct 3 12:11:19 2001 +++ linuxppc64_2_4/drivers/net/acenic.c Fri Sep 28 15:44:30 2001 @@ -120,30 +120,6 @@ #define PCI_DEVICE_ID_SGI_ACENIC 0x0009 #endif -#if LINUX_VERSION_CODE >= 0x20400 -static struct pci_device_id acenic_pci_tbl[] __initdata = { - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - /* - * Farallon used the DEC vendor ID on their cards incorrectly. - */ - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { } -}; -MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); -#endif - #ifndef wmb #define wmb() mb() @@ -521,230 +497,188 @@ "acenic.c: v0.81 04/20/2001 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static struct net_device *root_dev; - -static int probed __initdata = 0; - - -int __devinit acenic_probe (ACE_PROBE_ARG) +static int __devinit acenic_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { -#ifdef NEW_NETINIT struct net_device *dev; -#endif - struct ace_private *ap; - struct pci_dev *pdev = NULL; int boards_found = 0; int version_disp; - if (probed) - return -ENODEV; - probed++; - if (!pci_present()) /* is PCI support present? */ return -ENODEV; version_disp = 0; - while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) { + dev = init_etherdev(NULL, sizeof(struct ace_private)); - if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) && - ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) || - (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&& - !((pdev->vendor == PCI_VENDOR_ID_3COM) && - (pdev->device == PCI_DEVICE_ID_3COM_3C985)) && - !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) && - ((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) || - (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) && - /* - * Farallon used the DEC vendor ID on their cards by - * mistake for a while - */ - !((pdev->vendor == PCI_VENDOR_ID_DEC) && - (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) && - !((pdev->vendor == PCI_VENDOR_ID_SGI) && - (pdev->device == PCI_DEVICE_ID_SGI_ACENIC))) - continue; - - dev = init_etherdev(NULL, sizeof(struct ace_private)); - - if (dev == NULL) { - printk(KERN_ERR "acenic: Unable to allocate " - "net_device structure!\n"); - break; - } + if (dev == NULL) { + printk(KERN_ERR "acenic: Unable to allocate " + "net_device structure!\n"); + return -ENODEV; + } - SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(dev); - if (!dev->priv) - dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); - if (!dev->priv) { - printk(KERN_ERR "acenic: Unable to allocate memory\n"); - return -ENOMEM; - } - - ap = dev->priv; - ap->pdev = pdev; - - dev->irq = pdev->irq; - dev->open = &ace_open; - dev->hard_start_xmit = &ace_start_xmit; - dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; - if (1) { - static void ace_watchdog(struct net_device *dev); - dev->tx_timeout = &ace_watchdog; - dev->watchdog_timeo = 5*HZ; - } - dev->stop = &ace_close; - dev->get_stats = &ace_get_stats; - dev->set_multicast_list = &ace_set_multicast_list; - dev->do_ioctl = &ace_ioctl; - dev->set_mac_address = &ace_set_mac_addr; - dev->change_mtu = &ace_change_mtu; + if (!dev->priv) + dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); + if (!dev->priv) { + printk(KERN_ERR "acenic: Unable to allocate memory\n"); + return -ENOMEM; + } - /* display version info if adapter is found */ - if (!version_disp) - { - /* set display flag to TRUE so that */ - /* we only display this string ONCE */ - version_disp = 1; - printk(version); - } + ap = dev->priv; + ap->pdev = pdev; - /* - * Enable master mode before we start playing with the - * pci_command word since pci_set_master() will modify - * it. - */ - pci_set_master(pdev); + dev->irq = pdev->irq; + dev->open = &ace_open; + dev->hard_start_xmit = &ace_start_xmit; + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; + if (1) { + static void ace_watchdog(struct net_device *dev); + dev->tx_timeout = &ace_watchdog; + dev->watchdog_timeo = 5*HZ; + } + dev->stop = &ace_close; + dev->get_stats = &ace_get_stats; + dev->set_multicast_list = &ace_set_multicast_list; + dev->do_ioctl = &ace_ioctl; + dev->set_mac_address = &ace_set_mac_addr; + dev->change_mtu = &ace_change_mtu; - pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); + /* display version info if adapter is found */ + if (!version_disp) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + version_disp = 1; + printk(version); + } - /* OpenFirmware on Mac's does not set this - DOH.. */ - if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { - printk(KERN_INFO "%s: Enabling PCI Memory Mapped " - "access - was not enabled by BIOS/Firmware\n", - dev->name); - ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; - pci_write_config_word(ap->pdev, PCI_COMMAND, - ap->pci_command); - wmb(); - } + /* + * Enable master mode before we start playing with the + * pci_command word since pci_set_master() will modify + * it. + */ + pci_set_master(pdev); - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, - &ap->pci_latency); - if (ap->pci_latency <= 0x40) { - ap->pci_latency = 0x40; - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, - ap->pci_latency); - } + pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); - /* - * Remap the regs into kernel space - this is abuse of - * dev->base_addr since it was means for I/O port - * addresses but who gives a damn. - */ - dev->base_addr = pci_resource_start(pdev, 0); - ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); - if (!ap->regs) { - printk(KERN_ERR "%s: Unable to map I/O register, " - "AceNIC %i will be disabled.\n", - dev->name, boards_found); - break; - } + /* OpenFirmware on Mac's does not set this - DOH.. */ + if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { + printk(KERN_INFO "%s: Enabling PCI Memory Mapped " + "access - was not enabled by BIOS/Firmware\n", + dev->name); + ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; + pci_write_config_word(ap->pdev, PCI_COMMAND, + ap->pci_command); + wmb(); + } - switch(pdev->vendor) { - case PCI_VENDOR_ID_ALTEON: - strncpy(ap->name, "AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); - break; - case PCI_VENDOR_ID_3COM: - strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); - break; - case PCI_VENDOR_ID_NETGEAR: - strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: NetGear GA620 ", dev->name); - break; - case PCI_VENDOR_ID_DEC: - if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { - strncpy(ap->name, "Farallon PN9000-SX " - "Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Farallon PN9000-SX ", - dev->name); - break; - } - case PCI_VENDOR_ID_SGI: - strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: SGI AceNIC ", dev->name); - break; - default: - strncpy(ap->name, "Unknown AceNIC based Gigabit " - "Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, + &ap->pci_latency); + if (ap->pci_latency <= 0x40) { + ap->pci_latency = 0x40; + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + ap->pci_latency); + } + + /* + * Remap the regs into kernel space - this is abuse of + * dev->base_addr since it was means for I/O port + * addresses but who gives a damn. + */ + dev->base_addr = pci_resource_start(pdev, 0); + ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); + if (!ap->regs) { + printk(KERN_ERR "%s: Unable to map I/O register, " + "AceNIC %i will be disabled.\n", + dev->name, boards_found); + return -ENODEV; + } + + switch(pdev->vendor) { + case PCI_VENDOR_ID_ALTEON: + strncpy(ap->name, "AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); + break; + case PCI_VENDOR_ID_3COM: + strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); + break; + case PCI_VENDOR_ID_NETGEAR: + strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: NetGear GA620 ", dev->name); + break; + case PCI_VENDOR_ID_DEC: + if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { + strncpy(ap->name, "Farallon PN9000-SX " + "Gigabit Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Farallon PN9000-SX ", + dev->name); break; } - ap->name [sizeof (ap->name) - 1] = '\0'; - printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); + case PCI_VENDOR_ID_SGI: + strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: SGI AceNIC ", dev->name); + break; + default: + strncpy(ap->name, "Unknown AceNIC based Gigabit " + "Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + break; + } + ap->name [sizeof (ap->name) - 1] = '\0'; + printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); #ifdef __sparc__ - printk("irq %s\n", __irq_itoa(dev->irq)); + printk("irq %s\n", __irq_itoa(dev->irq)); #else - printk("irq %i\n", dev->irq); + printk("irq %i\n", dev->irq); #endif #ifdef CONFIG_ACENIC_OMIT_TIGON_I - if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { - printk(KERN_ERR "%s: Driver compiled without Tigon I" - " support - NIC disabled\n", dev->name); - ace_init_cleanup(dev); - kfree(dev); - continue; - } + if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { + printk(KERN_ERR "%s: Driver compiled without Tigon I" + " support - NIC disabled\n", dev->name); + ace_init_cleanup(dev); + kfree(dev); + return -ENODEV; + } #endif - if (ace_allocate_descriptors(dev)) { - /* - * ace_allocate_descriptors() calls - * ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + if (ace_allocate_descriptors(dev)) { + /* + * ace_allocate_descriptors() calls + * ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; + } #ifdef MODULE - if (boards_found >= ACE_MAX_MOD_PARMS) - ap->board_idx = BOARD_IDX_OVERFLOW; - else - ap->board_idx = boards_found; + if (boards_found >= ACE_MAX_MOD_PARMS) + ap->board_idx = BOARD_IDX_OVERFLOW; + else + ap->board_idx = boards_found; #else - ap->board_idx = BOARD_IDX_STATIC; + ap->board_idx = BOARD_IDX_STATIC; #endif - if (ace_init(dev)) { - /* - * ace_init() calls ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + pdev->driver_data = dev; - boards_found++; + if (ace_init(dev)) { + /* + * ace_init() calls ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; } - /* - * If we're at this point we're going through ace_probe() for - * the first time. Return success (0) if we've initialized 1 - * or more boards. Otherwise, return failure (-ENODEV). - */ - - if (boards_found > 0) - return 0; - else - return -ENODEV; + return 0; } @@ -759,136 +693,95 @@ MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); #endif - -static void __exit ace_module_cleanup(void) +static void __devexit acenic_remove_one(struct pci_dev *pdev) { + struct net_device *dev = pdev->driver_data; struct ace_private *ap; struct ace_regs *regs; - struct net_device *next; short i; - while (root_dev) { - ap = root_dev->priv; - next = ap->next; - - regs = ap->regs; + ap = dev->priv; + regs = ap->regs; - writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); - if (ap->version >= 2) - writel(readl(®s->CpuBCtrl) | CPU_HALT, - ®s->CpuBCtrl); - /* - * This clears any pending interrupts - */ - writel(1, ®s->Mb0Lo); + writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); + if (ap->version >= 2) + writel(readl(®s->CpuBCtrl) | CPU_HALT, + ®s->CpuBCtrl); + /* + * This clears any pending interrupts + */ + writel(1, ®s->Mb0Lo); - /* - * Make sure no other CPUs are processing interrupts - * on the card before the buffers are being released. - * Otherwise one might experience some `interesting' - * effects. - * - * Then release the RX buffers - jumbo buffers were - * already released in ace_close(). - */ - synchronize_irq(); + /* + * Make sure no other CPUs are processing interrupts + * on the card before the buffers are being released. + * Otherwise one might experience some `interesting' + * effects. + * + * Then release the RX buffers - jumbo buffers were + * already released in ace_close(). + */ + synchronize_irq(); - for (i = 0; i < RX_STD_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; + for (i = 0; i < RX_STD_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; - if (skb) { + if (skb) { #ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + dma_addr_t mapping; - mapping = ap->skb->rx_std_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_STD_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); + mapping = ap->skb->rx_std_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_STD_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - ap->rx_std_ring[i].size = 0; - ap->skb->rx_std_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } + ap->rx_std_ring[i].size = 0; + ap->skb->rx_std_skbuff[i].skb = NULL; + dev_kfree_skb(skb); } - if (ap->version >= 2) { - for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - - if (skb) { -#ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + } + if (ap->version >= 2) { + for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - mapping = ap->skb->rx_mini_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_MINI_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); -#endif - ap->rx_mini_ring[i].size = 0; - ap->skb->rx_mini_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } - } - } - for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; if (skb) { #ifndef DUMMY_PCI_UNMAP dma_addr_t mapping; - mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + mapping = ap->skb->rx_mini_skbuff[i].mapping; pci_unmap_single(ap->pdev, mapping, - ACE_JUMBO_BUFSIZE - (2 + 16), + ACE_MINI_BUFSIZE - (2 + 16), PCI_DMA_FROMDEVICE); #endif - - ap->rx_jumbo_ring[i].size = 0; - ap->skb->rx_jumbo_skbuff[i].skb = NULL; + ap->rx_mini_ring[i].size = 0; + ap->skb->rx_mini_skbuff[i].skb = NULL; dev_kfree_skb(skb); } } - - ace_init_cleanup(root_dev); - kfree(root_dev); - root_dev = next; } -} - - -int __init ace_module_init(void) -{ - int status; - - root_dev = NULL; + for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; + if (skb) { +#ifndef DUMMY_PCI_UNMAP + dma_addr_t mapping; -#ifdef NEW_NETINIT - status = acenic_probe(); -#else - status = acenic_probe(NULL); + mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_JUMBO_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - return status; -} + ap->rx_jumbo_ring[i].size = 0; + ap->skb->rx_jumbo_skbuff[i].skb = NULL; + dev_kfree_skb(skb); + } + } -#if (LINUX_VERSION_CODE < 0x02032a) -#ifdef MODULE -int init_module(void) -{ - return ace_module_init(); + ace_init_cleanup(dev); + kfree(dev); } - -void cleanup_module(void) -{ - ace_module_cleanup(); -} -#endif -#else -module_init(ace_module_init); -module_exit(ace_module_cleanup); -#endif - - static void ace_free_descriptors(struct net_device *dev) { struct ace_private *ap = dev->priv; @@ -1324,13 +1217,6 @@ goto init_error; } - /* - * Register the device here to be able to catch allocated - * interrupt handlers in case the firmware doesn't come up. - */ - ap->next = root_dev; - root_dev = dev; - #ifdef INDEX_DEBUG spin_lock_init(&ap->debug_lock); ap->last_tx = TX_RING_ENTRIES - 1; @@ -2525,7 +2411,7 @@ #if defined(CONFIG_X86) #define DMAADDR_OFFSET 0 typedef unsigned long long dmaaddr_high_t; -#elif defined(CONFIG_PPC) +#elif defined(CONFIG_PPC32) #define DMAADDR_OFFSET PCI_DRAM_OFFSET typedef unsigned long dmaaddr_high_t; #endif @@ -3338,3 +3224,45 @@ * compile-command: "gcc -D__SMP__ -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ + +static struct pci_device_id acenic_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + /* + * Farallon used the DEC vendor ID on their cards incorrectly. + */ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { } +}; +MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); + +static struct pci_driver acenic_driver = { + name: "acenic", + id_table: acenic_pci_tbl, + probe: acenic_init_one, + remove: acenic_remove_one, +}; + +static int __init acenic_init_module(void) +{ + return pci_module_init(&acenic_driver); +} + +static void __exit acenic_cleanup_module(void) +{ + pci_unregister_driver(&acenic_driver); +} + +module_init(acenic_init_module); +module_exit(acenic_cleanup_module); diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/net/acenic.h linuxppc64_2_4/drivers/net/acenic.h --- linux-2.4.9-ac10/drivers/net/acenic.h Thu Jun 28 16:47:10 2001 +++ linuxppc64_2_4/drivers/net/acenic.h Wed Aug 22 08:27:45 2001 @@ -582,7 +582,7 @@ aceaddr stats2_ptr; }; -#if defined(CONFIG_X86) || defined(CONFIG_PPC) +#if defined(CONFIG_X86) || defined(CONFIG_PPC32) /* Intel has null pci_unmap_single, no reasons to remember mapping. */ #define DUMMY_PCI_UNMAP #endif diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/net/pcnet32.c linuxppc64_2_4/drivers/net/pcnet32.c --- linux-2.4.9-ac10/drivers/net/pcnet32.c Wed Oct 3 12:11:21 2001 +++ linuxppc64_2_4/drivers/net/pcnet32.c Tue Oct 2 15:07:32 2001 @@ -201,7 +201,7 @@ #define RX_RING_MOD_MASK (RX_RING_SIZE - 1) #define RX_RING_LEN_BITS ((PCNET32_LOG_RX_BUFFERS) << 4) -#define PKT_BUF_SZ 1544 +#define PKT_BUF_SZ 2048 /* Offsets from base I/O address. */ #define PCNET32_WIO_RDP 0x10 @@ -294,7 +294,7 @@ static int pcnet32_probe_vlbus(int cards_found); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); -static int pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *); +static int pcnet32_probe1(unsigned long, unsigned int, int, int, struct pci_dev *); static int pcnet32_open(struct net_device *); static int pcnet32_init_ring(struct net_device *); static int pcnet32_start_xmit(struct sk_buff *, struct net_device *); @@ -317,7 +317,7 @@ const char *name; u16 vendor_id, device_id, svid, sdid, flags; int io_size; - int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *); + int (*probe1) (unsigned long, unsigned int, int, int, struct pci_dev *); }; @@ -440,7 +440,9 @@ static int __init pcnet32_probe_vlbus(int cards_found) { unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0; +#ifndef __powerpc__ unsigned int irq_line = 0; // FIXME dev ? dev->irq : 0; +#endif int *port; printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found); @@ -508,7 +510,7 @@ * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit -pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) +pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, int card_idx, struct pci_dev *pdev) { struct pcnet32_private *lp; struct resource *res; @@ -522,17 +524,20 @@ char *chipname; struct net_device *dev; struct pcnet32_access *a = NULL; + u8 promaddr[6]; /* reset the chip */ pcnet32_dwio_reset(ioaddr); + udelay (100); pcnet32_wio_reset(ioaddr); - /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) { - a = &pcnet32_wio; + /* Important to do the check for dwio mode first. */ + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { + a = &pcnet32_dwio; } else { - if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { - a = &pcnet32_dwio; + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && + pcnet32_wio_check(ioaddr)) { + a = &pcnet32_wio; } else return -ENODEV; } @@ -607,11 +612,18 @@ * one for latency - although on PCI this isnt a big loss. Older chips * have FIFO's smaller than a packet, so you can't do this. */ - + /* + * UPDATE + * Got to make sure that BCR18:MEMCMD, BCR18:BREADE, BCR18:BWRITE are + * set on a PCI + */ if(fset) { - a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); - a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); + a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0xA60)); + a->write_csr(ioaddr, 3, 0x2eb7); + a->write_csr(ioaddr, 4, 0x32ea); + a->write_csr(ioaddr, 80, 0x3f00); + #ifdef DO_DXSUFLO dxsuflo = 1; #endif @@ -631,6 +643,7 @@ * they disagree with the CSRs. Either way, we use the CSR values, and * double check that they are valid. */ +#ifndef CONFIG_ALL_PPC for (i = 0; i < 3; i++) { unsigned int val; val = a->read_csr(ioaddr, i+12) & 0x0ffff; @@ -638,20 +651,21 @@ dev->dev_addr[2*i] = val & 0x0ff; dev->dev_addr[2*i+1] = (val >> 8) & 0x0ff; } +#endif + for (i = 0; i < 6; i++) { + promaddr[i] = inb(ioaddr + i); +#ifdef CONFIG_ALL_PPC + dev->dev_addr[i] = promaddr[i]; +#endif + } + if( memcmp( promaddr, dev->dev_addr, 6) ) { - u8 promaddr[6]; - for (i = 0; i < 6; i++) { - promaddr[i] = inb(ioaddr + i); - } - if( memcmp( promaddr, dev->dev_addr, 6) ) - { - printk(" warning PROM address does not match CSR address\n"); + printk(" warning PROM address does not match CSR address"); #if defined(__i386__) - printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); - memcpy(dev->dev_addr, promaddr, 6); + printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); + memcpy(dev->dev_addr, promaddr, 6); #endif - } - } + } /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ if( !is_valid_ether_addr(dev->dev_addr) ) for (i = 0; i < 6; i++) @@ -885,7 +899,7 @@ lp->init_block.filter[1] = 0x00000000; if (pcnet32_init_ring(dev)) return -ENOMEM; - + /* Re-initialize the PCNET32, and start it when done. */ lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff); lp->a.write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16); @@ -967,7 +981,7 @@ } skb_reserve (rx_skbuff, 2); } - lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, rx_skbuff->len, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]); lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); lp->rx_ring[i].status = le16_to_cpu(0x8000); @@ -1014,7 +1028,7 @@ pcnet32_tx_timeout (struct net_device *dev) { struct pcnet32_private *lp = dev->priv; - unsigned int ioaddr = dev->base_addr; + unsigned long ioaddr = dev->base_addr; /* Transmitter timeout, serious problems. */ printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n", @@ -1047,7 +1061,7 @@ pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct pcnet32_private *lp = dev->priv; - unsigned int ioaddr = dev->base_addr; + unsigned long ioaddr = dev->base_addr; u16 status; int entry; unsigned long flags; @@ -1301,7 +1315,8 @@ skb_put (skb, pkt_len); lp->rx_skbuff[entry] = newskb; newskb->dev = dev; - lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]); rx_in_place = 1; } else @@ -1387,7 +1402,7 @@ for (i = 0; i < RX_RING_SIZE; i++) { lp->rx_ring[i].status = 0; if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], lp->rx_skbuff[i]->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); dev_kfree_skb(lp->rx_skbuff[i]); } lp->rx_skbuff[i] = NULL; diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/scsi/sr_ioctl.c linuxppc64_2_4/drivers/scsi/sr_ioctl.c --- linux-2.4.9-ac10/drivers/scsi/sr_ioctl.c Wed Aug 15 11:51:27 2001 +++ linuxppc64_2_4/drivers/scsi/sr_ioctl.c Fri Sep 28 15:46:48 2001 @@ -334,7 +334,12 @@ { u_char sr_cmd[10]; int result, target = MINOR(cdi->dev); - unsigned char buffer[32]; + unsigned char *buffer = scsi_malloc(512); + + if (buffer == NULL) { + printk("SCSI DMA pool exhausted."); + return -ENOMEM; + } memset(sr_cmd, 0, sizeof(sr_cmd)); @@ -407,6 +412,7 @@ return -EINVAL; } + scsi_free(buffer, 512); #if 0 if (result) printk("DEBUG: sr_audio: result for ioctl %x: %x\n", cmd, result); diff -uNr --exclude=CVS linux-2.4.9-ac10/drivers/video/offb.c linuxppc64_2_4/drivers/video/offb.c --- linux-2.4.9-ac10/drivers/video/offb.c Wed Oct 3 12:11:39 2001 +++ linuxppc64_2_4/drivers/video/offb.c Tue Sep 4 14:09:20 2001 @@ -32,7 +32,9 @@ #endif #include #include +#ifndef CONFIG_PPC64 #include +#endif #include