diff -urN linux-2.4.26/CREDITS linux-2.4.27/CREDITS --- linux-2.4.26/CREDITS 2004-04-14 06:05:21.000000000 -0700 +++ linux-2.4.27/CREDITS 2004-08-07 16:26:04.249330136 -0700 @@ -1232,8 +1232,8 @@ D: National Language Support D: Linux Internationalization Project D: German Localization for Linux and GNU software -S: Helenenstrasse 18 -S: 65183 Wiesbaden +S: Kriemhildring 12a +S: 65795 Hattersheim am Main S: Germany N: Christoph Hellwig diff -urN linux-2.4.26/Documentation/CodingStyle linux-2.4.27/Documentation/CodingStyle --- linux-2.4.26/Documentation/CodingStyle 2001-09-09 16:40:43.000000000 -0700 +++ linux-2.4.27/Documentation/CodingStyle 2004-08-07 16:26:04.251330218 -0700 @@ -1,42 +1,75 @@ - Linux kernel coding style + Linux kernel coding style This is a short document describing the preferred coding style for the linux kernel. Coding style is very personal, and I won't _force_ my views on anybody, but this is what goes for anything that I have to be able to maintain, and I'd prefer it for most other things too. Please -at least consider the points made here. +at least consider the points made here. First off, I'd suggest printing out a copy of the GNU coding standards, -and NOT read it. Burn them, it's a great symbolic gesture. +and NOT reading it. Burn them, it's a great symbolic gesture. Anyway, here goes: Chapter 1: Indentation -Tabs are 8 characters, and thus indentations are also 8 characters. +Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to make indentations 4 (or even 2!) characters deep, and that is akin to trying to define the value of PI to -be 3. +be 3. Rationale: The whole idea behind indentation is to clearly define where a block of control starts and ends. Especially when you've been looking at your screen for 20 straight hours, you'll find it a lot easier to see -how the indentation works if you have large indentations. +how the indentation works if you have large indentations. Now, some people will claim that having 8-character indentations makes the code move too far to the right, and makes it hard to read on a 80-character terminal screen. The answer to that is that if you need more than 3 levels of indentation, you're screwed anyway, and should fix -your program. +your program. In short, 8-char indents make things easier to read, and have the added -benefit of warning you when you're nesting your functions too deep. -Heed that warning. +benefit of warning you when you're nesting your functions too deep. +Heed that warning. +Don't put multiple statements on a single line unless you have +something to hide: - Chapter 2: Placing Braces + if (condition) do_this; + do_something_everytime; + +Outside of comments, documentation and except in [cC]onfig.in, spaces are never +used for indentation, and the above example is deliberately broken. + +Get a decent editor and don't leave whitespace at the end of lines. + + + Chapter 2: Breaking long lines and strings + +Coding style is all about readability and maintainability using commonly +available tools. + +The limit on the length of lines is 80 columns and this is a hard limit. + +Statements longer than 80 columns will be broken into sensible chunks. +Descendants are always substantially shorter than the parent and are placed +substantially to the right. The same applies to function headers with a long +argument list. Long strings are as well broken into shorter strings. + +void fun(int a, int b, int c) +{ + if (condition) + printk(KERN_WARNING "Warning this is a long printk with " + "3 parameters a: %u b: %u " + "c: %u \n", a, b, c); + else + next_statement; +} + + Chapter 3: Placing Braces The other issue that always comes up in C styling is the placement of braces. Unlike the indent size, there are few technical reasons to @@ -59,7 +92,7 @@ Heretic people all over the world have claimed that this inconsistency is ... well ... inconsistent, but all right-thinking people know that (a) K&R are _right_ and (b) K&R are right. Besides, functions are -special anyway (you can't nest them in C). +special anyway (you can't nest them in C). Note that the closing brace is empty on a line of its own, _except_ in the cases where it is followed by a continuation of the same statement, @@ -79,60 +112,60 @@ } else { .... } - -Rationale: K&R. + +Rationale: K&R. Also, note that this brace-placement also minimizes the number of empty (or almost empty) lines, without any loss of readability. Thus, as the supply of new-lines on your screen is not a renewable resource (think 25-line terminal screens here), you have more empty lines to put -comments on. +comments on. - Chapter 3: Naming + Chapter 4: Naming C is a Spartan language, and so should your naming be. Unlike Modula-2 and Pascal programmers, C programmers do not use cute names like ThisVariableIsATemporaryCounter. A C programmer would call that variable "tmp", which is much easier to write, and not the least more -difficult to understand. +difficult to understand. HOWEVER, while mixed-case names are frowned upon, descriptive names for global variables are a must. To call a global function "foo" is a -shooting offense. +shooting offense. GLOBAL variables (to be used only if you _really_ need them) need to have descriptive names, as do global functions. If you have a function that counts the number of active users, you should call that -"count_active_users()" or similar, you should _not_ call it "cntusr()". +"count_active_users()" or similar, you should _not_ call it "cntusr()". Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged - the compiler knows the types anyway and can check those, and it only confuses the programmer. No wonder MicroSoft -makes buggy programs. +makes buggy programs. LOCAL variable names should be short, and to the point. If you have -some random integer loop counter, it should probably be called "i". +some random integer loop counter, it should probably be called "i". Calling it "loop_counter" is non-productive, if there is no chance of it being mis-understood. Similarly, "tmp" can be just about any type of -variable that is used to hold a temporary value. +variable that is used to hold a temporary value. If you are afraid to mix up your local variable names, you have another -problem, which is called the function-growth-hormone-imbalance syndrome. -See next chapter. +problem, which is called the function-growth-hormone-imbalance syndrome. +See next chapter. - - Chapter 4: Functions + + Chapter 5: Functions Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, -as we all know), and do one thing and do that well. +as we all know), and do one thing and do that well. The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of -different cases, it's OK to have a longer function. +different cases, it's OK to have a longer function. However, if you have a complex function, and you suspect that a less-than-gifted first-year high-school student might not even @@ -140,41 +173,78 @@ maximum limits all the more closely. Use helper functions with descriptive names (you can ask the compiler to in-line them if you think it's performance-critical, and it will probably do a better job of it -that you would have done). +than you would have done). Another measure of the function is the number of local variables. They shouldn't exceed 5-10, or you're doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused. You know you're brilliant, but maybe you'd like -to understand what you did 2 weeks from now. +to understand what you did 2 weeks from now. + + + Chapter 6: Centralized exiting of functions +Albeit deprecated by some people, the equivalent of the goto statement is +used frequently by compilers in form of the unconditional jump instruction. - Chapter 5: Commenting +The goto statement comes in handy when a function exits from multiple +locations and some common work such as cleanup has to be done. + +The rationale is: + +- unconditional statements are easier to understand and follow +- nesting is reduced +- errors by not updating individual exit points when making + modifications are prevented +- saves the compiler work to optimize redundant code away ;) + +int fun(int ) +{ + int result = 0; + char *buffer = kmalloc(SIZE); + + if (buffer == NULL) + return -ENOMEM; + + if (condition1) { + while (loop1) { + ... + } + result = 1; + goto out; + } + ... +out: + kfree(buffer); + return result; +} + + Chapter 7: Commenting Comments are good, but there is also a danger of over-commenting. NEVER try to explain HOW your code works in a comment: it's much better to write the code so that the _working_ is obvious, and it's a waste of -time to explain badly written code. +time to explain badly written code. -Generally, you want your comments to tell WHAT your code does, not HOW. +Generally, you want your comments to tell WHAT your code does, not HOW. Also, try to avoid putting comments inside a function body: if the function is so complex that you need to separately comment parts of it, -you should probably go back to chapter 4 for a while. You can make +you should probably go back to chapter 5 for a while. You can make small comments to note or warn about something particularly clever (or ugly), but try to avoid excess. Instead, put the comments at the head of the function, telling people what it does, and possibly WHY it does -it. +it. - Chapter 6: You've made a mess of it + Chapter 8: You've made a mess of it That's OK, we all do. You've probably been told by your long-time Unix user helper that "GNU emacs" automatically formats the C sources for you, and you've noticed that yes, it does do that, but the defaults it uses are less than desirable (in fact, they are worse than random -typing - a infinite number of monkeys typing into GNU emacs would never -make a good program). +typing - an infinite number of monkeys typing into GNU emacs would never +make a good program). So, you can either get rid of GNU emacs, or change it to use saner values. To do the latter, you can stick the following in your .emacs file: @@ -192,7 +262,7 @@ to add (setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode) - auto-mode-alist)) + auto-mode-alist)) to your .emacs file if you want to have linux-c-mode switched on automagically when you edit source files under /usr/src/linux. @@ -200,19 +270,20 @@ But even if you fail in getting emacs to do sane formatting, not everything is lost: use "indent". -Now, again, GNU indent has the same brain dead settings that GNU emacs -has, which is why you need to give it a few command line options. +Now, again, GNU indent has the same brain-dead settings that GNU emacs +has, which is why you need to give it a few command line options. However, that's not too bad, because even the makers of GNU indent recognize the authority of K&R (the GNU people aren't evil, they are just severely misguided in this matter), so you just give indent the -options "-kr -i8" (stands for "K&R, 8 character indents"). +options "-kr -i8" (stands for "K&R, 8 character indents"), or use +"scripts/Lindent", which indents in the latest style. "indent" has a lot of options, and especially when it comes to comment -re-formatting you may want to take a look at the manual page. But -remember: "indent" is not a fix for bad programming. +re-formatting you may want to take a look at the man page. But +remember: "indent" is not a fix for bad programming. - Chapter 7: Configuration-files + Chapter 9: Configuration-files For configuration options (arch/xxx/config.in, and all the Config.in files), somewhat different indentation is used. @@ -235,20 +306,20 @@ Experimental options should be denoted (EXPERIMENTAL). - Chapter 8: Data structures + Chapter 10: Data structures Data structures that have visibility outside the single-threaded environment they are created and destroyed in should always have reference counts. In the kernel, garbage collection doesn't exist (and outside the kernel garbage collection is slow and inefficient), which -means that you absolutely _have_ to reference count all your uses. +means that you absolutely _have_ to reference count all your uses. Reference counting means that you can avoid locking, and allows multiple users to have access to the data structure in parallel - and not having to worry about the structure suddenly going away from under them just -because they slept or did something else for a while. +because they slept or did something else for a while. -Note that locking is _not_ a replacement for reference counting. +Note that locking is _not_ a replacement for reference counting. Locking is used to keep data structures coherent, while reference counting is a memory management technique. Usually both are needed, and they are not to be confused with each other. @@ -258,9 +329,99 @@ the number of subclass users, and decrements the global count just once when the subclass count goes to zero. -Examples of this kind of "multi-reference-counting" can be found in +Examples of this kind of "multi-level-reference-counting" can be found in memory management ("struct mm_struct": mm_users and mm_count), and in filesystem code ("struct super_block": s_count and s_active). Remember: if another thread can find your data structure, and you don't have a reference count on it, you almost certainly have a bug. + + + Chapter 11: Macros, Enums, Inline functions and RTL + +Names of macros defining constants and labels in enums are capitalized. + +#define CONSTANT 0x12345 + +Enums are preferred when defining several related constants. + +CAPITALIZED macro names are appreciated but macros resembling functions +may be named in lower case. + +Generally, inline functions are preferable to macros resembling functions. + +Macros with multiple statements should be enclosed in a do - while block: + +#define macrofun(a,b,c) \ + do { \ + if (a == 5) \ + do_this(b,c); \ + } while (0) + +Things to avoid when using macros: + +1) macros that affect control flow: + +#define FOO(x) \ + do { \ + if (blah(x) < 0) \ + return -EBUGGERED; \ + } while(0) + +is a _very_ bad idea. It looks like a function call but exits the "calling" +function; don't break the internal parsers of those who will read the code. + +2) macros that depend on having a local variable with a magic name: + +#define FOO(val) bar(index, val) + +might look like a good thing, but it's confusing as hell when one reads the +code and it's prone to breakage from seemingly innocent changes. + +3) macros with arguments that are used as l-values: FOO(x) = y; will +bite you if somebody e.g. turns FOO into an inline function. + +4) forgetting about precedence: macros defining constants using expressions +must enclose the expression in parentheses. Beware of similar issues with +macros using parameters. + +#define CONSTANT 0x4000 +#define CONSTEXP (CONSTANT | 3) + +The cpp manual deals with macros exhaustively. The gcc internals manual also +covers RTL which is used frequently with assembly language in the kernel. + + + Chapter 12: Printing kernel messages + +Kernel developers like to be seen as literate. Do mind the spelling +of kernel messages to make a good impression. Do not use crippled +words like "dont" and use "do not" or "don't" instead. + +Kernel messages do not have to be terminated with a period. + +Printing numbers in parentheses (%d) adds no value and should be avoided. + + + Chapter 13: References + +The C Programming Language, Second Edition +by Brian W. Kernighan and Dennis M. Ritchie. +Prentice Hall, Inc., 1988. +ISBN 0-13-110362-8 (paperback), 0-13-110370-9 (hardback). +URL: http://cm.bell-labs.com/cm/cs/cbook/ + +The Practice of Programming +by Brian W. Kernighan and Rob Pike. +Addison-Wesley, Inc., 1999. +ISBN 0-201-61586-X. +URL: http://cm.bell-labs.com/cm/cs/tpop/ + +GNU manuals - where in compliance with K&R and this text - for cpp, gcc, +gcc internals and indent, all available from http://www.gnu.org + +WG14 is the international standardization working group for the programming +language C, URL: http://std.dkuug.dk/JTC1/SC22/WG14/ + +-- +Last updated on 16 March 2004 by a community effort on LKML. diff -urN linux-2.4.26/Documentation/Configure.help linux-2.4.27/Documentation/Configure.help --- linux-2.4.26/Documentation/Configure.help 2004-04-14 06:05:24.000000000 -0700 +++ linux-2.4.27/Documentation/Configure.help 2004-08-07 16:26:04.531341723 -0700 @@ -569,6 +569,19 @@ The umem driver has been allocated block major number 116. See Documentation/devices.txt for recommended device naming. +Promise SATA SX8 support +CONFIG_BLK_DEV_SX8 + Saying Y or M here will enable support for the + Promise SATA SX8 controllers. + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called sx8.o. + + The sx8 driver has been allocated block major numbers 160, 161. + See Documentation/devices.txt for recommended device naming. + Network block device support CONFIG_BLK_DEV_NBD Saying Y here will allow your computer to be a client for network @@ -1296,11 +1309,13 @@ If unsure, say N. -PROMISE PDC20246/PDC20262/PDC20265/PDC20267/PDC20268 support +Promise PDC202{46|62|65|67} support CONFIG_BLK_DEV_PDC202XX_OLD - Promise Ultra33 or PDC20246 - Promise Ultra66 or PDC20262 - Promise Ultra100 or PDC20265/PDC20267/PDC20268 + Promise Ultra 33 [PDC20246] + Promise Ultra 66 [PDC20262] + Promise FastTrak 66 [PDC20263] + Promise MB Ultra 100 [PDC20265] + Promise Ultra 100 [PDC20267] This driver adds up to 4 more EIDE devices sharing a single interrupt. This add-on card is a bootable PCI UDMA controller. Since @@ -1309,7 +1324,7 @@ not match, the driver attempts to do dynamic tuning of the chipset at boot-time for max-speed. Ultra33 BIOS 1.25 or newer is required for more than one card. This card may require that you say Y to - "Special UDMA Feature". + "Force (U)DMA burst transfers" (old name: "Special UDMA Feature"). If you say Y here, you need to say Y to "Use DMA by default when available" as well. @@ -1319,7 +1334,7 @@ If unsure, say N. -PROMISE PDC202{68|69|70|71|75|76|77} support +Promise PDC202{68|69|70|71|75|76|77} support CONFIG_BLK_DEV_PDC202XX_NEW Promise Ultra 100 TX2 [PDC20268] Promise Ultra 133 PTX2 [PDC20269] @@ -1334,17 +1349,16 @@ multiple cards can be installed and there are BIOS ROM problems that happen if the BIOS revisions of all installed cards (max of five) do not match, the driver attempts to do dynamic tuning of the chipset - at boot-time for max speed. Ultra33 BIOS 1.25 or newer is required - for more than one card. + at boot-time for max speed. If you say Y here, you need to say Y to "Use DMA by default when available" as well. If unsure, say N. -Special UDMA Feature +Force (U)DMA burst transfers CONFIG_PDC202XX_BURST - This option causes the pdc202xx driver to enable UDMA modes on the + This option causes the pdc202xx_old driver to enable UDMA modes on the PDC202xx even when the PDC202xx BIOS has not done so. It was originally designed for the PDC20246/Ultra33, whose BIOS will @@ -1357,9 +1371,17 @@ If unsure, say N. -Special FastTrak Feature +Ignore BIOS port disabled setting on FastTrak CONFIG_PDC202XX_FORCE - For FastTrak enable overriding BIOS. + Chipsets affected: + + PDC202{46|62|63|65|67} + (pdc202xx_old driver) + + PDC202{70|76} + (pdc202xx_new driver) + + Say Y unless you want to use Promise proprietary driver. SiS5513 chipset support CONFIG_BLK_DEV_SIS5513 @@ -4286,6 +4308,46 @@ inserted in and removed from the running kernel whenever you want). The module will be called acpiphp.o. If you want to compile it as a module, say M here and read . + +CONFIG_HOTPLUG_PCI_SHPC + Say Y here if you have a motherboard with a SHPC PCI Hotplug + controller. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called shpchp.o. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE + Say Y here if you want to use the polling mechanism for hot-plug + events for early platform testing. + + When in doubt, say N. + +CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY + Say Y here for AMD SHPC. You have to select this option if you are + using this driver on platform with AMD SHPC. + + When in doubt, say N. + +CONFIG_HOTPLUG_PCI_PCIE + Say Y here if you have a motherboard that supports PCI Express Native + Hotplug + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called pciehp.o. If you want to compile it + as a module, say M here and read . + + When in doubt, say N. + +CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE + Say Y here if you want to use the polling mechanism for hot-plug + events for early platform testing. + + When in doubt, say N. MCA support CONFIG_MCA @@ -5543,14 +5605,15 @@ SIS 300 series support CONFIG_FB_SIS_300 This enables support for SiS 300 series chipsets (300/305, 540, 630, - 730). Documentation available at the maintainer's website at - . + 630S, 730S). Documentation available at the maintainer's website at + . SIS 315/330 series support CONFIG_FB_SIS_315 - This enables support for SiS 315/330 series chipsets (315, 550, 650, - M650, 651, 661FX, M661FX, 740, 741, 330). Documentation available at - the maintainer's site . + This enables support for SiS 315/330 series chipsets (315, 315PRO, + 55x, (M)650, 651, (M)661FX, 661MX, 740, (M)741(GX), (M)760, 330). + Documentation available at the maintainer's website at + . IMS Twin Turbo display support CONFIG_FB_IMSTT @@ -6109,8 +6172,7 @@ can be useful if you want to make your (or some other) machine appear on a different network than it physically is, or to use mobile-IP facilities (allowing laptops to seamlessly move between - networks without changing their IP addresses; check out - ). + networks without changing their IP addresses). Saying Y to this option will produce two modules ( = code which can be inserted in and removed from the running kernel whenever you @@ -7521,6 +7583,11 @@ not have to supply an alternative one. They just say Y to "Use default SBA-200E firmware", above. +CONFIG_ATM_FORE200E_USE_TASKLET + This defers work to be done by the interrupt handler to a + tasklet instead of handling everything at interrupt time. This + may improve the responsiveness of the host. + Maximum number of tx retries CONFIG_ATM_FORE200E_TX_RETRY Specifies the number of times the driver attempts to transmit @@ -9202,6 +9269,48 @@ say M here and read . The module will be called megaraid2.o. +CONFIG_SCSI_SATA + This driver family supports Serial ATA host controllers + and devices. + + If unsure, say N. + +CONFIG_SCSI_SATA_SVW + This option enables support for Broadcom/Serverworks/Apple K2 + SATA support. + + If unsure, say N. + +CONFIG_SCSI_SATA_PROMISE + This option enables support for Promise Serial ATA TX2/TX4. + + If unsure, say N. + +CONFIG_SCSI_SATA_SX4 + This option enables support for Promise Serial ATA SX4. + + If unsure, say N. + +CONFIG_SCSI_SATA_SIL + This option enables support for Silicon Image Serial ATA. + + If unsure, say N. + +CONFIG_SCSI_SATA_SIS + This option enables support for SiS Serial ATA 964/180. + + If unsure, say N. + +CONFIG_SCSI_SATA_VIA + This option enables support for VIA Serial ATA. + + If unsure, say N. + +CONFIG_SCSI_SATA_VITESSE + This option enables support for Vitesse VSC7174 Serial ATA. + + If unsure, say N. + Intel/ICP (former GDT SCSI Disk Array) RAID Controller support CONFIG_SCSI_GDTH Formerly called GDT SCSI Disk Array Controller Support. @@ -10840,13 +10949,15 @@ whenever you want). If you want to compile it as a module, say M here and read . -Network delay simualtor -CONFIG_NET_SCH_DELAY - Say Y if you want to delay packets by a fixed amount of - time. This is often useful to simulate network delay when +CONFIG_NET_SCH_NETEM + Say Y if you want to emulate network delay, loss, and packet + re-ordering. This is often useful to simulate networks when testing applications or protocols. - - This code is also available as a module called sch_delay.o + + To compile this driver as a module, choose M here: the module + will be called sch_netem. + + If unsure, say N. Ingress Qdisc CONFIG_NET_SCH_INGRESS @@ -12101,6 +12212,20 @@ module, say M here and read as well as . +CONFIG_E1000_NAPI + NAPI is a new driver API designed to reduce CPU and interrupt load + when the driver is receiving lots of packets from the card. It is + still somewhat experimental and thus not yet enabled by default. + + If your estimated Rx load is 10kpps or more, or if the card will be + deployed on potentially unfriendly networks (e.g. in a firewall), + then say Y here. + + See for more + information. + + If in doubt, say N. + AMD LANCE and PCnet (AT1500 and NE2100) support CONFIG_LANCE If you have a network (Ethernet) card of this type, say Y and read @@ -13407,6 +13532,16 @@ under Linux, say Y here (you must also remember to enable the driver for your HIPPI card below). Most people will say N here. +IBM PowerPC Virtual Ethernet driver support +CONFIG_IBMVETH + This driver supports virtual ethernet adapters on newer IBM iSeries + and pSeries systems. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read . The module + will be called ibmveth.o. + Essential RoadRunner HIPPI PCI adapter support CONFIG_ROADRUNNER Say Y here if this is your PCI HIPPI network card. @@ -16093,6 +16228,27 @@ Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_ether". +CONFIG_USB_ETH_RNDIS + Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, + and Microsoft provides redistributable binary RNDIS drivers for + older versions of Windows. + + If you say "y" here, the Ethernet gadget driver will try to provide + a second device configuration, supporting RNDIS to talk to such + Microsoft USB hosts. + +CONFIG_USB_FILE_STORAGE + The File-backed Storage Gadget acts as a USB Mass Storage + disk drive. As its storage repository it can use a regular + file or a block device (in much the same way as the "loop" + device driver), specified as a module parameter. + +CONFIG_USB_FILE_STORAGE_TEST + Say "y" to generate the larger testing version of the + File-backed Storage Gadget, useful for probing the + behavior of USB Mass Storage hosts. Not needed for + normal operation. + Always do synchronous disk IO for UBD CONFIG_BLK_DEV_UBD_SYNC The User-Mode Linux port includes a driver called UBD which will let @@ -17269,7 +17425,7 @@ automounter (amd), which is a pure user space daemon. To use the automounter you need the user-space tools from - ; you also + ; you also want to answer Y to "NFS file system support", below. If you want to compile this as a module ( = code which can be @@ -17427,13 +17583,10 @@ CONFIG_XFS_TRACE Say Y here to get an XFS build with activity tracing enabled. Enabling this option will attach historical information to XFS - inodes, pagebufs, certain locks, the log, the IO path, and a + inodes, buffers, certain locks, the log, the IO path, and a few other key areas within XFS. These traces can be examined using a kernel debugger. - Note that for the pagebuf traces, you will also have to enable - the sysctl in /proc/sys/vm/pagebuf/debug for this to work. - Say N unless you are an XFS developer. Debugging support (EXPERIMENTAL) @@ -23001,22 +23154,20 @@ Linux Bluetooth subsystem consist of several layers: BlueZ Core (HCI device and connection manager, scheduler) - HCI Device drivers (interface to the hardware) - L2CAP Module (L2CAP protocol) - SCO Module (SCO links) - RFCOMM Module (RFCOMM protocol) - BNEP Module (BNEP protocol) - CMTP Module (CMTP protocol) + HCI Device drivers (Interface to the hardware) + SCO Module (SCO audio links) + L2CAP Module (Logical Link Control and Adaptation Protocol) + RFCOMM Module (RFCOMM Protocol) + BNEP Module (Bluetooth Network Encapsulation Protocol) + CMTP Module (CAPI Message Transport Protocol) - Say Y here to enable Linux Bluetooth support and to build BlueZ Core - layer. + Say Y here to compile Bluetooth support into the kernel or say M to + compile it as module (bluez.o). To use Linux Bluetooth subsystem, you will need several user-space utilities like hciconfig and hcid. These utilities and updates to Bluetooth kernel modules are provided in the BlueZ package. - For more information, see . - - If you want to compile BlueZ Core as module (bluez.o) say M here. + For more information, see . L2CAP protocol support CONFIG_BLUEZ_L2CAP @@ -23029,7 +23180,7 @@ SCO links support CONFIG_BLUEZ_SCO - SCO link provides voice transport over Bluetooth. SCO support is + SCO link provides voice transport over Bluetooth. SCO support is required for voice applications like Headset and Audio. Say Y here to compile SCO support into the kernel or say M to @@ -23037,7 +23188,7 @@ RFCOMM protocol support CONFIG_BLUEZ_RFCOMM - RFCOMM provides connection oriented stream transport. RFCOMM + RFCOMM provides connection oriented stream transport. RFCOMM support is required for Dialup Networking, OBEX and other Bluetooth applications. @@ -23051,25 +23202,12 @@ BNEP protocol support CONFIG_BLUEZ_BNEP BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet - emulation layer on top of Bluetooth. BNEP is required for Bluetooth - PAN (Personal Area Network). - - To use BNEP, you will need user-space utilities provided in the - BlueZ-PAN package. - For more information, see . + emulation layer on top of Bluetooth. BNEP is required for + Bluetooth PAN (Personal Area Network). Say Y here to compile BNEP support into the kernel or say M to compile it as module (bnep.o). -CMTP protocol support -CONFIG_BLUEZ_CMTP - CMTP (CAPI Message Transport Protocol) is a transport layer - for CAPI messages. CMTP is required for the Bluetooth Common - ISDN Access Profile. - - Say Y here to compile CMTP support into the kernel or say M to - compile it as module (cmtp.o). - BNEP multicast filter support CONFIG_BLUEZ_BNEP_MC_FILTER This option enables the multicast filter support for BNEP. @@ -23078,6 +23216,15 @@ CONFIG_BLUEZ_BNEP_PROTO_FILTER This option enables the protocol filter support for BNEP. +CMTP protocol support +CONFIG_BLUEZ_CMTP + CMTP (CAPI Message Transport Protocol) is a transport layer + for CAPI messages. CMTP is required for the Bluetooth Common + ISDN Access Profile. + + Say Y here to compile CMTP support into the kernel or say M to + compile it as module (cmtp.o). + HCI UART driver CONFIG_BLUEZ_HCIUART Bluetooth HCI UART driver. @@ -23167,9 +23314,6 @@ 3Com Bluetooth Card (3CRWB6096) HP Bluetooth Card - The HCI BT3C driver uses external firmware loader program provided in - the BlueFW package. For more information, see . - Say Y here to compile support for HCI BT3C devices into the kernel or say M to compile it as module (bt3c_cs.o). @@ -28781,12 +28925,24 @@ The CAST6 encryption algorithm (synonymous with CAST-256) is described in RFC2612. +CONFIG_CRYPTO_TEA + TEA cipher algorithm. + + Tiny Encryption Algorithm is a simple cipher that uses + many rounds for security. It is very fast and uses + little memory. + + Xtendend Tiny Encryption Algorithm is a modifcation to + the TEA algorithm to address a potential key weakness + in the TEA algorithm. + CONFIG_CRYPTO_ARC4 ARC4 cipher algorithm. - This is a stream cipher using keys ranging from 8 bits to 2048 - bits in length. ARC4 is commonly used in protocols such as WEP - and SSL. + ARC4 is a stream cipher using keys ranging from 8 bits to 2048 + bits in length. This algorithm is required for driver-based + WEP, but it should not be for other purposes because of the + weakness of the algorithm. CONFIG_CRYPTO_DEFLATE This is the Deflate algorithm (RFC1951), specified for use in @@ -28794,6 +28950,12 @@ You will most probably want this if using IPSec. +CONFIG_CRYPTO_MICHAEL_MIC + Michael MIC is used for message integrity protection in TKIP + (IEEE 802.11i). This algorithm is required for TKIP, but it + should not be used for other purposes because of the weakness + of the algorithm. + CONFIG_CRYPTO_TEST Quick & dirty crypto test module. diff -urN linux-2.4.26/Documentation/DocBook/Makefile linux-2.4.27/Documentation/DocBook/Makefile --- linux-2.4.26/Documentation/DocBook/Makefile 2002-11-28 15:53:08.000000000 -0800 +++ linux-2.4.27/Documentation/DocBook/Makefile 2004-08-07 16:26:04.533341806 -0700 @@ -2,7 +2,7 @@ kernel-api.sgml parportbook.sgml kernel-hacking.sgml \ kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \ deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \ - journal-api.sgml + journal-api.sgml libata.sgml PS := $(patsubst %.sgml, %.ps, $(BOOKS)) PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) @@ -79,6 +79,16 @@ $(TOPDIR)/scripts/docgen $(TOPDIR)/arch/i386/kernel/mca.c \ mcabook.sgml +libata.sgml: libata.tmpl $(TOPDIR)/drivers/scsi/libata-core.c \ + $(TOPDIR)/drivers/scsi/libata-scsi.c \ + $(TOPDIR)/drivers/scsi/sata_sil.c \ + $(TOPDIR)/drivers/scsi/sata_via.c + $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/scsi/libata-core.c \ + $(TOPDIR)/drivers/scsi/libata-scsi.c \ + $(TOPDIR)/drivers/scsi/sata_sil.c \ + $(TOPDIR)/drivers/scsi/sata_via.c \ + < libata.tmpl > libata.sgml + videobook.sgml: videobook.tmpl $(TOPDIR)/drivers/media/video/videodev.c $(TOPDIR)/scripts/docgen $(TOPDIR)/drivers/media/video/videodev.c \ videobook.sgml diff -urN linux-2.4.26/Documentation/DocBook/libata.tmpl linux-2.4.27/Documentation/DocBook/libata.tmpl --- linux-2.4.26/Documentation/DocBook/libata.tmpl 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/Documentation/DocBook/libata.tmpl 2004-08-07 16:26:04.533341806 -0700 @@ -0,0 +1,85 @@ + + + + + libATA Developer's Guide + + + + Jeff + Garzik + + + + + 2003 + Jeff Garzik + + + + + The contents of this file are subject to the Open + Software License version 1.1 that can be found at + http://www.opensource.org/licenses/osl-1.1.txt and is included herein + by reference. + + + + Alternatively, the contents of this file may be used under the terms + of the GNU General Public License version 2 (the "GPL") as distributed + in the kernel source COPYING file, in which case the provisions of + the GPL are applicable instead of the above. If you wish to allow + the use of your version of this file only under the terms of the + GPL and not to allow others to use your version of this file under + the OSL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the GPL. + If you do not delete the provisions above, a recipient may use your + version of this file under either the OSL or the GPL. + + + + + + + + + Thanks + + The bulk of the ATA knowledge comes thanks to long conversations with + Andre Hedrick (www.linux-ide.org). + + + Thanks to Alan Cox for pointing out similarities + between SATA and SCSI, and in general for motivation to hack on + libata. + + + libata's device detection + method, ata_pio_devchk, and in general all the early probing was + based on extensive study of Hale Landis's probe/reset code in his + ATADRVR driver (www.ata-atapi.com). + + + + + libata Library +!Edrivers/scsi/libata-core.c + + + + libata Core Internals +!Idrivers/scsi/libata-core.c + + + + libata SCSI translation/emulation +!Edrivers/scsi/libata-scsi.c +!Idrivers/scsi/libata-scsi.c + + + + ata_sil Internals +!Idrivers/scsi/sata_sil.c + + + diff -urN linux-2.4.26/Documentation/cciss.txt linux-2.4.27/Documentation/cciss.txt --- linux-2.4.26/Documentation/cciss.txt 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/Documentation/cciss.txt 2004-08-07 16:26:04.534341847 -0700 @@ -14,6 +14,8 @@ * SA 6400 * SA 6400 U320 Expansion Module * SA 6i + * SA 6422 + * SA V100 If nodes are not already created in the /dev/cciss directory diff -urN linux-2.4.26/Documentation/crypto/api-intro.txt linux-2.4.27/Documentation/crypto/api-intro.txt --- linux-2.4.26/Documentation/crypto/api-intro.txt 2004-04-14 06:05:24.000000000 -0700 +++ linux-2.4.27/Documentation/crypto/api-intro.txt 2004-08-07 16:26:04.534341847 -0700 @@ -187,6 +187,7 @@ Brian Gladman (AES) Kartikey Mahendra Bhatt (CAST6) Jon Oberheide (ARC4) + Jouni Malinen (Michael MIC) SHA1 algorithm contributors: Jean-Francois Dive diff -urN linux-2.4.26/Documentation/filesystems/hpfs.txt linux-2.4.27/Documentation/filesystems/hpfs.txt --- linux-2.4.26/Documentation/filesystems/hpfs.txt 2001-06-11 19:15:27.000000000 -0700 +++ linux-2.4.27/Documentation/filesystems/hpfs.txt 2004-08-07 16:26:04.535341888 -0700 @@ -1,5 +1,5 @@ -Read/Write HPFS 2.05 -1998-2001, Mikulas Patocka +Read/Write HPFS 2.09 +1998-2004, Mikulas Patocka email: mikulas@artax.karlin.mff.cuni.cz homepage: http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi @@ -283,6 +283,14 @@ 2.05 Fixed crash when got mount parameters without = Fixed crash when allocation of anode failed due to full disk Fixed some crashes when block io or inode allocation failed +2.06 Fixed some crash on corrupted disk structures + Better allocation strategy + Reschedule points added so that it doesn't lock CPU long time + It should work in read-only mode on Warp Server +2.07 More fixes for Warp Server. Now it really works +2.08 Creating new files is not so slow on large disks + An attempt to sync deleted file does not generate filesystem error +2.09 Fixed error on extremly fragmented files vim: set textwidth=80: diff -urN linux-2.4.26/Documentation/filesystems/xfs.txt linux-2.4.27/Documentation/filesystems/xfs.txt --- linux-2.4.26/Documentation/filesystems/xfs.txt 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/Documentation/filesystems/xfs.txt 2004-08-07 16:26:04.536341929 -0700 @@ -123,9 +123,15 @@ in /proc/fs/xfs/stat. It then immediately reset to "0". fs.xfs.sync_interval (Min: HZ Default: 30*HZ Max: 60*HZ) - The interval at which the xfssyncd thread for xfs filesystems - flushes metadata out to disk. This thread will flush log - activity out, and do some processing on unlinked inodes + The interval at which the xfssyncd thread flushes metadata + out to disk. This thread will flush log activity out, and + do some processing on unlinked inodes. + + fs.xfs.age_buffer (Min: 1*HZ Default: 15*HZ Max: 300*HZ) + The age at which xfsbufd flushes dirty metadata buffers to disk. + + fs.xfs.flush_interval (Min: HZ/2 Default: HZ Max: 30*HZ) + The interval at which xfsbufd scans the dirty metadata buffers list. fs.xfs.error_level (Min: 0 Default: 3 Max: 11) A volume knob for error reporting when internal errors occur. @@ -190,14 +196,3 @@ Setting this to "1" will cause the "noatime" flag set by the chattr(1) command on a directory to be inherited by files in that directory. - - vm.pagebuf.stats_clear (Min: 0 Default: 0 Max: 1) - Setting this to "1" clears accumulated pagebuf statistics - in /proc/fs/pagebuf/stat. It then immediately reset to "0". - - vm.pagebuf.flush_age (Min: 1*HZ Default: 15*HZ Max: 300*HZ) - The age at which dirty metadata buffers are flushed to disk - - vm.pagebuf.flush_int (Min: HZ/2 Default: HZ Max: 30*HZ) - The interval at which the list of dirty metadata buffers is - scanned. diff -urN linux-2.4.26/Documentation/ioctl-number.txt linux-2.4.27/Documentation/ioctl-number.txt --- linux-2.4.26/Documentation/ioctl-number.txt 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/Documentation/ioctl-number.txt 2004-08-07 16:26:04.536341929 -0700 @@ -183,5 +183,6 @@ 0xB1 00-1F PPPoX 0xCB 00-1F CBM serial IEC bus in development: - +0xF3 00-3F linux/sisfb.h SiS framebuffer device driver + 0xFE 00-9F Logical Volume Manager diff -urN linux-2.4.26/Documentation/kernel-parameters.txt linux-2.4.27/Documentation/kernel-parameters.txt --- linux-2.4.26/Documentation/kernel-parameters.txt 2004-04-14 06:05:24.000000000 -0700 +++ linux-2.4.27/Documentation/kernel-parameters.txt 2004-08-07 16:26:04.537341970 -0700 @@ -69,8 +69,9 @@ 53c7xx= [HW,SCSI] Amiga SCSI controllers. acpi= [HW,ACPI] Advanced Configuration and Power Interface - force Force ACPI on, even if blacklisted platform - off Disable ACPI + force Enable ACPI if default was off + off Disable ACPI if default was on + noirq Do not use ACPI for IRQ routing (see pci=noacpi) ht Limit ACPI to boot-time LAPIC enumeration for HT, disabling the run-time AML interpreter. strict Be less tolerant of platforms that are not diff -urN linux-2.4.26/Documentation/networking/bridge.txt linux-2.4.27/Documentation/networking/bridge.txt --- linux-2.4.26/Documentation/networking/bridge.txt 2000-11-09 15:57:53.000000000 -0800 +++ linux-2.4.27/Documentation/networking/bridge.txt 2004-08-07 16:26:04.537341970 -0700 @@ -1,11 +1,8 @@ -In order to use the ethernet bridging functionality you'll need the -userspace tools available at http://www.math.leidenuniv.nl/~buytenh/bridge. -The tarball available there contains extensive documentation, but if you -still have questions, don't hesitate to post to the mailing list (more info -at http://www.math.leidenuniv.nl/mailman/listinfo/bridge). You can also -mail me at buytenh@gnu.org. +In order to use the Ethernet bridging functionality, you'll need the +userspace tools. These programs and documentation are available +at http://bridge.sourceforge.net. The download page is +http://prdownloads.sourceforge.net/bridge. +If you still have questions, don't hesitate to post to the mailing list +(more info http://lists.osdl.org/mailman/listinfo/bridge). - -Lennert Buytenhek - diff -urN linux-2.4.26/Documentation/networking/ip-sysctl.txt linux-2.4.27/Documentation/networking/ip-sysctl.txt --- linux-2.4.26/Documentation/networking/ip-sysctl.txt 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/Documentation/networking/ip-sysctl.txt 2004-08-07 16:26:04.538342011 -0700 @@ -289,6 +289,44 @@ changed would be a Beowulf compute cluster. Default: 0 +tcp_vegas_cong_avoid - BOOLEAN + Enable TCP Vegas congestion avoidance algorithm. + TCP Vegas is a sender-side only change to TCP that anticipates + the onset of congestion by estimating the bandwidth. TCP Vegas + adjusts the sending rate by modifying the congestion + window. TCP Vegas should provide less packet loss, but it is + not as aggressive as TCP Reno. + Default:0 + +tcp_bic - BOOLEAN + Enable BIC TCP congestion control algorithm. + BIC-TCP is a sender-side only change that ensures a linear RTT + fairness under large windows while offering both scalability and + bounded TCP-friendliness. The protocol combines two schemes + called additive increase and binary search increase. When the + congestion window is large, additive increase with a large + increment ensures linear RTT fairness as well as good + scalability. Under small congestion windows, binary search + increase provides TCP friendliness. + Default: 0 + +tcp_bic_low_window - INTEGER + Sets the threshold window (in packets) where BIC TCP starts to + adjust the congestion window. Below this threshold BIC TCP behaves + the same as the default TCP Reno. + Default: 14 + +tcp_bic_fast_convergence - BOOLEAN + Forces BIC TCP to more quickly respond to changes in congestion + window. Allows two flows sharing the same connection to converge + more rapidly. + Default: 1 + +tcp_default_win_scale - INTEGER + Sets the minimum window scale TCP will negotiate for on all + conections. + Default: 7 + ip_local_port_range - 2 INTEGERS Defines the local port range that is used by TCP and UDP to choose the local port. The first number is the first, the @@ -603,9 +641,11 @@ disabled if local forwarding is enabled. autoconf - BOOLEAN - Configure link-local addresses using L2 hardware addresses. + Autoconfigure addresses using Prefix Information in Router + Advertisements. - Default: TRUE + Functional default: enabled if accept_ra is enabled. + disabled if accept_ra is disabled. dad_transmits - INTEGER The amount of Duplicate Address Detection probes to send. diff -urN linux-2.4.26/Documentation/networking/packet_mmap.txt linux-2.4.27/Documentation/networking/packet_mmap.txt --- linux-2.4.26/Documentation/networking/packet_mmap.txt 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/Documentation/networking/packet_mmap.txt 2004-08-07 16:26:04.540342093 -0700 @@ -1,11 +1,3 @@ - -DaveM: - -If you agree with it I will send two small patches to modify -kernel's configure help. - - Ulisses - -------------------------------------------------------------------------------- + ABSTRACT -------------------------------------------------------------------------------- @@ -405,8 +397,3 @@ Jesse Brandeburg, for fixing my grammathical/spelling errors ->>> EOF -- -To unsubscribe from this list: send the line "unsubscribe linux-net" in -the body of a message to majordomo@vger.kernel.org -More majordomo info at http://vger.kernel.org/majordomo-info.html \ No newline at end of file diff -urN linux-2.4.26/Documentation/usb/silverlink.txt linux-2.4.27/Documentation/usb/silverlink.txt --- linux-2.4.26/Documentation/usb/silverlink.txt 2002-11-28 15:53:08.000000000 -0800 +++ linux-2.4.27/Documentation/usb/silverlink.txt 2004-08-07 16:26:04.540342093 -0700 @@ -8,7 +8,7 @@ INTRODUCTION: This is a driver for the TI-GRAPH LINK USB (aka SilverLink) cable, a cable -designed by TI for connecting their TI8x/9x calculators to a computer +designed by TI for connecting their TI8x/9x graphing handhelds to a computer (PC or Mac usually). If you need more information, please visit the 'SilverLink drivers' homepage @@ -16,10 +16,8 @@ WHAT YOU NEED: -A TI calculator of course and a program capable to communicate with your -calculator. -TiLP will work for sure (since I am his developer !). yal92 may be able to use -it by changing tidev for tiglusb (may require some hacking...). +A TI calculator/handheld of course and a program capable to communicate with +your calculator. A good choice is TiLP (http://www.tilp.info). HOW TO USE IT: @@ -58,14 +56,19 @@ QUIRKS: The following problem seems to be specific to the link cable since it appears -on all platforms (Linux, Windows, Mac OS-X). +on all platforms (Linux, Windows, Mac OS-X). A guy told me it was a common but +weird behaviour with Cypress microcontrollers (it uses an CY7C64013). -In some very particular cases, the driver returns with success but +In some very particular cases, the driver returns with success (no error) but without any data. The application should retry a read operation at least once. +This problem and the need to issue IOCTL_TIUSB_RESET_PIPES before doing any +packet transfer (like TI's software do) make this driver difficult to use in +pure raw access. + HOW TO CONTACT US: -You can email me at roms@lpg.ticalc.org. Please prefix the subject line +You can email me at roms@tilp.info. Please prefix the subject line with "TIGLUSB: " so that I am certain to notice your message. You can also mail JB at jb@jblache.org: he has written the first release of this driver but he better knows the Mac OS-X driver. @@ -73,4 +76,4 @@ CREDITS: The code is based on dabusb.c, printer.c and scanner.c ! -The driver has been developed independantly of Texas Instruments. +The driver has been developed without any support from Texas Instruments Inc. diff -urN linux-2.4.26/MAINTAINERS linux-2.4.27/MAINTAINERS --- linux-2.4.26/MAINTAINERS 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/MAINTAINERS 2004-08-07 16:26:04.542342175 -0700 @@ -113,6 +113,12 @@ W: http://sourceforge.net/projects/gkernel/ S: Maintained +8169 10/100/1000 GIGABIT ETHERNET DRIVER +P: Francois Romieu +M: romieu@fr.zoreil.com +L: netdev@oss.sgi.com +S: Maintained + 8250/16?50 (AND CLONE UARTS) SERIAL DRIVER P: Theodore Ts'o M: tytso@mit.edu @@ -328,6 +334,8 @@ M: maxk@qualcomm.com L: bluez-devel@lists.sf.net W: http://bluez.sf.net +W: http://www.bluez.org +W: http://www.holtmann.org/linux/bluetooth/ S: Maintained BLUETOOTH RFCOMM LAYER @@ -335,7 +343,6 @@ M: marcel@holtmann.org P: Maxim Krasnyansky M: maxk@qualcomm.com -W: http://bluez.sf.net S: Maintained BLUETOOTH BNEP LAYER @@ -343,65 +350,60 @@ M: marcel@holtmann.org P: Maxim Krasnyansky M: maxk@qualcomm.com -W: http://bluez.sf.net S: Maintained BLUETOOTH CMTP LAYER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ S: Maintained -BLUETOOTH HCI USB DRIVER +BLUETOOTH HCI UART DRIVER P: Marcel Holtmann M: marcel@holtmann.org P: Maxim Krasnyansky M: maxk@qualcomm.com -W: http://bluez.sf.net S: Maintained -BLUETOOTH HCI UART DRIVER +BLUETOOTH HCI USB DRIVER P: Marcel Holtmann M: marcel@holtmann.org P: Maxim Krasnyansky M: maxk@qualcomm.com -W: http://bluez.sf.net +S: Maintained + +BLUETOOTH HCI BCM203X DRIVER +P: Marcel Holtmann +M: marcel@holtmann.org S: Maintained BLUETOOTH HCI BFUSB DRIVER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ S: Maintained BLUETOOTH HCI DTL1 DRIVER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ S: Maintained BLUETOOTH HCI BLUECARD DRIVER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ S: Maintained BLUETOOTH HCI BT3C DRIVER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ S: Maintained BLUETOOTH HCI BTUART DRIVER P: Marcel Holtmann M: marcel@holtmann.org -W: http://www.holtmann.org/linux/bluetooth/ S: Maintained BLUETOOTH HCI VHCI DRIVER P: Maxim Krasnyansky M: maxk@qualcomm.com -W: http://bluez.sf.net S: Maintained BONDING DRIVER @@ -954,11 +956,21 @@ M: tigran@veritas.com S: Maintained +INTEL PRO/100 ETHERNET SUPPORT +P: John Ronciak +M: john.ronciak@intel.com +P: Ganesh Venkatesan +M: Ganesh.Venkatesan@intel.com +W: http://sourceforge.net/projects/e1000/ +S: Supported + INTEL PRO/1000 GIGABIT ETHERNET SUPPORT P: Jeb Cramer M: cramerj@intel.com -P: Scott Feldman -M: scott.feldman@intel.com +P: John Ronciak +M: john.ronciak@intel.com +P: Ganesh Venkatesan +M: Ganesh.Venkatesan@intel.com W: http://sourceforge.net/projects/e1000/ S: Supported @@ -1141,11 +1153,14 @@ L: linux-scsi@vger.kernel.org S: Maintained -M68K -P: Jes Sorensen -M: jes@trained-monkey.org -W: http://www.clark.net/pub/lawrencc/linux/index.html +M68K ARCHITECTURE +P: Geert Uytterhoeven +M: geert@linux-m68k.org +P: Roman Zippel +M: zippel@linux-m68k.org L: linux-m68k@lists.linux-m68k.org +W: http://www.linux-m68k.org/ +W: http://linux-m68k-cvs.ubb.ca/ S: Maintained M68K ON APPLE MACINTOSH @@ -1306,6 +1321,8 @@ M: jmorris@intercode.com.au P: Hideaki YOSHIFUJI M: yoshfuji@linux-ipv6.org +P: Patrick McHardy +M: kaber@coreworks.de L: netdev@oss.sgi.com S: Maintained @@ -1638,6 +1655,12 @@ W: http://www.weinigel.se S: Supported +SERIAL ATA (SATA) SUBSYSTEM: +P: Jeff Garzik +M: jgarzik@pobox.com +L: linux-ide@vger.kernel.org +S: Supported + SGI VISUAL WORKSTATION 320 AND 540 P: Bent Hagemark M: bh@sgi.com @@ -2161,7 +2184,7 @@ XFS FILESYSTEM P: Silicon Graphics Inc -M: owner-xfs@oss.sgi.com +M: xfs-masters@oss.sgi.com M: nathans@sgi.com L: linux-xfs@oss.sgi.com W: http://oss.sgi.com/projects/xfs diff -urN linux-2.4.26/Makefile linux-2.4.27/Makefile --- linux-2.4.26/Makefile 2004-04-14 06:05:41.000000000 -0700 +++ linux-2.4.27/Makefile 2004-08-07 16:26:07.119448065 -0700 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 4 -SUBLEVEL = 26 +SUBLEVEL = 27 EXTRAVERSION = KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) @@ -175,6 +175,7 @@ DRIVERS-$(CONFIG_PPC32) += drivers/macintosh/macintosh.o DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.o DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o +DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o DRIVERS-$(CONFIG_VT) += drivers/video/video.o DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o @@ -186,7 +187,6 @@ DRIVERS-$(CONFIG_HIL) += drivers/hil/hil.o DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda.o -DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.o DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.o DRIVERS-$(CONFIG_MD) += drivers/md/mddev.o DRIVERS-$(CONFIG_GSC) += drivers/gsc/gscbus.o diff -urN linux-2.4.26/arch/alpha/kernel/sys_eiger.c linux-2.4.27/arch/alpha/kernel/sys_eiger.c --- linux-2.4.26/arch/alpha/kernel/sys_eiger.c 2001-10-12 15:35:53.000000000 -0700 +++ linux-2.4.27/arch/alpha/kernel/sys_eiger.c 2004-08-07 16:26:04.543342216 -0700 @@ -193,27 +193,20 @@ case 0x0f: bridge_count = 4; break; /* 4 */ }; - /* Check first for the built-in bridges on hose 0. */ - if (hose->index == 0 - && PCI_SLOT(dev->bus->self->devfn) > 20-bridge_count) { - slot = PCI_SLOT(dev->devfn); - } else { - /* Must be a card-based bridge. */ - do { - /* Check for built-in bridges on hose 0. */ - if (hose->index == 0 - && (PCI_SLOT(dev->bus->self->devfn) - > 20 - bridge_count)) { - slot = PCI_SLOT(dev->devfn); - break; - } - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); - - /* Move up the chain of bridges. */ - dev = dev->bus->self; - /* Slot of the next bridge. */ + slot = PCI_SLOT(dev->devfn); + while (dev->bus->self) { + /* Check for built-in bridges on hose 0. */ + if (hose->index == 0 + && (PCI_SLOT(dev->bus->self->devfn) + > 20 - bridge_count)) { slot = PCI_SLOT(dev->devfn); - } while (dev->bus->self); + break; + } + /* Must be a card-based bridge. */ + pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + + /* Move up the chain of bridges. */ + dev = dev->bus->self; } *pinp = pin; return slot; diff -urN linux-2.4.26/arch/alpha/kernel/sys_takara.c linux-2.4.27/arch/alpha/kernel/sys_takara.c --- linux-2.4.26/arch/alpha/kernel/sys_takara.c 2000-10-27 10:55:01.000000000 -0700 +++ linux-2.4.27/arch/alpha/kernel/sys_takara.c 2004-08-07 16:26:04.544342258 -0700 @@ -230,8 +230,12 @@ int slot = PCI_SLOT(dev->devfn); int pin = *pinp; unsigned int ctlreg = inl(0x500); - unsigned int busslot = PCI_SLOT(dev->bus->self->devfn); + unsigned int busslot; + if (!dev->bus->self) + return slot; + + busslot = PCI_SLOT(dev->bus->self->devfn); /* Check for built-in bridges. */ if (dev->bus->number != 0 && busslot > 16 diff -urN linux-2.4.26/arch/cris/drivers/eeprom.c linux-2.4.27/arch/cris/drivers/eeprom.c --- linux-2.4.26/arch/cris/drivers/eeprom.c 2003-08-25 04:44:39.000000000 -0700 +++ linux-2.4.27/arch/cris/drivers/eeprom.c 2004-08-07 16:26:04.545342299 -0700 @@ -506,7 +506,7 @@ static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off) { int read=0; - unsigned long p = file->f_pos; + unsigned long p = *off; unsigned char page; @@ -540,7 +540,7 @@ return -EFAULT; } - if( (p + count) > eeprom.size) + if(count > eeprom.size - p) { /* truncate count */ count = eeprom.size - p; @@ -560,7 +560,7 @@ if(read > 0) { - file->f_pos += read; + *off = p + read; } eeprom.busy--; @@ -605,7 +605,7 @@ { restart = 0; written = 0; - p = file->f_pos; + p = *off; while( (written < count) && (p < eeprom.size)) @@ -733,10 +733,10 @@ eeprom.busy--; wake_up_interruptible(&eeprom.wait_q); - if (written == 0 && file->f_pos >= eeprom.size){ + if (written == 0 && p >= eeprom.size){ return -ENOSPC; } - file->f_pos += written; + *off = p; return written; } diff -urN linux-2.4.26/arch/i386/defconfig linux-2.4.27/arch/i386/defconfig --- linux-2.4.26/arch/i386/defconfig 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/arch/i386/defconfig 2004-08-07 16:26:04.546342340 -0700 @@ -105,6 +105,11 @@ # CONFIG_HOTPLUG_PCI_COMPAQ is not set # CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set # CONFIG_HOTPLUG_PCI_IBM is not set +# CONFIG_HOTPLUG_PCI_SHPC is not set +# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set +# CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY is not set +# CONFIG_HOTPLUG_PCI_PCIE is not set +# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y @@ -151,6 +156,7 @@ # CONFIG_CISS_MONITOR_THREAD is not set # CONFIG_BLK_DEV_DAC960 is not set # CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_SX8 is not set # CONFIG_BLK_DEV_LOOP is not set # CONFIG_BLK_DEV_NBD is not set # CONFIG_BLK_DEV_RAM is not set @@ -266,6 +272,7 @@ # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set # CONFIG_BLK_DEV_CMD64X is not set # CONFIG_BLK_DEV_TRIFLEX is not set # CONFIG_BLK_DEV_CY82C693 is not set @@ -294,6 +301,7 @@ # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set # CONFIG_BLK_DEV_ATARAID_SII is not set # @@ -337,6 +345,14 @@ # CONFIG_SCSI_AM53C974 is not set # CONFIG_SCSI_MEGARAID is not set # CONFIG_SCSI_MEGARAID2 is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_SATA_SVW is not set +# CONFIG_SCSI_SATA_PROMISE is not set +# CONFIG_SCSI_SATA_SX4 is not set +# CONFIG_SCSI_SATA_SIL is not set +# CONFIG_SCSI_SATA_SIS is not set +# CONFIG_SCSI_SATA_VIA is not set +# CONFIG_SCSI_SATA_VITESSE is not set # CONFIG_SCSI_BUSLOGIC is not set # CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set @@ -453,6 +469,7 @@ # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set # CONFIG_8139CP is not set diff -urN linux-2.4.26/arch/i386/kernel/acpi.c linux-2.4.27/arch/i386/kernel/acpi.c --- linux-2.4.26/arch/i386/kernel/acpi.c 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/acpi.c 2004-08-07 16:26:04.547342381 -0700 @@ -59,7 +59,10 @@ Boot-time Configuration -------------------------------------------------------------------------- */ -int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ +#ifdef CONFIG_ACPI_PCI +int acpi_noirq __initdata; /* skip ACPI IRQ initialization */ +int acpi_pci_disabled __initdata; /* skip ACPI PCI scan and IRQ initialization */ +#endif int acpi_ht __initdata = 1; /* enable HT */ enum acpi_irq_model_id acpi_irq_model; @@ -105,6 +108,35 @@ return ((unsigned char *) base + offset); } +#ifdef CONFIG_ACPI_MMCONFIG + +u32 pci_mmcfg_base_addr; + +static int __init +acpi_parse_mcfg(unsigned long phys_addr, + unsigned long size) +{ + struct acpi_table_mcfg *mcfg = NULL; + + if (!phys_addr || !size) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *) __acpi_map_table(phys_addr, size); + if (!mcfg) { + printk(KERN_WARNING PREFIX "Unable to map MCFG\n"); + return -ENODEV; + } + + if (mcfg->base_reserved) { + printk(KERN_ERR PREFIX "MMCONFIG not in low 4GB of memory\n"); + return -ENODEV; + } + + pci_mmcfg_base_addr = mcfg->base_address; + + return 0; +} +#endif /* CONFIG_ACPI_MMCONFIG */ #ifdef CONFIG_X86_LOCAL_APIC @@ -406,6 +438,15 @@ return result; } +#ifdef CONFIG_ACPI_MMCONFIG + result = acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); + if (result < 0) { + printk(KERN_ERR PREFIX "Error %d parsing MCFG\n", result); + } else if (result > 1) { + printk(KERN_WARNING PREFIX "Multiple MCFG tables exist\n"); + } +#endif + #ifdef CONFIG_X86_LOCAL_APIC /* @@ -491,6 +532,11 @@ return 1; } + result = mp_irqs_alloc(); /* Dynamically allocate mp_irqs[] */ + if (result < 0) { + acpi_noirq = 1; + return result; + } result = acpi_table_parse_madt(ACPI_MADT_IOAPIC, acpi_parse_ioapic); if (!result) { @@ -502,9 +548,6 @@ return result; } - /* Build a default routing table for legacy (ISA) interrupts. */ - mp_config_acpi_legacy_irqs(); - /* Record sci_int for use when looking for MADT sci_int override */ acpi_table_parse(ACPI_FADT, acpi_parse_fadt); @@ -522,6 +565,9 @@ if (!acpi_sci_override_gsi) acpi_sci_ioapic_setup(acpi_fadt.sci_int, 0, 0); + /* Fill in identity legacy mapings where no override */ + mp_config_acpi_legacy_irqs(); + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); diff -urN linux-2.4.26/arch/i386/kernel/edd.c linux-2.4.27/arch/i386/kernel/edd.c --- linux-2.4.26/arch/i386/kernel/edd.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/i386/kernel/edd.c 2004-08-07 16:26:04.548342422 -0700 @@ -46,9 +46,8 @@ MODULE_DESCRIPTION("proc interface to BIOS EDD information"); MODULE_LICENSE("GPL"); -#define EDD_VERSION "0.10 2003-Dec-05" +#define EDD_VERSION "0.11 2004-Jun-21" #define EDD_DEVICE_NAME_SIZE 16 -#define REPORT_URL "http://domsch.com/linux/edd30/results.html" #define left (count - (p - page) - 1) @@ -312,10 +311,6 @@ } out: - p += snprintf(p, left, "\nPlease check %s\n", REPORT_URL); - p += snprintf(p, left, "to see if this device has been reported. If not,\n"); - p += snprintf(p, left, "please send the information requested there.\n"); - return proc_calc_metrics(page, start, off, count, eof, (p - page)); } @@ -405,7 +400,7 @@ return proc_calc_metrics(page, start, off, count, eof, 0); } - p += snprintf(p, left, "0x%x\n", info->params.num_default_cylinders); + p += snprintf(p, left, "%u\n", info->params.num_default_cylinders); return proc_calc_metrics(page, start, off, count, eof, (p - page)); } @@ -418,7 +413,7 @@ return proc_calc_metrics(page, start, off, count, eof, 0); } - p += snprintf(p, left, "0x%x\n", info->params.num_default_heads); + p += snprintf(p, left, "%u\n", info->params.num_default_heads); return proc_calc_metrics(page, start, off, count, eof, (p - page)); } @@ -431,7 +426,7 @@ return proc_calc_metrics(page, start, off, count, eof, 0); } - p += snprintf(p, left, "0x%x\n", info->params.sectors_per_track); + p += snprintf(p, left, "%u\n", info->params.sectors_per_track); return proc_calc_metrics(page, start, off, count, eof, (p - page)); } @@ -444,7 +439,7 @@ return proc_calc_metrics(page, start, off, count, eof, 0); } - p += snprintf(p, left, "0x%llx\n", info->params.number_of_sectors); + p += snprintf(p, left, "%llu\n", info->params.number_of_sectors); return proc_calc_metrics(page, start, off, count, eof, (p - page)); } diff -urN linux-2.4.26/arch/i386/kernel/i8259.c linux-2.4.27/arch/i386/kernel/i8259.c --- linux-2.4.26/arch/i386/kernel/i8259.c 2001-09-17 23:03:09.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/i8259.c 2004-08-07 16:26:04.548342422 -0700 @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -498,7 +499,8 @@ outb(LATCH >> 8 , 0x40); /* MSB */ #ifndef CONFIG_VISWS - setup_irq(2, &irq2); + if (!acpi_ioapic) + setup_irq(2, &irq2); #endif /* diff -urN linux-2.4.26/arch/i386/kernel/io_apic.c linux-2.4.27/arch/i386/kernel/io_apic.c --- linux-2.4.26/arch/i386/kernel/io_apic.c 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/io_apic.c 2004-08-07 16:26:04.550342504 -0700 @@ -1691,18 +1691,10 @@ /* * - * IRQ's that are handled by the old PIC in all cases: + * IRQ's that are handled by the PIC in the MPS IOAPIC case. * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. * Linux doesn't really care, as it's not actually used * for any interrupt handling anyway. - * - There used to be IRQ13 here as well, but all - * MPS-compliant must not use it for FPU coupling and we - * want to use exception 16 anyway. And there are - * systems who connect it to an I/O APIC for other uses. - * Thus we don't mark it special any longer. - * - * Additionally, something is definitely wrong with irq9 - * on PIIX4 boards. */ #define PIC_IRQS (1<<2) @@ -1710,7 +1702,11 @@ { enable_IO_APIC(); - io_apic_irqs = ~PIC_IRQS; + if (acpi_ioapic) + io_apic_irqs = ~0; /* all IRQs go through IOAPIC */ + else + io_apic_irqs = ~PIC_IRQS; + printk("ENABLING IO-APIC IRQs\n"); /* @@ -1872,7 +1868,7 @@ entry.vector = assign_irq_vector(irq); - printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " + Dprintk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " "IRQ %d Mode:%i Active:%i)\n", ioapic, mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, edge_level, active_high_low); diff -urN linux-2.4.26/arch/i386/kernel/mpparse.c linux-2.4.27/arch/i386/kernel/mpparse.c --- linux-2.4.26/arch/i386/kernel/mpparse.c 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/mpparse.c 2004-08-07 16:26:04.551342545 -0700 @@ -1104,6 +1104,20 @@ } +/* allocate mp_irqs[] for ACPI parsing table parsing */ +int __init mp_irqs_alloc() +{ + int size = (MAX_IRQ_SOURCES * sizeof(int)) * 4; + + mp_irqs = (struct mpc_config_intsrc *)alloc_bootmem(size); + if (!mp_irqs) { + printk(KERN_ERR "mp_irqs_alloc(): alloc_bootmem(%d) failed!\n", size); + return -ENOMEM; + } + return 0; +} + + void __init mp_override_legacy_irq ( u8 bus_irq, u8 polarity, @@ -1111,8 +1125,6 @@ u32 global_irq) { struct mpc_config_intsrc intsrc; - int i = 0; - int found = 0; int ioapic = -1; int pin = -1; @@ -1145,23 +1157,9 @@ (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus, intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, intsrc.mpc_dstirq); - /* - * If an existing [IOAPIC.PIN -> IRQ] routing entry exists we override it. - * Otherwise create a new entry (e.g. global_irq == 2). - */ - for (i = 0; i < mp_irq_entries; i++) { - if ((mp_irqs[i].mpc_srcbus == intsrc.mpc_srcbus) - && (mp_irqs[i].mpc_srcbusirq == intsrc.mpc_srcbusirq)) { - mp_irqs[i] = intsrc; - found = 1; - break; - } - } - if (!found) { - mp_irqs[mp_irq_entries] = intsrc; - if (++mp_irq_entries == MAX_IRQ_SOURCES) - panic("Max # of irq sources exceeded!\n"); - } + mp_irqs[mp_irq_entries] = intsrc; + if (++mp_irq_entries == MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!\n"); return; } @@ -1179,7 +1177,6 @@ int count; count = (MAX_MP_BUSSES * sizeof(int)) * 4; - count += (MAX_IRQ_SOURCES * sizeof(int)) * 4; bus_data = alloc_bootmem(count); if (!bus_data) { panic("Fatal: can't allocate bus memory for ACPI legacy IRQ!"); @@ -1188,7 +1185,7 @@ mp_bus_id_to_node = (int *)&bus_data[(MAX_MP_BUSSES * sizeof(int))]; mp_bus_id_to_local = (int *)&bus_data[(MAX_MP_BUSSES * sizeof(int)) * 2]; mp_bus_id_to_pci_bus = (int *)&bus_data[(MAX_MP_BUSSES * sizeof(int)) * 3]; - mp_irqs = (struct mpc_config_intsrc *)&bus_data[(MAX_MP_BUSSES * sizeof(int)) * 4]; + for (i = 0; i < MAX_MP_BUSSES; ++i) mp_bus_id_to_pci_bus[i] = -1; @@ -1206,13 +1203,20 @@ return; /* - * Use the default configuration for the IRQs 0-15. These may be + * Use the default configuration for the IRQs 0-15. Unless * overriden by (MADT) interrupt source override entries. */ for (i = 0; i < 16; i++) { + int idx; - if (i == 2) - continue; /* Don't connect IRQ2 */ + for (idx = 0; idx < mp_irq_entries; idx++) + if (mp_irqs[idx].mpc_srcbus == MP_ISA_BUS && + (mp_irqs[idx].mpc_dstapic == mp_ioapics[ioapic].mpc_apicid) && + (mp_irqs[idx].mpc_srcbusirq == i || + mp_irqs[idx].mpc_dstirq == i)) + break; + if (idx != mp_irq_entries) + continue; /* IRQ already used */ mp_irqs[mp_irq_entries].mpc_type = MP_INTSRC; mp_irqs[mp_irq_entries].mpc_irqflag = 0; /* Conforming */ @@ -1308,11 +1312,13 @@ if (!io_apic_set_pci_routing(ioapic, ioapic_pin, irq, edge_level, active_high_low)) entry->irq = irq; - printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> IRQ %d\n", - entry->id.segment, entry->id.bus, - entry->id.device, ('A' + entry->pin), - mp_ioapic_routing[ioapic].apic_id, ioapic_pin, - entry->irq); + printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d" + " -> IRQ %d %s %s\n", entry->id.segment, entry->id.bus, + entry->id.device, ('A' + entry->pin), + mp_ioapic_routing[ioapic].apic_id, ioapic_pin, + entry->irq, edge_level ? "level" : "edge", + active_high_low ? "low" : "high"); + } print_IO_APIC(); diff -urN linux-2.4.26/arch/i386/kernel/mtrr.c linux-2.4.27/arch/i386/kernel/mtrr.c --- linux-2.4.26/arch/i386/kernel/mtrr.c 2003-06-13 07:51:29.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/mtrr.c 2004-08-07 16:26:04.553342627 -0700 @@ -1648,11 +1648,17 @@ static ssize_t mtrr_read (struct file *file, char *buf, size_t len, loff_t *ppos) { - if (*ppos >= ascii_buf_bytes) return 0; - if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos; - if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT; - *ppos += len; - return len; + loff_t pos = *ppos; + if (pos < 0 || pos >= ascii_buf_bytes) + return 0; + if (len > ascii_buf_bytes - pos) + len = ascii_buf_bytes - pos; + if (copy_to_user(buf, ascii_buffer + pos, len)) + return -EFAULT; + pos += len; + *ppos = pos; + + return len; } /* End Function mtrr_read */ static ssize_t mtrr_write (struct file *file, const char *buf, size_t len, diff -urN linux-2.4.26/arch/i386/kernel/pci-irq.c linux-2.4.27/arch/i386/kernel/pci-irq.c --- linux-2.4.26/arch/i386/kernel/pci-irq.c 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/pci-irq.c 2004-08-07 16:26:04.554342668 -0700 @@ -1067,6 +1067,7 @@ { u8 pin; extern int interrupt_line_quirk; + struct pci_dev *temp_dev; pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { @@ -1076,9 +1077,44 @@ if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5)) return; - if (io_apic_assign_pci_irqs) - msg = " Probably buggy MP table."; - else if (pci_probe & PCI_BIOS_IRQ_SCAN) + if (io_apic_assign_pci_irqs) { + int irq; + + if (pin) { + pin--; /* interrupt pins are numbered starting from 1 */ + irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); + /* + * Busses behind bridges are typically not listed in the MP-table. + * In this case we have to look up the IRQ based on the parent bus, + * parent slot, and pin number. The SMP code detects such bridged + * busses itself so we should get into this branch reliably. + */ + temp_dev = dev; + while (irq < 0 && dev->bus->parent) { /* go back to the bridge */ + struct pci_dev * bridge = dev->bus->self; + + pin = (pin + PCI_SLOT(dev->devfn)) % 4; + irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, + PCI_SLOT(bridge->devfn), pin); + if (irq >= 0) + printk(KERN_WARNING "PCI: using PPB(B%d,I%d,P%d) to get irq %d\n", + bridge->bus->number, PCI_SLOT(bridge->devfn), pin, irq); + dev = bridge; + } + dev = temp_dev; + if (irq >= 0) { +#ifdef CONFIG_PCI_USE_VECTOR + if (!platform_legacy_irq(irq)) + irq = IO_APIC_VECTOR(irq); +#endif + printk(KERN_INFO "PCI->APIC IRQ transform: (B%d,I%d,P%d) -> %d\n", + dev->bus->number, PCI_SLOT(dev->devfn), pin, irq); + dev->irq = irq; + return; + } else + msg = " Probably buggy MP table."; + } + } else if (pci_probe & PCI_BIOS_IRQ_SCAN) msg = ""; else msg = " Please try using pci=biosirq."; diff -urN linux-2.4.26/arch/i386/kernel/pci-pc.c linux-2.4.27/arch/i386/kernel/pci-pc.c --- linux-2.4.26/arch/i386/kernel/pci-pc.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/arch/i386/kernel/pci-pc.c 2004-08-07 16:26:04.555342710 -0700 @@ -1321,13 +1321,46 @@ * system to PCI bus no matter what are their window settings, so they are * "transparent" (or subtractive decoding) from programmers point of view. */ -static void __init pci_fixup_transparent_bridge(struct pci_dev *dev) +static void __devinit pci_fixup_transparent_bridge(struct pci_dev *dev) { if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI && (dev->device & 0xff00) == 0x2400) dev->transparent = 1; } +/* + * Fixup for C1 Halt Disconnect problem on nForce2 systems. + * + * From information provided by "Allen Martin" : + * + * A hang is caused when the CPU generates a very fast CONNECT/HALT cycle + * sequence. Workaround is to set the SYSTEM_IDLE_TIMEOUT to 80 ns. + * This allows the state-machine and timer to return to a proper state within + * 80 ns of the CONNECT and probe appearing together. Since the CPU will not + * issue another HALT within 80 ns of the initial HALT, the failure condition + * is avoided. + */ +static void __devinit pci_fixup_nforce2(struct pci_dev *dev) +{ + u32 val, fixed_val; + u8 rev; + + pci_read_config_byte(dev, PCI_REVISION_ID, &rev); + + /* + * Chip Old value New value + * C17 0x1F0FFF01 0x1F01FF01 + * C18D 0x9F0FFF01 0x9F01FF01 + */ + fixed_val = rev < 0xC1 ? 0x1F01FF01 : 0x9F01FF01; + + pci_read_config_dword(dev, 0x6c, &val); + if (val != fixed_val) { + printk(KERN_WARNING "PCI: nForce2 C1 Halt Disconnect fixup\n"); + pci_write_config_dword(dev, 0x6c, fixed_val); + } +} + struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, @@ -1343,6 +1376,7 @@ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8367_0, pci_fixup_via_northbridge_bug }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_NCR, PCI_DEVICE_ID_NCR_53C810, pci_fixup_ncr53c810 }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_fixup_transparent_bridge }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2, pci_fixup_nforce2}, { 0 } }; @@ -1433,7 +1467,6 @@ if (!acpi_noirq && !acpi_pci_irq_init()) { pci_using_acpi_prt = 1; printk(KERN_INFO "PCI: Using ACPI for IRQ routing\n"); - printk(KERN_INFO "PCI: if you experience problems, try using option 'pci=noacpi' or even 'acpi=off'\n"); } #endif if (!pci_using_acpi_prt) { diff -urN linux-2.4.26/arch/i386/kernel/setup.c linux-2.4.27/arch/i386/kernel/setup.c --- linux-2.4.26/arch/i386/kernel/setup.c 2004-04-14 06:05:25.000000000 -0700 +++ linux-2.4.27/arch/i386/kernel/setup.c 2004-08-07 16:26:04.557342792 -0700 @@ -2333,6 +2333,7 @@ { 0x43, LVL_2, 512 }, { 0x44, LVL_2, 1024 }, { 0x45, LVL_2, 2048 }, + { 0x60, LVL_1_DATA, 16 }, { 0x66, LVL_1_DATA, 8 }, { 0x67, LVL_1_DATA, 16 }, { 0x68, LVL_1_DATA, 32 }, @@ -2443,6 +2444,8 @@ printk (KERN_INFO "CPU: L1 I cache: %dK", l1i); if ( l1d ) printk(", L1 D cache: %dK\n", l1d); + else + printk("\n"); if ( l2 ) printk(KERN_INFO "CPU: L2 cache: %dK\n", l2); diff -urN linux-2.4.26/arch/i386/mm/fault.c linux-2.4.27/arch/i386/mm/fault.c --- linux-2.4.26/arch/i386/mm/fault.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/i386/mm/fault.c 2004-08-07 16:26:04.558342833 -0700 @@ -71,7 +71,7 @@ if (!vma || vma->vm_start != start) goto bad_area; if (!(vma->vm_flags & VM_WRITE)) - goto bad_area;; + goto bad_area; } return 1; diff -urN linux-2.4.26/arch/i386/mm/pageattr.c linux-2.4.27/arch/i386/mm/pageattr.c --- linux-2.4.26/arch/i386/mm/pageattr.c 2002-11-28 15:53:09.000000000 -0800 +++ linux-2.4.27/arch/i386/mm/pageattr.c 2004-08-07 16:26:04.558342833 -0700 @@ -52,11 +52,9 @@ static void flush_kernel_map(void * address) { - if (!test_bit(X86_FEATURE_SELFSNOOP, boot_cpu_data.x86_capability)) { - /* Could use CLFLUSH here if the CPU supports it (Hammer,P4) */ - if (boot_cpu_data.x86_model >= 4) - asm volatile("wbinvd":::"memory"); - } + /* Could use CLFLUSH here if the CPU supports it (Hammer,P4) */ + if (boot_cpu_data.x86_model >= 4) + asm volatile("wbinvd":::"memory"); /* Do global flush here to work around large page flushing errata in some early Athlons */ diff -urN linux-2.4.26/arch/ia64/configs/dig linux-2.4.27/arch/ia64/configs/dig --- linux-2.4.26/arch/ia64/configs/dig 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/configs/dig 2004-08-07 16:26:04.559342874 -0700 @@ -28,9 +28,9 @@ # CONFIG_MCKINLEY is not set # CONFIG_IA64_GENERIC is not set CONFIG_IA64_DIG=y -# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_HP_ZX1 is not set # CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y @@ -116,7 +116,6 @@ # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -265,6 +264,7 @@ # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set # CONFIG_BLK_DEV_CMD64X is not set # CONFIG_BLK_DEV_TRIFLEX is not set # CONFIG_BLK_DEV_CY82C693 is not set @@ -293,6 +293,7 @@ # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set # CONFIG_BLK_DEV_ATARAID_SII is not set # @@ -430,6 +431,7 @@ # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set # CONFIG_8139CP is not set @@ -511,6 +513,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_UINPUT is not set # # Character devices @@ -586,6 +589,7 @@ # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set # CONFIG_SCx200_GPIO is not set # CONFIG_INTEL_RNG is not set # CONFIG_HW_RANDOM is not set @@ -727,6 +731,11 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set # # Network File Systems @@ -940,7 +949,6 @@ # CONFIG_USB_RTL8150 is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set -# CONFIG_USB_AX8817X is not set # CONFIG_USB_CDCETHER is not set # CONFIG_USB_USBNET is not set diff -urN linux-2.4.26/arch/ia64/configs/generic linux-2.4.27/arch/ia64/configs/generic --- linux-2.4.26/arch/ia64/configs/generic 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/configs/generic 2004-08-07 16:26:04.560342915 -0700 @@ -28,9 +28,9 @@ # CONFIG_MCKINLEY is not set CONFIG_IA64_GENERIC=y # CONFIG_IA64_DIG is not set -# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_HP_ZX1 is not set # CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y @@ -116,7 +116,6 @@ # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -265,6 +264,7 @@ # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set CONFIG_BLK_DEV_CMD64X=y # CONFIG_BLK_DEV_TRIFLEX is not set # CONFIG_BLK_DEV_CY82C693 is not set @@ -294,6 +294,7 @@ # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set # CONFIG_BLK_DEV_ATARAID_SII is not set # @@ -436,6 +437,7 @@ # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set # CONFIG_8139CP is not set @@ -517,6 +519,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_UINPUT is not set # # Character devices @@ -592,6 +595,7 @@ # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set # CONFIG_SCx200_GPIO is not set # CONFIG_FETCHOP is not set # CONFIG_INTEL_RNG is not set @@ -734,6 +738,11 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set # # Network File Systems @@ -947,7 +956,6 @@ # CONFIG_USB_RTL8150 is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set -# CONFIG_USB_AX8817X is not set # CONFIG_USB_CDCETHER is not set # CONFIG_USB_USBNET is not set diff -urN linux-2.4.26/arch/ia64/configs/numa linux-2.4.27/arch/ia64/configs/numa --- linux-2.4.26/arch/ia64/configs/numa 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/configs/numa 2004-08-07 16:26:04.561342956 -0700 @@ -28,9 +28,9 @@ # CONFIG_MCKINLEY is not set CONFIG_IA64_GENERIC=y # CONFIG_IA64_DIG is not set -# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_HP_ZX1 is not set # CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y @@ -118,7 +118,6 @@ # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -267,6 +266,7 @@ # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set CONFIG_BLK_DEV_CMD64X=y # CONFIG_BLK_DEV_TRIFLEX is not set # CONFIG_BLK_DEV_CY82C693 is not set @@ -296,6 +296,7 @@ # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set # CONFIG_BLK_DEV_ATARAID_SII is not set # @@ -438,6 +439,7 @@ # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set # CONFIG_8139CP is not set @@ -519,6 +521,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_UINPUT is not set # # Character devices @@ -594,6 +597,7 @@ # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set # CONFIG_SCx200_GPIO is not set # CONFIG_FETCHOP is not set # CONFIG_INTEL_RNG is not set @@ -736,6 +740,11 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set # # Network File Systems @@ -949,7 +958,6 @@ # CONFIG_USB_RTL8150 is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set -# CONFIG_USB_AX8817X is not set # CONFIG_USB_CDCETHER is not set # CONFIG_USB_USBNET is not set diff -urN linux-2.4.26/arch/ia64/configs/ski linux-2.4.27/arch/ia64/configs/ski --- linux-2.4.26/arch/ia64/configs/ski 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/configs/ski 2004-08-07 16:26:04.562342997 -0700 @@ -28,9 +28,9 @@ # CONFIG_MCKINLEY is not set # CONFIG_IA64_GENERIC is not set # CONFIG_IA64_DIG is not set -CONFIG_IA64_HP_SIM=y # CONFIG_IA64_HP_ZX1 is not set # CONFIG_IA64_SGI_SN2 is not set +CONFIG_IA64_HP_SIM=y # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y @@ -83,7 +83,6 @@ # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -202,6 +201,7 @@ # CONFIG_INPUT_MOUSEDEV is not set # CONFIG_INPUT_JOYDEV is not set # CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_UINPUT is not set # # Character devices @@ -256,6 +256,7 @@ # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set # CONFIG_SCx200_GPIO is not set # CONFIG_INTEL_RNG is not set # CONFIG_HW_RANDOM is not set @@ -374,6 +375,11 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set # # Network File Systems diff -urN linux-2.4.26/arch/ia64/configs/zx1 linux-2.4.27/arch/ia64/configs/zx1 --- linux-2.4.26/arch/ia64/configs/zx1 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/configs/zx1 2004-08-07 16:26:04.563343038 -0700 @@ -28,9 +28,9 @@ CONFIG_MCKINLEY=y # CONFIG_IA64_GENERIC is not set # CONFIG_IA64_DIG is not set -# CONFIG_IA64_HP_SIM is not set CONFIG_IA64_HP_ZX1=y # CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y @@ -117,7 +117,6 @@ # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -266,6 +265,7 @@ # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set # CONFIG_BLK_DEV_CMD64X is not set # CONFIG_BLK_DEV_TRIFLEX is not set # CONFIG_BLK_DEV_CY82C693 is not set @@ -294,6 +294,7 @@ # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set # CONFIG_BLK_DEV_ATARAID_SII is not set # @@ -431,6 +432,7 @@ # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set # CONFIG_8139CP is not set @@ -512,6 +514,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_UINPUT is not set # # Character devices @@ -587,6 +590,7 @@ # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set # CONFIG_SCx200_GPIO is not set # CONFIG_INTEL_RNG is not set # CONFIG_HW_RANDOM is not set @@ -728,6 +732,11 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set # # Network File Systems @@ -941,7 +950,6 @@ # CONFIG_USB_RTL8150 is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set -# CONFIG_USB_AX8817X is not set # CONFIG_USB_CDCETHER is not set # CONFIG_USB_USBNET is not set diff -urN linux-2.4.26/arch/ia64/defconfig linux-2.4.27/arch/ia64/defconfig --- linux-2.4.26/arch/ia64/defconfig 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/defconfig 2004-08-07 16:26:04.564343079 -0700 @@ -28,9 +28,9 @@ # CONFIG_MCKINLEY is not set CONFIG_IA64_GENERIC=y # CONFIG_IA64_DIG is not set -# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_HP_ZX1 is not set # CONFIG_IA64_SGI_SN2 is not set +# CONFIG_IA64_HP_SIM is not set # CONFIG_IA64_PAGE_SIZE_4KB is not set # CONFIG_IA64_PAGE_SIZE_8KB is not set CONFIG_IA64_PAGE_SIZE_16KB=y @@ -116,7 +116,6 @@ # # SCTP Configuration (EXPERIMENTAL) # -CONFIG_IPV6_SCTP__=y # CONFIG_IP_SCTP is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -265,6 +264,7 @@ # CONFIG_WDC_ALI15X3 is not set # CONFIG_BLK_DEV_AMD74XX is not set # CONFIG_AMD74XX_OVERRIDE is not set +# CONFIG_BLK_DEV_ATIIXP is not set CONFIG_BLK_DEV_CMD64X=y # CONFIG_BLK_DEV_TRIFLEX is not set # CONFIG_BLK_DEV_CY82C693 is not set @@ -294,6 +294,7 @@ # CONFIG_BLK_DEV_ATARAID is not set # CONFIG_BLK_DEV_ATARAID_PDC is not set # CONFIG_BLK_DEV_ATARAID_HPT is not set +# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set # CONFIG_BLK_DEV_ATARAID_SII is not set # @@ -436,6 +437,7 @@ # CONFIG_FEALNX is not set # CONFIG_NATSEMI is not set # CONFIG_NE2K_PCI is not set +# CONFIG_FORCEDETH is not set # CONFIG_NE3210 is not set # CONFIG_ES3210 is not set # CONFIG_8139CP is not set @@ -517,6 +519,7 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 # CONFIG_INPUT_JOYDEV is not set CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_UINPUT is not set # # Character devices @@ -592,6 +595,7 @@ # Watchdog Cards # # CONFIG_WATCHDOG is not set +# CONFIG_SCx200 is not set # CONFIG_SCx200_GPIO is not set # CONFIG_FETCHOP is not set # CONFIG_INTEL_RNG is not set @@ -734,6 +738,11 @@ # CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set # CONFIG_UFS_FS_WRITE is not set +# CONFIG_XFS_FS is not set +# CONFIG_XFS_QUOTA is not set +# CONFIG_XFS_RT is not set +# CONFIG_XFS_TRACE is not set +# CONFIG_XFS_DEBUG is not set # # Network File Systems @@ -947,7 +956,6 @@ # CONFIG_USB_RTL8150 is not set # CONFIG_USB_KAWETH is not set # CONFIG_USB_CATC is not set -# CONFIG_USB_AX8817X is not set # CONFIG_USB_CDCETHER is not set # CONFIG_USB_USBNET is not set diff -urN linux-2.4.26/arch/ia64/ia32/binfmt_elf32.c linux-2.4.27/arch/ia64/ia32/binfmt_elf32.c --- linux-2.4.26/arch/ia64/ia32/binfmt_elf32.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/arch/ia64/ia32/binfmt_elf32.c 2004-08-07 16:26:04.564343079 -0700 @@ -17,6 +17,8 @@ #include #include +#include "elfcore32.h" + #define CONFIG_BINFMT_ELF32 /* Override some function names */ diff -urN linux-2.4.26/arch/ia64/ia32/elfcore32.h linux-2.4.27/arch/ia64/ia32/elfcore32.h --- linux-2.4.26/arch/ia64/ia32/elfcore32.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/arch/ia64/ia32/elfcore32.h 2004-08-07 16:26:04.565343120 -0700 @@ -0,0 +1,133 @@ +/* + * IA-32 ELF core dump support. + * + * Copyright (C) 2003 Arun Sharma + * + * Derived from the x86_64 version + */ +#ifndef _ELFCORE32_H_ +#define _ELFCORE32_H_ + +#define USE_ELF_CORE_DUMP 1 + +/* Override elfcore.h */ +#define _LINUX_ELFCORE_H 1 +typedef unsigned int elf_greg_t; + +#define ELF_NGREG (sizeof (struct user_regs_struct32) / sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef struct ia32_user_i387_struct elf_fpregset_t; +typedef struct ia32_user_fxsr_struct elf_fpxregset_t; + +struct elf_siginfo +{ + int si_signo; /* signal number */ + int si_code; /* extra code */ + int si_errno; /* errno */ +}; + +#define jiffies_to_timeval(a,b) do { (b)->tv_usec = 0; (b)->tv_sec = (a)/HZ; }while(0) + +struct compat_timeval +{ + int tv_sec, tv_usec; +}; + +struct elf_prstatus +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct compat_timeval pr_utime; /* User time */ + struct compat_timeval pr_stime; /* System time */ + struct compat_timeval pr_cutime; /* Cumulative user time */ + struct compat_timeval pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define ELF_PRARGSZ (80) /* Number of chars for args */ + +struct elf_prpsinfo +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned int pr_flag; /* flags */ + __u16 pr_uid; + __u16 pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + pr_reg[0] = regs->r11; \ + pr_reg[1] = regs->r9; \ + pr_reg[2] = regs->r10; \ + pr_reg[3] = regs->r14; \ + pr_reg[4] = regs->r15; \ + pr_reg[5] = regs->r13; \ + pr_reg[6] = regs->r8; \ + pr_reg[7] = regs->r16 & 0xffff; \ + pr_reg[8] = (regs->r16 >> 16) & 0xffff; \ + pr_reg[9] = (regs->r16 >> 32) & 0xffff; \ + pr_reg[10] = (regs->r16 >> 48) & 0xffff; \ + pr_reg[11] = regs->r1; \ + pr_reg[12] = regs->cr_iip; \ + pr_reg[13] = regs->r17 & 0xffff; \ + asm volatile ("mov %0=ar.eflag ;;" \ + : "=r"(pr_reg[14])); \ + pr_reg[15] = regs->r12; \ + pr_reg[16] = (regs->r17 >> 16) & 0xffff; + +static inline void elf_core_copy_regs(elf_gregset_t *elfregs, + struct pt_regs *regs) +{ + ELF_CORE_COPY_REGS((*elfregs), regs) +} + +static inline int elf_core_copy_task_regs(struct task_struct *t, + elf_gregset_t* elfregs) +{ + struct pt_regs *pp = ia64_task_regs(t); + ELF_CORE_COPY_REGS((*elfregs), pp); + return 1; +} + +static inline int +elf_core_copy_task_fpregs(struct task_struct *tsk, elf_fpregset_t *fpu) +{ + struct ia32_user_i387_struct *fpstate = (void*)fpu; + + if (!tsk->used_math) + return 0; + + save_ia32_fpstate(tsk, fpstate); + + return 1; +} + +#define ELF_CORE_COPY_XFPREGS 1 +static inline int +elf_core_copy_task_xfpregs(struct task_struct *tsk, elf_fpxregset_t *xfpu) +{ + struct ia32_user_fxsr_struct *fpxstate = (void*) xfpu; + + if (!tsk->used_math) + return 0; + + save_ia32_fpxstate(tsk, fpxstate); + + return 1; +} + +#endif /* _ELFCORE32_H_ */ diff -urN linux-2.4.26/arch/ia64/ia32/sys_ia32.c linux-2.4.27/arch/ia64/ia32/sys_ia32.c --- linux-2.4.26/arch/ia64/ia32/sys_ia32.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/ia32/sys_ia32.c 2004-08-07 16:26:04.568343244 -0700 @@ -2949,7 +2949,7 @@ return; } -static int +int save_ia32_fpstate (struct task_struct *tsk, struct ia32_user_i387_struct *save) { struct switch_stack *swp; @@ -3011,7 +3011,7 @@ return 0; } -static int +int save_ia32_fpxstate (struct task_struct *tsk, struct ia32_user_fxsr_struct *save) { struct switch_stack *swp; diff -urN linux-2.4.26/arch/ia64/kernel/efi.c linux-2.4.27/arch/ia64/kernel/efi.c --- linux-2.4.26/arch/ia64/kernel/efi.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/kernel/efi.c 2004-08-07 16:26:04.569343285 -0700 @@ -673,8 +673,7 @@ for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { md = p; if (md->type == EFI_MEMORY_MAPPED_IO_PORT_SPACE) { - /* paranoia attribute checking */ - if (md->attribute == (EFI_MEMORY_UC | EFI_MEMORY_RUNTIME)) + if (md->attribute & EFI_MEMORY_UC) return md->phys_addr; } } diff -urN linux-2.4.26/arch/ia64/kernel/efivars.c linux-2.4.27/arch/ia64/kernel/efivars.c --- linux-2.4.26/arch/ia64/kernel/efivars.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/arch/ia64/kernel/efivars.c 2004-08-07 16:26:04.570343326 -0700 @@ -364,6 +364,7 @@ int ret; const int max_nr_entries = 7; /* num ptrs to tables we could expose */ const int max_line_len = 80; + loff_t pos = *ppos; if (!efi.systab) return 0; @@ -388,13 +389,13 @@ if (efi.boot_info) length += sprintf(proc_buffer + length, "BOOTINFO=0x%lx\n", __pa(efi.boot_info)); - if (*ppos >= length) { + if (pos != (unsigned) pos || pos >= length) { ret = 0; goto out; } - data = proc_buffer + file->f_pos; - size = length - file->f_pos; + data = proc_buffer + pos; + size = length - pos; if (size > count) size = count; if (copy_to_user(buffer, data, size)) { @@ -402,7 +403,7 @@ goto out; } - *ppos += size; + *ppos = pos + size; ret = size; out: diff -urN linux-2.4.26/arch/ia64/kernel/palinfo.c linux-2.4.27/arch/ia64/kernel/palinfo.c --- linux-2.4.26/arch/ia64/kernel/palinfo.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/arch/ia64/kernel/palinfo.c 2004-08-07 16:26:04.570343326 -0700 @@ -509,10 +509,10 @@ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - "Enable Cache Line Repl. Exclusive", "Enable Cache Line Repl. Shared", + "Enable Cache Line Repl. Exclusive", "Disable Transaction Queuing", - "Disable Reponse Error Checking", + "Disable Response Error Checking", "Disable Bus Error Checking", "Disable Bus Requester Internal Error Signalling", "Disable Bus Requester Error Signalling", diff -urN linux-2.4.26/arch/ia64/kernel/perfmon.c linux-2.4.27/arch/ia64/kernel/perfmon.c --- linux-2.4.26/arch/ia64/kernel/perfmon.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/kernel/perfmon.c 2004-08-07 16:26:04.574343490 -0700 @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -887,6 +888,8 @@ DBprintk(("Cannot allocate vma\n")); goto error_kmem; } + memset(vma, 0, sizeof(*vma)); + /* * partially initialize the vma for the sampling buffer * @@ -3166,62 +3169,111 @@ } } -/* for debug only */ -static int -pfm_proc_info(char *page) +#define PFM_PROC_SHOW_HEADER ((void *)NR_CPUS+1) + +static void * +pfm_proc_start(struct seq_file *m, loff_t *pos) { - char *p = page; - int i; - - p += sprintf(p, "fastctxsw : %s\n", pfm_sysctl.fastctxsw > 0 ? "Yes": "No"); - p += sprintf(p, "ovfl_mask : 0x%lx\n", pmu_conf.ovfl_val); - - for(i=0; i < NR_CPUS; i++) { - if (cpu_online(i) == 0) continue; - p += sprintf(p, "CPU%-2d overflow intrs : %lu\n", i, pfm_stats[i].pfm_ovfl_intr_count); - p += sprintf(p, "CPU%-2d spurious intrs : %lu\n", i, pfm_stats[i].pfm_spurious_ovfl_intr_count); - p += sprintf(p, "CPU%-2d recorded samples : %lu\n", i, pfm_stats[i].pfm_recorded_samples_count); - p += sprintf(p, "CPU%-2d smpl buffer full : %lu\n", i, pfm_stats[i].pfm_full_smpl_buffer_count); - p += sprintf(p, "CPU%-2d syst_wide : %d\n", i, cpu_data(i)->pfm_syst_info & PFM_CPUINFO_SYST_WIDE ? 1 : 0); - p += sprintf(p, "CPU%-2d dcr_pp : %d\n", i, cpu_data(i)->pfm_syst_info & PFM_CPUINFO_DCR_PP ? 1 : 0); - p += sprintf(p, "CPU%-2d exclude idle : %d\n", i, cpu_data(i)->pfm_syst_info & PFM_CPUINFO_EXCL_IDLE ? 1 : 0); - p += sprintf(p, "CPU%-2d owner : %d\n", i, pmu_owners[i].owner ? pmu_owners[i].owner->pid: -1); - p += sprintf(p, "CPU%-2d activations : %lu\n", i, pmu_owners[i].activation_number); + if (*pos == 0) { + return PFM_PROC_SHOW_HEADER; } - LOCK_PFS(); - - p += sprintf(p, "proc_sessions : %u\n" - "sys_sessions : %u\n" - "sys_use_dbregs : %u\n" - "ptrace_use_dbregs : %u\n", - pfm_sessions.pfs_task_sessions, - pfm_sessions.pfs_sys_sessions, - pfm_sessions.pfs_sys_use_dbregs, - pfm_sessions.pfs_ptrace_use_dbregs); - - UNLOCK_PFS(); - - return p - page; + while (*pos <= NR_CPUS) { + if (cpu_online(*pos - 1)) { + return (void *)*pos; + } + ++*pos; + } + return NULL; +} + +static void * +pfm_proc_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return pfm_proc_start(m, pos); +} + +static void +pfm_proc_stop(struct seq_file *m, void *v) +{ } -/* /proc interface, for debug only */ +static void +pfm_proc_show_header(struct seq_file *m) +{ + seq_printf(m, + "perfmon version : %u.%u\n" + "fastctxsw : %s\n" + "ovfl_mask : 0x%lx\n", + PFM_VERSION_MAJ, PFM_VERSION_MIN, + pfm_sysctl.fastctxsw > 0 ? "Yes": "No", + pmu_conf.ovfl_val); + + LOCK_PFS(); + + seq_printf(m, + "proc_sessions : %u\n" + "sys_sessions : %u\n" + "sys_use_dbregs : %u\n" + "ptrace_use_dbregs : %u\n", + pfm_sessions.pfs_task_sessions, + pfm_sessions.pfs_sys_sessions, + pfm_sessions.pfs_sys_use_dbregs, + pfm_sessions.pfs_ptrace_use_dbregs); + + UNLOCK_PFS(); +} + static int -perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data) +pfm_proc_show(struct seq_file *m, void *v) { - int len = pfm_proc_info(page); + int cpu; - if (len <= off+count) *eof = 1; + if (v == PFM_PROC_SHOW_HEADER) { + pfm_proc_show_header(m); + return 0; + } - *start = page + off; - len -= off; + /* show info for CPU (v - 1) */ - if (len>count) len = count; - if (len<0) len = 0; + cpu = (long)v - 1; + seq_printf(m, + "CPU%-2d overflow intrs : %lu\n" + "CPU%-2d spurious intrs : %lu\n" + "CPU%-2d recorded samples : %lu\n" + "CPU%-2d smpl buffer full : %lu\n" + "CPU%-2d syst_wide : %d\n" + "CPU%-2d dcr_pp : %d\n" + "CPU%-2d exclude idle : %d\n" + "CPU%-2d owner : %d\n" + "CPU%-2d activations : %lu\n", + cpu, pfm_stats[cpu].pfm_ovfl_intr_count, + cpu, pfm_stats[cpu].pfm_spurious_ovfl_intr_count, + cpu, pfm_stats[cpu].pfm_recorded_samples_count, + cpu, pfm_stats[cpu].pfm_full_smpl_buffer_count, + cpu, cpu_data(cpu)->pfm_syst_info & PFM_CPUINFO_SYST_WIDE ? 1 : 0, + cpu, cpu_data(cpu)->pfm_syst_info & PFM_CPUINFO_DCR_PP ? 1 : 0, + cpu, cpu_data(cpu)->pfm_syst_info & PFM_CPUINFO_EXCL_IDLE ? 1 : 0, + cpu, pmu_owners[cpu].owner ? pmu_owners[cpu].owner->pid: -1, + cpu, pmu_owners[cpu].activation_number); - return len; + return 0; } - + +struct seq_operations pfm_seq_ops = { + .start = pfm_proc_start, + .next = pfm_proc_next, + .stop = pfm_proc_stop, + .show = pfm_proc_show +}; + +static int +pfm_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &pfm_seq_ops); +} + /* * we come here as soon as local_cpu_data->pfm_syst_wide is set. this happens * during pfm_enable() hence before pfm_start(). We cannot assume monitoring @@ -4448,6 +4500,13 @@ return 0; } +static struct file_operations pfm_proc_fops = { + .open = pfm_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + /* * perfmon initialization routine, called from the initcall() table */ @@ -4498,11 +4557,15 @@ /* * for now here for debug purposes */ - perfmon_dir = create_proc_read_entry ("perfmon", 0, 0, perfmon_read_entry, NULL); + perfmon_dir = create_proc_entry("perfmon", S_IRUGO, NULL); if (perfmon_dir == NULL) { printk(KERN_ERR "perfmon: cannot create /proc entry, perfmon disabled\n"); return -1; } + /* + * install customized file operations for /proc/perfmon entry + */ + perfmon_dir->proc_fops = &pfm_proc_fops; /* * create /proc/sys/kernel/perfmon diff -urN linux-2.4.26/arch/ia64/kernel/salinfo.c linux-2.4.27/arch/ia64/kernel/salinfo.c --- linux-2.4.26/arch/ia64/kernel/salinfo.c 2004-04-14 06:05:26.000000000 -0700 +++ linux-2.4.27/arch/ia64/kernel/salinfo.c 2004-08-07 16:26:04.574343490 -0700 @@ -451,6 +451,7 @@ size_t size; u8 *buf; u64 bufsize; + loff_t pos = *ppos; if (data->state == STATE_LOG_RECORD) { buf = data->log_buffer; @@ -462,17 +463,17 @@ buf = NULL; bufsize = 0; } - if (*ppos >= bufsize) + if (pos != (unsigned)pos || pos >= bufsize) return 0; - saldata = buf + file->f_pos; - size = bufsize - file->f_pos; + saldata = buf + pos; + size = bufsize - pos; if (size > count) size = count; if (copy_to_user(buffer, saldata, size)) return -EFAULT; - *ppos += size; + *ppos = pos + size; return size; } diff -urN linux-2.4.26/arch/ia64/kernel/unwind.c linux-2.4.27/arch/ia64/kernel/unwind.c --- linux-2.4.26/arch/ia64/kernel/unwind.c 2004-04-14 06:05:26.000000000 -0700 +++ linux-2.4.27/arch/ia64/kernel/unwind.c 2004-08-07 16:26:04.576343572 -0700 @@ -1750,7 +1750,7 @@ if (!state->pri_unat_loc) state->pri_unat_loc = &state->sw->ar_unat; /* register off. is a multiple of 8, so the least 3 bits (type) are 0 */ - s[dst+1] = (*state->pri_unat_loc - s[dst]) | UNW_NAT_MEMSTK; + s[dst+1] = ((unsigned long) state->pri_unat_loc - s[dst]) | UNW_NAT_MEMSTK; break; case UNW_INSN_SETNAT_TYPE: diff -urN linux-2.4.26/arch/ia64/mm/hugetlbpage.c linux-2.4.27/arch/ia64/mm/hugetlbpage.c --- linux-2.4.26/arch/ia64/mm/hugetlbpage.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ia64/mm/hugetlbpage.c 2004-08-07 16:26:04.577343613 -0700 @@ -73,8 +73,12 @@ pte_t *pte = NULL; pgd = pgd_offset(mm, taddr); - pmd = pmd_offset(pgd, taddr); - pte = pte_offset(pmd, taddr); + if (pgd_present(*pgd)) { + pmd = pmd_offset(pgd, taddr); + if (pmd_present(*pmd)) + pte = pte_offset(pmd, taddr); + } + return pte; } @@ -269,7 +273,7 @@ for (address = start; address < end; address += HPAGE_SIZE) { pte = huge_pte_offset(mm, address); - if (pte_none(*pte)) + if (!pte || pte_none(*pte)) continue; page = pte_page(*pte); huge_page_release(page); diff -urN linux-2.4.26/arch/m68k/ifpsp060/iskeleton.S linux-2.4.27/arch/m68k/ifpsp060/iskeleton.S --- linux-2.4.26/arch/m68k/ifpsp060/iskeleton.S 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.27/arch/m68k/ifpsp060/iskeleton.S 2004-08-07 16:26:04.577343613 -0700 @@ -196,14 +196,58 @@ | Expected outputs: | d0 = 0 -> success; non-zero -> failure | -| Linux/68k: As long as ints are disabled, no swapping out should -| occur (hopefully...) +| Linux/m68k: Make sure the page is properly paged in, so we use +| plpaw and handle any exception here. The kernel must not be +| preempted until _060_unlock_page(), so that the page stays mapped. | .global _060_real_lock_page _060_real_lock_page: - clr.l %d0 + move.l %d2,-(%sp) + | load sfc/dfc + tst.b %d0 + jne 1f + moveq #1,%d0 + jra 2f +1: moveq #5,%d0 +2: movec.l %dfc,%d2 + movec.l %d0,%dfc + movec.l %d0,%sfc + + clr.l %d0 + | prefetch address + .chip 68060 + move.l %a0,%a1 +1: plpaw (%a1) + addq.w #1,%a0 + tst.b %d1 + jeq 2f + addq.w #2,%a0 +2: plpaw (%a0) +3: .chip 68k + + | restore sfc/dfc + movec.l %d2,%dfc + movec.l %d2,%sfc + move.l (%sp)+,%d2 rts +.section __ex_table,"a" + .align 4 + .long 1b,11f + .long 2b,21f +.previous +.section .fixup,"ax" + .even +11: move.l #0x020003c0,%d0 + or.l %d2,%d0 + swap %d0 + jra 3b +21: move.l #0x02000bc0,%d0 + or.l %d2,%d0 + swap %d0 + jra 3b +.previous + | | _060_unlock_page(): | @@ -216,8 +260,7 @@ | d0 = `xxxxxxff -> supervisor; `xxxxxx00 -> user | d1 = `xxxxxxff -> longword; `xxxxxx00 -> word | -| Linux/68k: As we do no special locking operation, also no unlocking -| is needed... +| Linux/m68k: perhaps reenable preemption here... .global _060_real_unlock_page _060_real_unlock_page: diff -urN linux-2.4.26/arch/m68k/kernel/setup.c linux-2.4.27/arch/m68k/kernel/setup.c --- linux-2.4.26/arch/m68k/kernel/setup.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.27/arch/m68k/kernel/setup.c 2004-08-07 16:26:04.578343655 -0700 @@ -244,6 +244,18 @@ else if (CPU_IS_060) m68k_is040or060 = 6; + if (CPU_IS_060) { + u32 pcr; + + asm (".chip 68060; movec %%pcr,%0; .chip 68k" + : "=d" (pcr)); + if (((pcr >> 8) & 0xff) <= 5) { + printk("Enabling workaround for errata I14\n"); + asm (".chip 68060; movec %0,%%pcr; .chip 68k" + : : "d" (pcr | 0x20)); + } + } + /* FIXME: m68k_fputype is passed in by Penguin booter, which can * be confused by software FPU emulation. BEWARE. * We should really do our own FPU check at startup. diff -urN linux-2.4.26/arch/m68k/mac/iop.c linux-2.4.27/arch/m68k/mac/iop.c --- linux-2.4.26/arch/m68k/mac/iop.c 2003-06-13 07:51:31.000000000 -0700 +++ linux-2.4.27/arch/m68k/mac/iop.c 2004-08-07 16:26:04.579343696 -0700 @@ -261,7 +261,7 @@ } else { iop_base[IOP_NUM_ISM] = (struct mac_iop *) ISM_IOP_BASE_QUADRA; } - iop_base[IOP_NUM_SCC]->status_ctrl = 0; + iop_base[IOP_NUM_ISM]->status_ctrl = 0; iop_ism_present = 1; } else { iop_base[IOP_NUM_ISM] = NULL; diff -urN linux-2.4.26/arch/mips/sibyte/sb1250/bcm1250_tbprof.c linux-2.4.27/arch/mips/sibyte/sb1250/bcm1250_tbprof.c --- linux-2.4.26/arch/mips/sibyte/sb1250/bcm1250_tbprof.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/mips/sibyte/sb1250/bcm1250_tbprof.c 2004-08-07 16:26:04.579343696 -0700 @@ -300,6 +300,9 @@ char *dest = buf; long cur_off = *offp; + if (cur_off < 0) + return -EINVAL; + count = 0; cur_sample = cur_off / TB_SAMPLE_SIZE; sample_off = cur_off % TB_SAMPLE_SIZE; diff -urN linux-2.4.26/arch/ppc/boot/simple/m8260_tty.c linux-2.4.27/arch/ppc/boot/simple/m8260_tty.c --- linux-2.4.26/arch/ppc/boot/simple/m8260_tty.c 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc/boot/simple/m8260_tty.c 2004-08-07 16:26:04.580343737 -0700 @@ -156,7 +156,7 @@ sccp->scc_sccm = 0; sccp->scc_scce = 0xffff; sccp->scc_dsr = 0x7e7e; - sccp->scc_pmsr = 0x3000; + sccp->scc_psmr = 0x3000; /* Wire BRG1 to SCC1. The console driver will take care of * others. diff -urN linux-2.4.26/arch/ppc/config.in linux-2.4.27/arch/ppc/config.in --- linux-2.4.26/arch/ppc/config.in 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc/config.in 2004-08-07 16:26:04.580343737 -0700 @@ -410,7 +410,6 @@ if [ "$CONFIG_ALL_PPC" = "y" ]; then bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE - bool 'Support for RTAS (RunTime Abstraction Services) in /proc' CONFIG_PPC_RTAS bool 'Support for PReP Residual Data' CONFIG_PREP_RESIDUAL dep_bool ' Support for reading of PReP Residual Data in /proc' CONFIG_PROC_PREPRESIDUAL $CONFIG_PREP_RESIDUAL define_bool CONFIG_PPCBUG_NVRAM y diff -urN linux-2.4.26/arch/ppc/cpm2_io/fcc_enet.c linux-2.4.27/arch/ppc/cpm2_io/fcc_enet.c --- linux-2.4.26/arch/ppc/cpm2_io/fcc_enet.c 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc/cpm2_io/fcc_enet.c 2004-08-07 16:26:04.582343819 -0700 @@ -305,6 +305,8 @@ ushort skb_cur; ushort skb_dirty; + atomic_t n_pkts; /* Number of packets in tx ring */ + /* CPM dual port RAM relative addresses. */ cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ @@ -396,13 +398,15 @@ bdp->cbd_datlen = skb->len; bdp->cbd_bufaddr = __pa(skb->data); + spin_lock_irq(&cep->lock); + /* Save skb pointer. */ cep->tx_skbuff[cep->skb_cur] = skb; cep->stats.tx_bytes += skb->len; cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; - spin_lock_irq(&cep->lock); + atomic_inc(&cep->n_pkts); /* Send it on its way. Tell CPM its ready, interrupt when done, * its the last BD of the frame, and to put the CRC on the end. @@ -421,9 +425,12 @@ else bdp++; - if (bdp->cbd_sc & BD_ENET_TX_READY) { - netif_stop_queue(dev); + /* If the tx_ring is full, stop the queue */ + if (atomic_read(&cep->n_pkts) >= (TX_RING_SIZE-1)) { + if (!netif_queue_stopped(dev)) { + netif_stop_queue(dev); cep->tx_full = 1; + } } cep->cur_tx = (cbd_t *)bdp; @@ -542,6 +549,8 @@ dev_kfree_skb_irq(cep->tx_skbuff[cep->skb_dirty]); cep->skb_dirty = (cep->skb_dirty + 1) & TX_RING_MOD_MASK; + atomic_dec(&cep->n_pkts); + /* Update pointer to next buffer descriptor to be transmitted. */ if (bdp->cbd_sc & BD_ENET_TX_WRAP) bdp = cep->tx_bd_base; @@ -1782,6 +1791,7 @@ while (cp->cp_cpcr & CPM_CR_FLG); cep->skb_cur = cep->skb_dirty = 0; + atomic_set(&cep->n_pkts, 0); } /* Let 'er rip. diff -urN linux-2.4.26/arch/ppc/kernel/head_44x.S linux-2.4.27/arch/ppc/kernel/head_44x.S --- linux-2.4.26/arch/ppc/kernel/head_44x.S 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc/kernel/head_44x.S 2004-08-07 16:26:04.583343860 -0700 @@ -3,9 +3,26 @@ * * Kernel execution entry point code. * - * Matt Porter - * - * Copyright 2002-2003 MontaVista Software, Inc. + * Copyright (c) 1995-1996 Gary Thomas + * Initial PowerPC version. + * Copyright (c) 1996 Cort Dougan + * Rewritten for PReP + * Copyright (c) 1996 Paul Mackerras + * Low-level exception handers, MMU support, and rewrite. + * Copyright (c) 1997 Dan Malek + * PowerPC 8xx modifications. + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright 2000 MontaVista Software Inc. + * PPC405 modifications + * PowerPC 403GCX/405GP modifications. + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * debbie_chu@mvista.com + * Copyright 2002-2004 MontaVista Software, Inc. + * PowerPC 44x support, Matt Porter * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the diff -urN linux-2.4.26/arch/ppc/kernel/m8260_setup.c linux-2.4.27/arch/ppc/kernel/m8260_setup.c --- linux-2.4.26/arch/ppc/kernel/m8260_setup.c 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc/kernel/m8260_setup.c 2004-08-07 16:26:04.584343901 -0700 @@ -93,7 +93,7 @@ */ static uint rtc_time; -static static int +static int m8260_set_rtc_time(unsigned long time) { rtc_time = time; diff -urN linux-2.4.26/arch/ppc/kernel/ppc_htab.c linux-2.4.27/arch/ppc/kernel/ppc_htab.c --- linux-2.4.26/arch/ppc/kernel/ppc_htab.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ppc/kernel/ppc_htab.c 2004-08-07 16:26:04.584343901 -0700 @@ -112,6 +112,7 @@ size_t count, loff_t *ppos) { unsigned long mmcr0 = 0, pmc1 = 0, pmc2 = 0; + loff_t pos = *ppos; int n = 0; #if defined(CONFIG_PPC_STD_MMU) && !defined(CONFIG_PPC64BRIDGE) int valid; @@ -219,14 +220,14 @@ "Non-error misses: %lu\n" "Error misses\t: %lu\n", pte_misses, pte_errors); - if (*ppos >= strlen(buffer)) + if (pos != (unsigned)pos || pos >= strlen(buffer)) return 0; - if (n > strlen(buffer) - *ppos) - n = strlen(buffer) - *ppos; + if (n > strlen(buffer) - pos) + n = strlen(buffer) - pos; if (n > count) n = count; - copy_to_user(buf, buffer + *ppos, n); - *ppos += n; + copy_to_user(buf, buffer + pos, n); + *ppos = pos + n; return n; } diff -urN linux-2.4.26/arch/ppc/platforms/Makefile linux-2.4.27/arch/ppc/platforms/Makefile --- linux-2.4.26/arch/ppc/platforms/Makefile 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc/platforms/Makefile 2004-08-07 16:26:04.585343942 -0700 @@ -51,7 +51,6 @@ prep_time.o prep_setup.o pmac_sleep.o \ pmac_nvram.o obj-$(CONFIG_PMAC_BACKLIGHT) += pmac_backlight.o -obj-$(CONFIG_PPC_RTAS) += error_log.o proc_rtas.o obj-$(CONFIG_PREP_RESIDUAL) += residual.o obj-$(CONFIG_GEMINI) += gemini_pci.o gemini_setup.o gemini_prom.o obj-$(CONFIG_LOPEC) += lopec_setup.o lopec_pci.o diff -urN linux-2.4.26/arch/ppc/platforms/error_log.c linux-2.4.27/arch/ppc/platforms/error_log.c --- linux-2.4.26/arch/ppc/platforms/error_log.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/ppc/platforms/error_log.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,183 +0,0 @@ -/* - * arch/ppc/kernel/error_log.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 "error_log.h" - -/* ****************************************************************** */ -/* - * 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 - */ -int ppc_rtas_errorlog_scan(void) -{ -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 */ -}; - -#if 0 /* unused?? */ -const char *_errlog_disposition[] = { -#ifdef VERBOSE_ERRORS - "Fully recovered\n\t\ -There was an error, but it is fully recovered by RTAS.", - "Limited recovery\n\t\ -RTAS was able to recover the state of the machine, but some\n\t\ -feature of the machine has been disabled or lost (for example\n\t\ -error checking) or performance may suffer.", - "Not recovered\n\t\ -Whether RTAS did not try to recover anything or recovery failed:\n\t\ -HOUSTON, WE HAVE A PROBLEM!" -#else - "Fully recovered", - "Limited recovery", - "Not recovered" -#endif /* VERBOSE_ERRORS */ -}; -#endif - -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" -}; - rtas_error_log error_log; - char logdata[1024]; - int error; -#if 0 /* unused?? */ - int retries = 0; /* if HW error, try 10 times */ -#endif - - error = call_rtas ("event-scan", 4, 1, (unsigned long *)&error_log, - INTERNAL_ERROR | EPOW_WARNING, - 0, __pa(logdata), 1024); - - if (error == 1) /* no errors found */ - return 0; - - if (error == -1) { - printk(KERN_ERR "Unable to get errors. Do you a favor and throw this box away\n"); - return 0; - } - if (error_log.version != 1) - printk(KERN_WARNING "Unknown version (%d), please implement me\n", - error_log.version); - - switch (error_log.disposition) { - case DISP_FULLY_RECOVERED: - /* there was an error, but everything is fine now */ - return 0; - case DISP_NOT_RECOVERED: - printk("We have a really serious Problem!\n"); - case DISP_LIMITED_RECOVERY: - printk("Error classification\n"); - printk("Severity : %s\n", - ppc_rtas_errorlog_check_severity (error_log)); - printk("Initiator : %s\n", - ppc_rtas_errorlog_check_initiator (error_log)); - printk("Target : %s\n", - ppc_rtas_errorlog_check_target (error_log)); - printk("Type : %s\n", - ppc_rtas_errorlog_check_type (error_log)); - printk("Ext. log : %s\n", - ppc_rtas_errorlog_check_extended (error_log)); - if (error_log.extended) - ppc_rtas_errorlog_disect_extended (logdata); - return 1; - default: - /* nothing */ - break; - } - return 0; -} -/* ****************************************************************** */ -const char * ppc_rtas_errorlog_check_type (rtas_error_log error_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 (error_log.type == TYPE_EPOW) - return "EPOW"; - if (error_log.type >= TYPE_PMGM_POWER_SW_ON) - return "PowerMGM Event (not handled right now)"; - return _errlog_type[error_log.type]; -} - diff -urN linux-2.4.26/arch/ppc/platforms/error_log.h linux-2.4.27/arch/ppc/platforms/error_log.h --- linux-2.4.26/arch/ppc/platforms/error_log.h 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/ppc/platforms/error_log.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,95 +0,0 @@ -#ifndef __ERROR_LOG_H__ -#define __ERROR_LOG_H__ - -#define VERBOSE_ERRORS 1 /* Maybe I enlarge the kernel too much */ -#undef VERBOSE_ERRORS - -/* Event classes */ -/* XXX: Endianess correct? NOW*/ -#define INTERNAL_ERROR 0x80000000 /* set bit 0 */ -#define EPOW_WARNING 0x40000000 /* set bit 1 */ -#define POWERMGM_EVENTS 0x20000000 /* set bit 2 */ - -/* event-scan returns */ -#define SEVERITY_FATAL 0x5 -#define SEVERITY_ERROR 0x4 -#define SEVERITY_ERROR_SYNC 0x3 -#define SEVERITY_WARNING 0x2 -#define SEVERITY_EVENT 0x1 -#define SEVERITY_NO_ERROR 0x0 -#define DISP_FULLY_RECOVERED 0x0 -#define DISP_LIMITED_RECOVERY 0x1 -#define DISP_NOT_RECOVERED 0x2 -#define PART_PRESENT 0x0 -#define PART_NOT_PRESENT 0x1 -#define INITIATOR_UNKNOWN 0x0 -#define INITIATOR_CPU 0x1 -#define INITIATOR_PCI 0x2 -#define INITIATOR_ISA 0x3 -#define INITIATOR_MEMORY 0x4 -#define INITIATOR_POWERMGM 0x5 -#define TARGET_UNKNOWN 0x0 -#define TARGET_CPU 0x1 -#define TARGET_PCI 0x2 -#define TARGET_ISA 0x3 -#define TARGET_MEMORY 0x4 -#define TARGET_POWERMGM 0x5 -#define TYPE_RETRY 0x01 -#define TYPE_TCE_ERR 0x02 -#define TYPE_INTERN_DEV_FAIL 0x03 -#define TYPE_TIMEOUT 0x04 -#define TYPE_DATA_PARITY 0x05 -#define TYPE_ADDR_PARITY 0x06 -#define TYPE_CACHE_PARITY 0x07 -#define TYPE_ADDR_INVALID 0x08 -#define TYPE_ECC_UNCORR 0x09 -#define TYPE_ECC_CORR 0x0a -#define TYPE_EPOW 0x40 -/* I don't add PowerMGM events right now, this is a different topic */ -#define TYPE_PMGM_POWER_SW_ON 0x60 -#define TYPE_PMGM_POWER_SW_OFF 0x61 -#define TYPE_PMGM_LID_OPEN 0x62 -#define TYPE_PMGM_LID_CLOSE 0x63 -#define TYPE_PMGM_SLEEP_BTN 0x64 -#define TYPE_PMGM_WAKE_BTN 0x65 -#define TYPE_PMGM_BATTERY_WARN 0x66 -#define TYPE_PMGM_BATTERY_CRIT 0x67 -#define TYPE_PMGM_SWITCH_TO_BAT 0x68 -#define TYPE_PMGM_SWITCH_TO_AC 0x69 -#define TYPE_PMGM_KBD_OR_MOUSE 0x6a -#define TYPE_PMGM_ENCLOS_OPEN 0x6b -#define TYPE_PMGM_ENCLOS_CLOSED 0x6c -#define TYPE_PMGM_RING_INDICATE 0x6d -#define TYPE_PMGM_LAN_ATTENTION 0x6e -#define TYPE_PMGM_TIME_ALARM 0x6f -#define TYPE_PMGM_CONFIG_CHANGE 0x70 -#define TYPE_PMGM_SERVICE_PROC 0x71 - -typedef struct _rtas_error_log { - unsigned long version:8; /* Architectural version */ - unsigned long severity:3; /* Severity level of error */ - unsigned long disposition:2; /* Degree of recovery */ - unsigned long extended:1; /* extended log present? */ - unsigned long /* reserved */ :2; /* Reserved for future use */ - unsigned long initiator:4; /* Initiator of event */ - unsigned long target:4; /* Target of failed operation */ - unsigned long type:8; /* General event or error*/ - unsigned long extended_log_length:32; /* length in bytes */ -} rtas_error_log; - -/* ****************************************************************** */ -#define ppc_rtas_errorlog_check_severity(x) \ - (_errlog_severity[x.severity]) -#define ppc_rtas_errorlog_check_target(x) \ - (_errlog_target[x.target]) -#define ppc_rtas_errorlog_check_initiator(x) \ - (_errlog_initiator[x.initiator]) -#define ppc_rtas_errorlog_check_extended(x) \ - (_errlog_extended[x.extended]) -#define ppc_rtas_errorlog_disect_extended(x) \ - do { /* implement me */ } while(0) -extern const char * ppc_rtas_errorlog_check_type (rtas_error_log error_log); -extern int ppc_rtas_errorlog_scan(void); - - -#endif /* __ERROR_LOG_H__ */ diff -urN linux-2.4.26/arch/ppc/platforms/proc_rtas.c linux-2.4.27/arch/ppc/platforms/proc_rtas.c --- linux-2.4.26/arch/ppc/platforms/proc_rtas.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/ppc/platforms/proc_rtas.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,784 +0,0 @@ -/* - * arch/ppc/kernel/proc_rtas.c - * Copyright (C) 2000 Tilmann Bitterberg - * (tilmann@bitterberg.de) - * - * RTAS (Runtime Abstraction Services) stuff - * Intention is to provide a clean user interface - * to use the RTAS. - * - * TODO: - * Split off a header file and maybe move it to a different - * location. Write Documentation on what the /proc/rtas/ entries - * actually do. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include /* for ppc_md */ -#include - -/* Token for Sensors */ -#define KEY_SWITCH 0x0001 -#define ENCLOSURE_SWITCH 0x0002 -#define THERMAL_SENSOR 0x0003 -#define LID_STATUS 0x0004 -#define POWER_SOURCE 0x0005 -#define BATTERY_VOLTAGE 0x0006 -#define BATTERY_REMAINING 0x0007 -#define BATTERY_PERCENTAGE 0x0008 -#define EPOW_SENSOR 0x0009 -#define BATTERY_CYCLESTATE 0x000a -#define BATTERY_CHARGING 0x000b - -/* IBM specific sensors */ -#define IBM_SURVEILLANCE 0x2328 /* 9000 */ -#define IBM_FANRPM 0x2329 /* 9001 */ -#define IBM_VOLTAGE 0x232a /* 9002 */ -#define IBM_DRCONNECTOR 0x232b /* 9003 */ -#define IBM_POWERSUPPLY 0x232c /* 9004 */ -#define IBM_INTQUEUE 0x232d /* 9005 */ - -/* Status return values */ -#define SENSOR_CRITICAL_HIGH 13 -#define SENSOR_WARNING_HIGH 12 -#define SENSOR_NORMAL 11 -#define SENSOR_WARNING_LOW 10 -#define SENSOR_CRITICAL_LOW 9 -#define SENSOR_SUCCESS 0 -#define SENSOR_HW_ERROR -1 -#define SENSOR_BUSY -2 -#define SENSOR_NOT_EXIST -3 -#define SENSOR_DR_ENTITY -9000 - -/* Location Codes */ -#define LOC_SCSI_DEV_ADDR 'A' -#define LOC_SCSI_DEV_LOC 'B' -#define LOC_CPU 'C' -#define LOC_DISKETTE 'D' -#define LOC_ETHERNET 'E' -#define LOC_FAN 'F' -#define LOC_GRAPHICS 'G' -/* reserved / not used 'H' */ -#define LOC_IO_ADAPTER 'I' -/* reserved / not used 'J' */ -#define LOC_KEYBOARD 'K' -#define LOC_LCD 'L' -#define LOC_MEMORY 'M' -#define LOC_NV_MEMORY 'N' -#define LOC_MOUSE 'O' -#define LOC_PLANAR 'P' -#define LOC_OTHER_IO 'Q' -#define LOC_PARALLEL 'R' -#define LOC_SERIAL 'S' -#define LOC_DEAD_RING 'T' -#define LOC_RACKMOUNTED 'U' /* for _u_nit is rack mounted */ -#define LOC_VOLTAGE 'V' -#define LOC_SWITCH_ADAPTER 'W' -#define LOC_OTHER 'X' -#define LOC_FIRMWARE 'Y' -#define LOC_SCSI 'Z' - -/* Tokens for indicators */ -#define TONE_FREQUENCY 0x0001 /* 0 - 1000 (HZ)*/ -#define TONE_VOLUME 0x0002 /* 0 - 100 (%) */ -#define SYSTEM_POWER_STATE 0x0003 -#define WARNING_LIGHT 0x0004 -#define DISK_ACTIVITY_LIGHT 0x0005 -#define HEX_DISPLAY_UNIT 0x0006 -#define BATTERY_WARNING_TIME 0x0007 -#define CONDITION_CYCLE_REQUEST 0x0008 -#define SURVEILLANCE_INDICATOR 0x2328 /* 9000 */ -#define DR_ACTION 0x2329 /* 9001 */ -#define DR_INDICATOR 0x232a /* 9002 */ -/* 9003 - 9004: Vendor specific */ -#define GLOBAL_INTERRUPT_QUEUE 0x232d /* 9005 */ -/* 9006 - 9999: Vendor specific */ - -/* other */ -#define MAX_SENSORS 17 /* I only know of 17 sensors */ -#define MAX_LINELENGTH 256 -#define SENSOR_PREFIX "ibm,sensor-" -#define cel_to_fahr(x) ((x*9/5)+32) - - -/* Globals */ -static struct proc_dir_entry *proc_rtas; -static struct rtas_sensors sensors; -static struct device_node *rtas; -static unsigned long power_on_time = 0; /* Save the time the user set */ -static char progress_led[MAX_LINELENGTH]; - -static unsigned long rtas_tone_frequency = 1000; -static unsigned long rtas_tone_volume = 0; - -/* ****************STRUCTS******************************************* */ -struct individual_sensor { - unsigned int token; - unsigned int quant; -}; - -struct rtas_sensors { - struct individual_sensor sensor[MAX_SENSORS]; - unsigned int quant; -}; - -/* ****************************************************************** */ -/* Declarations */ -static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, - int count, int *eof, void *data); -static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); - -static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, - size_t count, loff_t *ppos); -static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, - size_t count, loff_t *ppos); - -struct file_operations ppc_rtas_poweron_operations = { - read: ppc_rtas_poweron_read, - write: ppc_rtas_poweron_write -}; -struct file_operations ppc_rtas_progress_operations = { - read: ppc_rtas_progress_read, - write: ppc_rtas_progress_write -}; - -struct file_operations ppc_rtas_clock_operations = { - read: ppc_rtas_clock_read, - write: ppc_rtas_clock_write -}; - -struct file_operations ppc_rtas_tone_freq_operations = { - read: ppc_rtas_tone_freq_read, - write: ppc_rtas_tone_freq_write -}; -struct file_operations ppc_rtas_tone_volume_operations = { - read: ppc_rtas_tone_volume_read, - write: ppc_rtas_tone_volume_write -}; - -int ppc_rtas_find_all_sensors (void); -int ppc_rtas_process_sensor(struct individual_sensor s, int state, - int error, char * buf); -char * ppc_rtas_process_error(int error); -int get_location_code(struct individual_sensor s, char * buf); -int check_location_string (char *c, char * buf); -int check_location (char *c, int idx, char * buf); - -/* ****************************************************************** */ -/* MAIN */ -/* ****************************************************************** */ -void proc_rtas_init(void) -{ - struct proc_dir_entry *entry; - - rtas = find_devices("rtas"); - if ((rtas == 0) || (_machine != _MACH_chrp)) { - return; - } - - proc_rtas = proc_mkdir("rtas", 0); - if (proc_rtas == 0) - return; - - /* /proc/rtas entries */ - - entry = create_proc_entry("progress", S_IRUGO|S_IWUSR, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_progress_operations; - - entry = create_proc_entry("clock", S_IRUGO|S_IWUSR, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_clock_operations; - - entry = create_proc_entry("poweron", S_IWUSR|S_IRUGO, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_poweron_operations; - - create_proc_read_entry("sensors", S_IRUGO, proc_rtas, - ppc_rtas_sensor_read, NULL); - - entry = create_proc_entry("frequency", S_IWUSR|S_IRUGO, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_tone_freq_operations; - - entry = create_proc_entry("volume", S_IWUSR|S_IRUGO, proc_rtas); - if (entry) entry->proc_fops = &ppc_rtas_tone_volume_operations; -} - -/* ****************************************************************** */ -/* POWER-ON-TIME */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - struct rtc_time tm; - unsigned long nowtime; - char *dest; - int error; - - nowtime = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_poweron_write: Invalid time\n"); - return count; - } - power_on_time = nowtime; /* save the time */ - - to_tm(nowtime, &tm); - - error = call_rtas("set-time-for-power-on", 7, 1, NULL, - tm.tm_year, tm.tm_mon, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */); - if (error != 0) - printk(KERN_WARNING "error: setting poweron time returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n; - if (power_on_time == 0) - n = sprintf(buf, "Power on time not set\n"); - else - n = sprintf(buf, "%lu\n", power_on_time); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} - -/* ****************************************************************** */ -/* PROGRESS */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - unsigned long hex; - - strcpy(progress_led, buf); /* save the string */ - /* Lets see if the user passed hexdigits */ - hex = simple_strtoul(buf, NULL, 10); - - ppc_md.progress ((char *)buf, hex); - return count; - - /* clear the line */ /* ppc_md.progress(" ", 0xffff);*/ -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n = 0; - if (progress_led != NULL) - n = sprintf (buf, "%s\n", progress_led); - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} - -/* ****************************************************************** */ -/* CLOCK */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - struct rtc_time tm; - unsigned long nowtime; - char *dest; - int error; - - nowtime = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_clock_write: Invalid time\n"); - return count; - } - - to_tm(nowtime, &tm); - 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 returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - unsigned int year, mon, day, hour, min, sec; - unsigned long *ret = kmalloc(4*8, GFP_KERNEL); - int n, error; - - error = call_rtas("get-time-of-day", 0, 8, ret); - - year = ret[0]; mon = ret[1]; day = ret[2]; - hour = ret[3]; min = ret[4]; sec = ret[5]; - - if (error != 0){ - printk(KERN_WARNING "error: reading the clock returned: %s\n", - ppc_rtas_process_error(error)); - n = sprintf (buf, "0"); - } else { - n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec)); - } - kfree(ret); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} - -/* ****************************************************************** */ -/* SENSOR STUFF */ -/* ****************************************************************** */ -static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, - int count, int *eof, void *data) -{ - int i,j,n; - unsigned long ret; - int state, error; - char buffer[MAX_LINELENGTH*MAX_SENSORS]; /* May not be enough */ - - if (count < 0) - return -EINVAL; - - n = sprintf ( buffer , "RTAS (RunTime Abstraction Services) Sensor Information\n"); - n += sprintf ( buffer+n, "Sensor\t\tValue\t\tCondition\tLocation\n"); - n += sprintf ( buffer+n, "********************************************************\n"); - - if (ppc_rtas_find_all_sensors() != 0) { - n += sprintf ( buffer+n, "\nNo sensors are available\n"); - goto return_string; - } - - for (i=0; i= 0) { - error = call_rtas("get-sensor-state", 2, 2, &ret, - sensors.sensor[i].token, sensors.sensor[i].quant-j); - state = (int) ret; - n += ppc_rtas_process_sensor(sensors.sensor[i], state, error, buffer+n ); - n += sprintf (buffer+n, "\n"); - j--; - } /* while */ - } /* for */ - -return_string: - if (off >= strlen(buffer)) { - *eof = 1; - return 0; - } - if (n > strlen(buffer) - off) - n = strlen(buffer) - off; - if (n > count) - n = count; - else - *eof = 1; - memcpy(buf, buffer + off, n); - *start = buf; - return n; -} - -/* ****************************************************************** */ - -int ppc_rtas_find_all_sensors (void) -{ - unsigned long *utmp; - int len, i, j; - - utmp = (unsigned long *) get_property(rtas, "rtas-sensors", &len); - if (utmp == NULL) { - printk (KERN_ERR "error: could not get rtas-sensors\n"); - return 1; - } - - sensors.quant = len / 8; /* int + int */ - - for (i=0, j=0; j= llen) pos=0; - } - return n; -} -/* ****************************************************************** */ -/* INDICATORS - Tone Frequency */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - unsigned long freq; - char *dest; - int error; - freq = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n"); - return count; - } - if (freq < 0) freq = 0; - rtas_tone_frequency = freq; /* save it for later */ - error = call_rtas("set-indicator", 3, 1, NULL, - TONE_FREQUENCY, 0, freq); - if (error != 0) - printk(KERN_WARNING "error: setting tone frequency returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n; - n = sprintf(buf, "%lu\n", rtas_tone_frequency); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} -/* ****************************************************************** */ -/* INDICATORS - Tone Volume */ -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, - size_t count, loff_t *ppos) -{ - unsigned long volume; - char *dest; - int error; - volume = simple_strtoul(buf, &dest, 10); - if (*dest != '\0' && *dest != '\n') { - printk("ppc_rtas_tone_volume_write: Invalid tone volume\n"); - return count; - } - if (volume < 0) volume = 0; - if (volume > 100) volume = 100; - - rtas_tone_volume = volume; /* save it for later */ - error = call_rtas("set-indicator", 3, 1, NULL, - TONE_VOLUME, 0, volume); - if (error != 0) - printk(KERN_WARNING "error: setting tone volume returned: %s\n", - ppc_rtas_process_error(error)); - return count; -} -/* ****************************************************************** */ -static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, - size_t count, loff_t *ppos) -{ - int n; - n = sprintf(buf, "%lu\n", rtas_tone_volume); - - if (*ppos >= strlen(buf)) - return 0; - if (n > strlen(buf) - *ppos) - n = strlen(buf) - *ppos; - if (n > count) - n = count; - *ppos += n; - return n; -} diff -urN linux-2.4.26/arch/ppc64/kernel/lparcfg.c linux-2.4.27/arch/ppc64/kernel/lparcfg.c --- linux-2.4.26/arch/ppc64/kernel/lparcfg.c 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/ppc64/kernel/lparcfg.c 2004-08-07 16:26:04.593344271 -0700 @@ -415,7 +415,7 @@ pnt = (char *)(data) + p; copy_to_user(buf, (void *)pnt, count); read += count; - *ppos += read; + *ppos = p + read; return read; } diff -urN linux-2.4.26/arch/ppc64/kernel/nvram.c linux-2.4.27/arch/ppc64/kernel/nvram.c --- linux-2.4.26/arch/ppc64/kernel/nvram.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ppc64/kernel/nvram.c 2004-08-07 16:26:04.594344312 -0700 @@ -78,11 +78,12 @@ size_t count, loff_t *ppos) { unsigned long len; - char *tmp_buffer; + char *tmp_buffer; + loff_t pos = *ppos; if (verify_area(VERIFY_WRITE, buf, count)) return -EFAULT; - if (*ppos >= rtas_nvram_size) + if ((unsigned)pos != pos || pos >= rtas_nvram_size) return 0; if (count > rtas_nvram_size) count = rtas_nvram_size; @@ -93,7 +94,7 @@ return 0; } - len = read_nvram(tmp_buffer, count, ppos); + len = read_nvram(tmp_buffer, count, &pos); if ((long)len <= 0) { kfree(tmp_buffer); return len; @@ -105,6 +106,7 @@ } kfree(tmp_buffer); + *ppos = pos; return len; } @@ -114,10 +116,11 @@ { unsigned long len; char * tmp_buffer; + loff_t pos = *ppos; if (verify_area(VERIFY_READ, buf, count)) return -EFAULT; - if (*ppos >= rtas_nvram_size) + if (pos != (unsigned) pos || pos >= rtas_nvram_size) return 0; if (count > rtas_nvram_size) count = rtas_nvram_size; @@ -133,7 +136,8 @@ return -EFAULT; } - len = write_nvram(tmp_buffer, count, ppos); + len = write_nvram(tmp_buffer, count, &pos); + *ppos = pos; kfree(tmp_buffer); return len; diff -urN linux-2.4.26/arch/ppc64/kernel/proc_pmc.c linux-2.4.27/arch/ppc64/kernel/proc_pmc.c --- linux-2.4.26/arch/ppc64/kernel/proc_pmc.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ppc64/kernel/proc_pmc.c 2004-08-07 16:26:04.595344353 -0700 @@ -445,8 +445,9 @@ } pnt = (char *)(perfmon_base.profile_buffer) + p - sizeof(unsigned int); copy_to_user(buf,(void *)pnt,count); + p += count; read += count; - *ppos += read; + *ppos = p; return read; } @@ -460,19 +461,17 @@ size_t count, loff_t *ppos) { unsigned long p = *ppos; - ssize_t read; char * pnt; if (p >= (perfmon_base.trace_length)) return 0; if (count > (perfmon_base.trace_length) - p) count = (perfmon_base.trace_length) - p; - read = 0; pnt = (char *)(perfmon_base.trace_buffer) + p; copy_to_user(buf,(void *)pnt,count); - read += count; - *ppos += read; - return read; + p += count; + *ppos = p; + return count; } static ssize_t write_trace(struct file * file, const char * buf, @@ -491,13 +490,11 @@ if (p >= (perfmon_base.timeslice_length)) return 0; if (count > (perfmon_base.timeslice_length) - p) count = (perfmon_base.timeslice_length) - p; - read = 0; pnt = (char *)(perfmon_base.timeslice_buffer) + p; copy_to_user(buf,(void *)pnt,count); - read += count; - *ppos += read; - return read; + *ppos = p + count; + return count; } static ssize_t write_timeslice(struct file * file, const char * buf, diff -urN linux-2.4.26/arch/ppc64/kernel/rtas-proc.c linux-2.4.27/arch/ppc64/kernel/rtas-proc.c --- linux-2.4.26/arch/ppc64/kernel/rtas-proc.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/ppc64/kernel/rtas-proc.c 2004-08-07 16:26:04.596344394 -0700 @@ -337,6 +337,7 @@ { char stkbuf[40]; /* its small, its on stack */ int n; + loff_t pos = *ppos; if (power_on_time == 0) n = snprintf(stkbuf, 40, "Power on time not set\n"); @@ -344,15 +345,15 @@ n = snprintf(stkbuf, 40, "%lu\n", power_on_time); int sn = strlen(stkbuf) +1; - if (*ppos >= sn) + if (pos != (unsigned)pos || pos >= sn) return 0; - if (n > sn - *ppos) - n = sn - *ppos; + if (n > sn - pos) + n = sn - pos; if (n > count) n = count; - if (copy_to_user(buf, stkbuf + (*ppos), n)) + if (copy_to_user(buf, stkbuf + pos, n)) return -EFAULT; - *ppos += n; + *ppos = pos + n; return n; } @@ -384,6 +385,7 @@ size_t count, loff_t *ppos) { int n = 0, sn; + loff_t pos = *ppos; if (progress_led == NULL) return 0; @@ -396,20 +398,20 @@ n = sprintf (tmpbuf, "%s\n", progress_led); sn = strlen (tmpbuf) +1; - if (*ppos >= sn) { + if (pos != (unsigned)pos || pos >= sn) { kfree(tmpbuf); return 0; } - if (n > sn - *ppos) - n = sn - *ppos; + if (n > sn - pos) + n = sn - pos; if (n > count) n = count; - if (copy_to_user(buf, tmpbuf + (*ppos), n)) { + if (copy_to_user(buf, tmpbuf + pos), n) { kfree(tmpbuf); return -EFAULT; } kfree(tmpbuf); - *ppos += n; + *ppos = pos + n; return n; } @@ -453,6 +455,7 @@ unsigned int year, mon, day, hour, min, sec; unsigned long *ret = kmalloc(4*8, GFP_KERNEL); int n, error; + loff_t pos = *ppos; error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret); @@ -471,16 +474,16 @@ kfree(ret); int sn = strlen(stkbuf) +1; - if (*ppos >= sn) + if (pos != (unsigned)pos || pos >= sn) return 0; - if (n > sn - *ppos) - n = sn - *ppos; + if (n > sn - pos) + n = sn - pos; if (n > count) n = count; - if (copy_to_user(buf, stkbuf + (*ppos), n)) + if (copy_to_user(buf, stkbuf + pos, n)) return -EFAULT; - *ppos += n; + *ppos = pos + n; return n; } @@ -878,20 +881,21 @@ { int n, sn; char stkbuf[40]; /* its small, its on stack */ + loff_t pos = *ppos; n = snprintf(stkbuf, 40, "%lu\n", rtas_tone_frequency); sn = strlen(stkbuf) +1; - if (*ppos >= sn) + if (pos != (unsigned)pos || pos >= sn) return 0; - if (n > sn - *ppos) - n = sn - *ppos; + if (n > sn - pos) + n = sn - pos; if (n > count) n = count; - if (copy_to_user(buf, stkbuf + (*ppos), n)) + if (copy_to_user(buf, stkbuf + pos, n)) return -EFAULT; - *ppos += n; + *ppos = pos + n; return n; } /* ****************************************************************** */ @@ -933,19 +937,20 @@ { int n, sn; char stkbuf[40]; /* its small, its on stack */ + loff_t pos = *ppos; n = snprintf(stkbuf, 40, "%lu\n", rtas_tone_volume); sn = strlen(stkbuf) +1; - if (*ppos >= sn) + if (pos != (unsigned)pos || pos >= sn) return 0; - if (n > sn - *ppos) - n = sn - *ppos; + if (n > sn - pos) + n = sn - pos; if (n > count) n = count; - if (copy_to_user(buf, stkbuf + (*ppos), n)) + if (copy_to_user(buf, stkbuf + pos, n)) return -EFAULT; - *ppos += n; + *ppos = pos + n; return n; } @@ -1064,6 +1069,7 @@ char * buffer; int i, sn; int n = 0; + loff_t pos = *ppos; int m = MAX_ERRINJCT_TOKENS * (ERRINJCT_TOKEN_LEN+1); buffer = (char *)kmalloc(m, GFP_KERNEL); @@ -1078,22 +1084,22 @@ } sn = strlen(buffer) +1; - if (*ppos >= sn) { + if (pos != (unsigned)pos || pos >= sn) { kfree(buffer); return 0; } - if (n > sn - *ppos) - n = sn - *ppos; + if (n > sn - pos) + n = sn - pos; if (n > count) n = count; - if (copy_to_user(buf, buffer + *ppos, n)) { + if (copy_to_user(buf, buffer + pos, n)) { kfree(buffer); return -EFAULT; } - *ppos += n; + *ppos = pos + n; kfree(buffer); return n; diff -urN linux-2.4.26/arch/s390/kernel/debug.c linux-2.4.27/arch/s390/kernel/debug.c --- linux-2.4.26/arch/s390/kernel/debug.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/s390/kernel/debug.c 2004-08-07 16:26:04.597344435 -0700 @@ -470,8 +470,8 @@ goto out; } out: - p_info->offset = *offset + count; - p_info->act_entry_offset = size; + p_info->offset += count; + p_info->act_entry_offset = size; *offset = p_info->offset; return count; } @@ -1068,7 +1068,7 @@ input_buf[0]); } out: - *offset += in_buf_size; + *offset = in_buf_size; return rc; /* number of input characters */ } @@ -1135,7 +1135,7 @@ printk(KERN_INFO "debug: area `%c` is not valid\n", input_buf[0]); out: - *offset += in_buf_size; + *offset = in_buf_size; return rc; /* number of input characters */ } diff -urN linux-2.4.26/arch/s390x/kernel/debug.c linux-2.4.27/arch/s390x/kernel/debug.c --- linux-2.4.26/arch/s390x/kernel/debug.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/s390x/kernel/debug.c 2004-08-07 16:26:04.598344476 -0700 @@ -470,7 +470,7 @@ goto out; } out: - p_info->offset = *offset + count; + p_info->offset += count; p_info->act_entry_offset = size; *offset = p_info->offset; return count; @@ -1068,7 +1068,7 @@ input_buf[0]); } out: - *offset += in_buf_size; + *offset = in_buf_size; return rc; /* number of input characters */ } @@ -1135,7 +1135,7 @@ printk(KERN_INFO "debug: area `%c` is not valid\n", input_buf[0]); out: - *offset += in_buf_size; + *offset = in_buf_size; return rc; /* number of input characters */ } diff -urN linux-2.4.26/arch/sh64/config.in linux-2.4.27/arch/sh64/config.in --- linux-2.4.26/arch/sh64/config.in 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/sh64/config.in 2004-08-07 16:26:04.599344517 -0700 @@ -58,8 +58,7 @@ # Use 32-bit addressing for now. # EMI based. -# P2 (UNCACHED) required to use identity mapping -# P1 (CACHED) assumes non-identity. +# (CACHED) assumes non-identity. # # Memory options @@ -68,7 +67,6 @@ int 'Memory size (in MB)' CONFIG_MEMORY_SIZE_IN_MB 64 hex 'Cached Area Offset' CONFIG_CACHED_MEMORY_OFFSET 20000000 -hex 'Uncached Area Offset' CONFIG_UNCACHED_MEMORY_OFFSET 00000000 hex 'Physical memory start address' CONFIG_MEMORY_START 80000000 # @@ -304,6 +302,7 @@ bool "Debug: audit page tables on return from syscall/exception/interrupt" CONFIG_SH64_PAGE_TABLE_AUDIT dep_bool "Debug: report TLB fill/purge activity through /proc/tlb" CONFIG_SH64_PROC_TLB $CONFIG_PROC_FS dep_bool "Debug: report ASIDS through /proc/asids" CONFIG_SH64_PROC_ASIDS $CONFIG_PROC_FS +bool "Debug: set SR.WATCH to enable hardware watchpoints and trace" CONFIG_SH64_SR_WATCH int 'Kernel messages buffer length shift (0 = default)' CONFIG_LOG_BUF_SHIFT 0 diff -urN linux-2.4.26/arch/sh64/defconfig linux-2.4.27/arch/sh64/defconfig --- linux-2.4.26/arch/sh64/defconfig 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/sh64/defconfig 2004-08-07 16:26:04.599344517 -0700 @@ -490,6 +490,7 @@ # CONFIG_SH64_PAGE_TABLE_AUDIT is not set # CONFIG_SH64_PROC_TLB is not set # CONFIG_SH64_PROC_ASIDS is not set +# CONFIG_SH64_SR_WATCH is not set # # Library routines diff -urN linux-2.4.26/arch/sh64/kernel/entry.S linux-2.4.27/arch/sh64/kernel/entry.S --- linux-2.4.26/arch/sh64/kernel/entry.S 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/kernel/entry.S 2004-08-07 16:26:04.601344600 -0700 @@ -1607,12 +1607,6 @@ shlli r36, 31, r36 andc r1, r36, r1 /* turn sr.mmu off in real mode section */ - /* Bodge : force sr.watch high on return. Can't understand why else this - isn't happening. */ - movi 1, r38 - shlli r38, 26, r38 - or r38, r0, r0 - putcon r1, ssr _loada .poke0-CONFIG_CACHED_MEMORY_OFFSET, r36 /* real mode target address */ _loada 1f, r37 /* virtual mode return addr */ diff -urN linux-2.4.26/arch/sh64/kernel/head.S linux-2.4.27/arch/sh64/kernel/head.S --- linux-2.4.26/arch/sh64/kernel/head.S 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/kernel/head.S 2004-08-07 16:26:04.601344600 -0700 @@ -37,18 +37,29 @@ #define MMUDR_END DTLB_LAST_VAR_UNRESTRICTED+TLB_STEP #define MMUDR_STEP TLB_STEP +/* Safety check : CONFIG_CACHED_MEMORY_OFFSET has to be a multiple of 512Mb */ +#if (CONFIG_CACHED_MEMORY_OFFSET & ((1UL<<29)-1)) +#error "CONFIG_CACHED_MEMORY_OFFSET must be a multiple of 512Mb" +#endif + /* * MMU defines: Fixed TLBs. */ -#define MMUIR_TEXT_H 0x0000000000000003 | (CONFIG_CACHED_MEMORY_OFFSET + CONFIG_MEMORY_START) +/* Deal safely with the case where the base of RAM is not 512Mb aligned */ + +#define ALIGN_512M_MASK (0xffffffffe0000000) +#define ALIGNED_EFFECTIVE ((CONFIG_CACHED_MEMORY_OFFSET + CONFIG_MEMORY_START) & ALIGN_512M_MASK) +#define ALIGNED_PHYSICAL (CONFIG_MEMORY_START & ALIGN_512M_MASK) + +#define MMUIR_TEXT_H (0x0000000000000003 | ALIGNED_EFFECTIVE) /* Enabled, Shared, ASID 0, Eff. Add. 0xA0000000 */ -#define MMUIR_TEXT_L 0x000000000000009a | (CONFIG_MEMORY_START) +#define MMUIR_TEXT_L (0x000000000000009a | ALIGNED_PHYSICAL) /* 512 Mb, Cacheable, Write-back, execute, Not User, Ph. Add. */ -#define MMUDR_CACHED_H 0x0000000000000003 | (CONFIG_CACHED_MEMORY_OFFSET + CONFIG_MEMORY_START) +#define MMUDR_CACHED_H 0x0000000000000003 | ALIGNED_EFFECTIVE /* Enabled, Shared, ASID 0, Eff. Add. 0xA0000000 */ -#define MMUDR_CACHED_L 0x000000000000015a | (CONFIG_MEMORY_START) +#define MMUDR_CACHED_L 0x000000000000015a | ALIGNED_PHYSICAL /* 512 Mb, Cacheable, Write-back, read/write, Not User, Ph. Add. */ #ifdef CONFIG_ICACHE_DISABLED @@ -209,15 +220,19 @@ /* Map one big (512Mb) page for ITLB */ movi MMUIR_FIRST, r21 movi MMUIR_TEXT_L, r22 /* PTEL first */ + add.l r22, r63, r22 /* Sign extend */ putcfg r21, 1, r22 /* Set MMUIR[0].PTEL */ movi MMUIR_TEXT_H, r22 /* PTEH last */ + add.l r22, r63, r22 /* Sign extend */ putcfg r21, 0, r22 /* Set MMUIR[0].PTEH */ /* Map one big CACHED (512Mb) page for DTLB */ movi MMUDR_FIRST, r21 movi MMUDR_CACHED_L, r22 /* PTEL first */ + add.l r22, r63, r22 /* Sign extend */ putcfg r21, 1, r22 /* Set MMUDR[0].PTEL */ movi MMUDR_CACHED_H, r22 /* PTEH last */ + add.l r22, r63, r22 /* Sign extend */ putcfg r21, 0, r22 /* Set MMUDR[0].PTEH */ /* diff -urN linux-2.4.26/arch/sh64/kernel/pci_sh5.c linux-2.4.27/arch/sh64/kernel/pci_sh5.c --- linux-2.4.26/arch/sh64/kernel/pci_sh5.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/kernel/pci_sh5.c 2004-08-07 16:26:04.602344641 -0700 @@ -320,43 +320,70 @@ { int result = -1; - if (dev->bus->number == 0) { - switch ((slot + (pin-1)) & 3) { - case 0: - result = IRQ_INTA; - break; - case 1: - result = IRQ_INTB; - break; - case 2: - result = IRQ_INTC; - break; - case 3: - result = IRQ_INTD; - break; - } - } - - if (dev->bus->number == 2) { - switch((slot + (pin-1)) & 3) { - case 0: - result = IRQ_P2INTA; - break; - case 1: - result = IRQ_P2INTB; - break; - case 2: - result = IRQ_P2INTC; - break; - case 3: - result = IRQ_P2INTD; - break; - } - } - - dprintk("map_cayman_irq for dev %d on bus %d slot %d, pin is %d : irq=%d\n", - dev->devfn,dev->bus->number,slot,pin,result); + /* The complication here is that the PCI IRQ lines from the Cayman's 2 + 5V slots get into the CPU via a different path from the IRQ lines + from the 3 3.3V slots. Thus, we have to detect whether the card's + interrupts go via the 5V or 3.3V path, i.e. the 'bridge swizzling' + at the point where we cross from 5V to 3.3V is not the normal case. + + The added complication is that we don't know that the 5V slots are + always bus 2, because a card containing a PCI-PCI bridge may be + plugged into a 3.3V slot, and this changes the bus numbering. + + Also, the Cayman has an intermediate PCI bus that goes a custom + expansion board header (and to the secondary bridge). This bus has + never been used in practice. + + The 1ary onboard PCI-PCI bridge is device 3 on bus 0 + The 2ary onboard PCI-PCI bridge is device 0 on the 2ary bus of the 1ary bridge. + */ + + struct slot_pin { + int slot; + int pin; + } path[4]; + int i=0; + int base; + + while (dev->bus->number > 0) { + + slot = path[i].slot = PCI_SLOT(dev->devfn); + pin = path[i].pin = bridge_swizzle(pin, slot); + dev = dev->bus->self; + i++; + if (i > 3) panic("PCI path to root bus too long!\n"); + } + slot = PCI_SLOT(dev->devfn); + /* This is the slot on bus 0 through which the device is eventually + reachable. */ + + /* Now work back up. */ + if ((slot < 3) || (i == 0)) { + /* Bus 0 (incl. PCI-PCI bridge itself) : perform the final + swizzle now. */ + result = IRQ_INTA + bridge_swizzle(pin, slot) - 1; + } else { + i--; + slot = path[i].slot; + pin = path[i].pin; + if (slot > 0) { + panic("PCI expansion bus device found - not handled!\n"); + } else { + if (i > 0) { + /* 5V slots */ + i--; + slot = path[i].slot; + pin = path[i].pin; + /* 'pin' was swizzled earlier wrt slot, don't do it again. */ + result = IRQ_P2INTA + (pin - 1); + } else { + /* IRQ for 2ary PCI-PCI bridge : unused */ + result = -1; + } + } + } + return result; } diff -urN linux-2.4.26/arch/sh64/kernel/time.c linux-2.4.27/arch/sh64/kernel/time.c --- linux-2.4.26/arch/sh64/kernel/time.c 2004-02-18 05:36:30.000000000 -0800 +++ linux-2.4.27/arch/sh64/kernel/time.c 2004-08-07 16:26:04.603344682 -0700 @@ -404,13 +404,14 @@ { unsigned int count; unsigned long __dummy; - + unsigned long ctc_val_init, ctc_val; + /* ** Regardless the toolchain, force the compiler to use the ** arbitrary register r3 as a clock tick counter. ** NOTE: r3 must be in accordance with rtc_interrupt() */ - register unsigned long long __clock_tick_count __asm__ ("r3"); + register unsigned long long __rtc_irq_flag __asm__ ("r3"); sti(); do {} while (ctrl_inb(R64CNT) != 0); @@ -419,13 +420,17 @@ /* * r3 is arbitrary. CDC does not support "=z". */ + ctc_val_init = 0xffffffff; + ctc_val = ctc_val_init; + asm volatile("gettr " __t0 ", %1\n\t" + "putcon %0, cr62\n\t" "and %2, r63, %2\n\t" "_pta 4, " __t0 "\n\t" - "addi %0, 1, %0\n\t" "beq/l %2, r63, " __t0 "\n\t" "ptabs %1, " __t0 "\n\t" - : "=r"(count), "=r" (__dummy), "=r" (__clock_tick_count) + "getcon cr62, %0\n\t" + : "=r"(ctc_val), "=r" (__dummy), "=r" (__rtc_irq_flag) : "0" (0)); cli(); /* @@ -445,11 +450,13 @@ * .... * * SH-5: - * CPU clock = 2 stages * loop - * .... + * Use CTC register to count. This approach returns the right value + * even if the I-cache is disabled (e.g. whilst debugging.) * */ + count = ctc_val_init - ctc_val; /* CTC counts down */ + #if defined (CONFIG_SH_SIMULATOR) /* * Let's pretend we are a 5MHz SH-5 to avoid a too @@ -457,18 +464,13 @@ * calibration within a reasonable time. */ return 5000000; -#elif defined (CONFIG_ICACHE_DISABLED) - /* - * Let's pretend we are a 300MHz SH-5. - */ - return 300000000; #else /* * This really is count by the number of clock cycles - * per loop (2) by the ratio between a complete R64CNT + * by the ratio between a complete R64CNT * wrap-around (128) and CUI interrupt being raised (64). */ - return count*2*2; + return count*2; #endif } diff -urN linux-2.4.26/arch/sh64/lib/io.c linux-2.4.27/arch/sh64/lib/io.c --- linux-2.4.26/arch/sh64/lib/io.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/lib/io.c 2004-08-07 16:26:04.603344682 -0700 @@ -27,17 +27,23 @@ #define dprintk(x...) -//#define io_addr(x) (((unsigned)(x) & 0x000fffff)| PCI_ST50_IO_ADDR ) - +static int io_addr(int x) { + if (x < 0x400) { #ifdef CONFIG_SH_CAYMAN -extern unsigned long smsc_virt; -extern unsigned long pciio_virt; -#define io_addr(x) ( ((x)<0x400) ? \ - (((x) << 2)|smsc_virt) : \ - ((unsigned long)(x)+pciio_virt) ) + return (x << 2) | smsc_superio_virt; +#else + panic ("Illegal access to I/O port 0x%04x\n", x); + return 0; +#endif + } else { +#ifdef CONFIG_PCI + return (x + pciio_virt); #else -#define io_addr(x) ((unsigned long)(x)+pciio_virt) + panic ("Illegal access to I/O port 0x%04x\n", x); + return 0; #endif + } +} unsigned long inb(unsigned long port) { diff -urN linux-2.4.26/arch/sh64/mach-cayman/irq.c linux-2.4.27/arch/sh64/mach-cayman/irq.c --- linux-2.4.26/arch/sh64/mach-cayman/irq.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/mach-cayman/irq.c 2004-08-07 16:26:04.604344723 -0700 @@ -27,9 +27,12 @@ #define EPLD_STATUS_BASE (epld_virt + 0x10) #define EPLD_MASK_BASE (epld_virt + 0x20) +/* Note the SMSC SuperIO chip and SMSC LAN chip interrupts are all muxed onto + the same SH-5 interrupt */ + static void cayman_interrupt_smsc(int irq, void *dev_id, struct pt_regs *regs) { - printk(KERN_INFO "CAYMAN: spurious interrupt\n"); + printk(KERN_INFO "CAYMAN: spurious SMSC interrupt\n"); } static void cayman_interrupt_pci2(int irq, void *dev_id, struct pt_regs *regs) diff -urN linux-2.4.26/arch/sh64/mach-cayman/setup.c linux-2.4.27/arch/sh64/mach-cayman/setup.c --- linux-2.4.26/arch/sh64/mach-cayman/setup.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/mach-cayman/setup.c 2004-08-07 16:26:04.604344723 -0700 @@ -74,7 +74,7 @@ #define ITI TOP_PRIORITY /* WDT Ints */ /* Setup for the SMSC FDC37C935 */ -#define SMSC_BASE 0x04000000 +#define SMSC_SUPERIO_BASE 0x04000000 #define SMSC_CONFIG_PORT_ADDR 0x3f0 #define SMSC_INDEX_PORT_ADDR SMSC_CONFIG_PORT_ADDR #define SMSC_DATA_PORT_ADDR 0x3f1 @@ -91,14 +91,14 @@ #define SMSC_KEYBOARD_DEVICE 7 -#define SMSC_READ_INDEXED(index) ({ \ +#define SMSC_SUPERIO_READ_INDEXED(index) ({ \ outb((index), SMSC_INDEX_PORT_ADDR); \ inb(SMSC_DATA_PORT_ADDR); }) -#define SMSC_WRITE_INDEXED(val, index) ({ \ +#define SMSC_SUPERIO_WRITE_INDEXED(val, index) ({ \ outb((index), SMSC_INDEX_PORT_ADDR); \ outb((val), SMSC_DATA_PORT_ADDR); }) -unsigned long smsc_virt; +unsigned long smsc_superio_virt; /* * Platform dependent structures: maps and parms block. @@ -145,13 +145,13 @@ RES, RES, RES, RES, RES, RES, RES, ITI, /* IRQ 56-63 */ }; -static int __init smsc_setup(void) +static int __init smsc_superio_setup(void) { unsigned char devid, devrev; - smsc_virt = onchip_remap(SMSC_BASE, 1024, "SMSC"); - if (!smsc_virt) { - panic("Unable to remap SMSC\n"); + smsc_superio_virt = onchip_remap(SMSC_SUPERIO_BASE, 1024, "SMSC SuperIO"); + if (!smsc_superio_virt) { + panic("Unable to remap SMSC SuperIO\n"); } /* Initially the chip is in run state */ @@ -160,20 +160,20 @@ outb(SMSC_ENTER_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR); /* Read device ID info */ - devid = SMSC_READ_INDEXED(SMSC_DEVICE_ID_INDEX); - devrev = SMSC_READ_INDEXED(SMSC_DEVICE_REV_INDEX); - printk("SMSC devid %02x rev %02x\n", devid, devrev); + devid = SMSC_SUPERIO_READ_INDEXED(SMSC_DEVICE_ID_INDEX); + devrev = SMSC_SUPERIO_READ_INDEXED(SMSC_DEVICE_REV_INDEX); + printk("SMSC SuperIO devid %02x rev %02x\n", devid, devrev); /* Select the keyboard device */ - SMSC_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX); + SMSC_SUPERIO_WRITE_INDEXED(SMSC_KEYBOARD_DEVICE, SMCS_LOGICAL_DEV_INDEX); /* enable it */ - SMSC_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX); + SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_ACTIVATE_INDEX); /* Select the interrupts */ /* On a PC keyboard is IRQ1, mouse is IRQ12 */ - SMSC_WRITE_INDEXED(1, SMSC_PRIMARY_INT_INDEX); - SMSC_WRITE_INDEXED(12, SMSC_SECONDARY_INT_INDEX); + SMSC_SUPERIO_WRITE_INDEXED(1, SMSC_PRIMARY_INT_INDEX); + SMSC_SUPERIO_WRITE_INDEXED(12, SMSC_SECONDARY_INT_INDEX); /* Exit the configuraton state */ outb(SMSC_EXIT_CONFIG_KEY, SMSC_CONFIG_PORT_ADDR); @@ -184,7 +184,7 @@ /* This is grotty, but, because kernel is always referenced on the link line * before any devices, this is safe. */ -__initcall(smsc_setup); +__initcall(smsc_superio_setup); void __init platform_setup(void) { diff -urN linux-2.4.26/arch/sh64/mm/cache.c linux-2.4.27/arch/sh64/mm/cache.c --- linux-2.4.26/arch/sh64/mm/cache.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/mm/cache.c 2004-08-07 16:26:04.605344764 -0700 @@ -474,7 +474,7 @@ /* Assumes this address (+ (2**n_synbits) pages up from it) aren't used for anything else in the kernel */ -#define MAGIC_PAGE0_START 0xffffffffe0000000ULL +#define MAGIC_PAGE0_START 0xffffffffec000000ULL static void sh64_dcache_purge_coloured_phy_page(unsigned long paddr, unsigned long eaddr) { @@ -740,8 +740,8 @@ /****************************************************************************/ /* These *MUST* lie in an area of virtual address space that's otherwise unused. */ -#define UNIQUE_EADDR_START 0xc0000000UL -#define UNIQUE_EADDR_END 0xd0000000UL +#define UNIQUE_EADDR_START 0xe0000000UL +#define UNIQUE_EADDR_END 0xe8000000UL static unsigned long sh64_make_unique_eaddr(unsigned long user_eaddr, unsigned long paddr) { diff -urN linux-2.4.26/arch/sh64/vmlinux.lds.S linux-2.4.27/arch/sh64/vmlinux.lds.S --- linux-2.4.26/arch/sh64/vmlinux.lds.S 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sh64/vmlinux.lds.S 2004-08-07 16:26:04.606344805 -0700 @@ -38,7 +38,6 @@ OUTPUT_ARCH(sh:sh5) #define C_PHYS(x) AT (ADDR(x) - CONFIG_CACHED_MEMORY_OFFSET) -#define U_PHYS(x) AT (ADDR(x) - CONFIG_UNCACHED_MEMORY_OFFSET) ENTRY(__start) SECTIONS diff -urN linux-2.4.26/arch/sparc/Makefile linux-2.4.27/arch/sparc/Makefile --- linux-2.4.26/arch/sparc/Makefile 2001-07-28 12:12:37.000000000 -0700 +++ linux-2.4.27/arch/sparc/Makefile 2004-08-07 16:26:04.606344805 -0700 @@ -16,7 +16,7 @@ # debugging of the kernel to get the proper debugging information. IS_EGCS := $(shell if $(CC) -m32 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo y; else echo n; fi; ) -NEW_GAS := $(shell if $(LD) --version 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi) +NEW_GAS := $(shell if $(LD) -V 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi) ifeq ($(NEW_GAS),y) AS := $(AS) -32 diff -urN linux-2.4.26/arch/sparc/kernel/process.c linux-2.4.27/arch/sparc/kernel/process.c --- linux-2.4.26/arch/sparc/kernel/process.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/sparc/kernel/process.c 2004-08-07 16:26:04.607344846 -0700 @@ -54,6 +54,12 @@ */ void (*pm_power_off)(void); +/* + * sysctl - toggle power-off restriction for serial console + * systems in machine_power_off() + */ +int scons_pwroff = 1; + extern void fpsave(unsigned long *, unsigned long *, void *, unsigned long *); struct task_struct *last_task_used_math = NULL; @@ -189,7 +195,7 @@ void machine_power_off(void) { #ifdef CONFIG_SUN_AUXIO - if (auxio_power_register && !serial_console) + if (auxio_power_register && (!serial_console || scons_pwroff)) *auxio_power_register |= AUXIO_POWER_OFF; #endif machine_halt(); diff -urN linux-2.4.26/arch/sparc/kernel/unaligned.c linux-2.4.27/arch/sparc/kernel/unaligned.c --- linux-2.4.26/arch/sparc/kernel/unaligned.c 2002-02-25 11:37:56.000000000 -0800 +++ linux-2.4.27/arch/sparc/kernel/unaligned.c 2004-08-07 16:26:04.608344887 -0700 @@ -136,8 +136,8 @@ return &win->locals[reg - 16]; } -static inline unsigned long compute_effective_address(struct pt_regs *regs, - unsigned int insn) +static unsigned long compute_effective_address(struct pt_regs *regs, + unsigned int insn) { unsigned int rs1 = (insn >> 14) & 0x1f; unsigned int rs2 = insn & 0x1f; @@ -152,8 +152,8 @@ } } -static inline unsigned long safe_compute_effective_address(struct pt_regs *regs, - unsigned int insn) +unsigned long safe_compute_effective_address(struct pt_regs *regs, + unsigned int insn) { unsigned int rs1 = (insn >> 14) & 0x1f; unsigned int rs2 = insn & 0x1f; diff -urN linux-2.4.26/arch/sparc/mm/fault.c linux-2.4.27/arch/sparc/mm/fault.c --- linux-2.4.26/arch/sparc/mm/fault.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/sparc/mm/fault.c 2004-08-07 16:26:04.609344928 -0700 @@ -200,6 +200,25 @@ return 0; } +extern unsigned long safe_compute_effective_address(struct pt_regs *, + unsigned int); + +static unsigned long compute_si_addr(struct pt_regs *regs, int text_fault) +{ + unsigned int insn; + + if (text_fault) + return regs->pc; + + if (regs->psr & PSR_PS) { + insn = *(unsigned int *) regs->pc; + } else { + __get_user(insn, (unsigned int *) regs->pc); + } + + return safe_compute_effective_address(regs, insn); +} + asmlinkage void do_sparc_fault(struct pt_regs *regs, int text_fault, int write, unsigned long address) { @@ -306,7 +325,7 @@ info.si_errno = 0; /* info.si_code set above to make clear whether this was a SEGV_MAPERR or SEGV_ACCERR fault. */ - info.si_addr = (void *)address; + info.si_addr = (void *) compute_si_addr(regs, text_fault); info.si_trapno = 0; force_sig_info (SIGSEGV, &info, tsk); return; @@ -360,7 +379,7 @@ info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; - info.si_addr = (void *)address; + info.si_addr = (void *) compute_si_addr(regs, text_fault); info.si_trapno = 0; force_sig_info (SIGBUS, &info, tsk); if (!from_user) @@ -523,7 +542,7 @@ info.si_errno = 0; /* info.si_code set above to make clear whether this was a SEGV_MAPERR or SEGV_ACCERR fault. */ - info.si_addr = (void *)address; + info.si_addr = (void *) address; info.si_trapno = 0; force_sig_info (SIGSEGV, &info, tsk); return; @@ -533,7 +552,7 @@ info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRERR; - info.si_addr = (void *)address; + info.si_addr = (void *) address; info.si_trapno = 0; force_sig_info (SIGBUS, &info, tsk); } diff -urN linux-2.4.26/arch/sparc/prom/memory.c linux-2.4.27/arch/sparc/prom/memory.c --- linux-2.4.26/arch/sparc/prom/memory.c 2000-01-31 23:37:19.000000000 -0800 +++ linux-2.4.27/arch/sparc/prom/memory.c 2004-08-07 16:26:04.609344928 -0700 @@ -156,7 +156,7 @@ prom_prom_taken[iter].num_bytes = (unsigned long) prom_reg_memlist[iter].reg_size; prom_prom_taken[iter].theres_more = - &prom_phys_total[iter+1]; + &prom_prom_taken[iter+1]; } prom_prom_taken[iter-1].theres_more = 0x0; diff -urN linux-2.4.26/arch/sparc64/Makefile linux-2.4.27/arch/sparc64/Makefile --- linux-2.4.26/arch/sparc64/Makefile 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/sparc64/Makefile 2004-08-07 16:26:04.610344969 -0700 @@ -12,7 +12,7 @@ # line... SHELL =/bin/bash -CC := $(shell if gcc -m64 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo gcc; else echo sparc64-linux-gcc; fi ) +CC := $(shell if $(CC) -m64 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo $(CC); else echo sparc64-linux-gcc; fi ) NEW_GCC := $(shell if $(CC) -m64 -mcmodel=medlow -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo y; else echo n; fi; ) NEW_GAS := $(shell if $(LD) -V 2>&1 | grep 'elf64_sparc' > /dev/null; then echo y; else echo n; fi) diff -urN linux-2.4.26/arch/sparc64/config.in linux-2.4.27/arch/sparc64/config.in --- linux-2.4.26/arch/sparc64/config.in 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/arch/sparc64/config.in 2004-08-07 16:26:04.610344969 -0700 @@ -21,6 +21,8 @@ mainmenu_option next_comment comment 'General setup' +source drivers/i2c/Config.in + tristate 'UltraSPARC-III bootbus i2c controller driver' CONFIG_BBC_I2C define_bool CONFIG_VT y diff -urN linux-2.4.26/arch/sparc64/defconfig linux-2.4.27/arch/sparc64/defconfig --- linux-2.4.26/arch/sparc64/defconfig 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/sparc64/defconfig 2004-08-07 16:26:04.611345011 -0700 @@ -17,6 +17,20 @@ # # General setup # + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_PHILIPSPAR is not set +# CONFIG_I2C_ELV is not set +# CONFIG_I2C_VELLEMAN is not set +# CONFIG_SCx200_I2C is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_ALGOPCF is not set +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_PROC=y CONFIG_BBC_I2C=m CONFIG_VT=y CONFIG_VT_CONSOLE=y @@ -428,6 +442,7 @@ # CONFIG_WDC_ALI15X3 is not set CONFIG_BLK_DEV_AMD74XX=m # CONFIG_AMD74XX_OVERRIDE is not set +CONFIG_BLK_DEV_ATIIXP=m CONFIG_BLK_DEV_CMD64X=y # CONFIG_BLK_DEV_TRIFLEX is not set CONFIG_BLK_DEV_CY82C693=m @@ -1121,6 +1136,7 @@ CONFIG_CRYPTO_CAST6=m CONFIG_CRYPTO_ARC4=m CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_MICHAEL_MIC=m # CONFIG_CRYPTO_TEST is not set # diff -urN linux-2.4.26/arch/sparc64/kernel/ioctl32.c linux-2.4.27/arch/sparc64/kernel/ioctl32.c --- linux-2.4.26/arch/sparc64/kernel/ioctl32.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/arch/sparc64/kernel/ioctl32.c 2004-08-07 16:26:04.615345175 -0700 @@ -1271,6 +1271,7 @@ case FDGETPRM32: { struct floppy_struct *f; + u32 u_name; f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL); if (!karg) @@ -1286,7 +1287,8 @@ err |= __get_user(f->rate, &((struct floppy_struct32 *)arg)->rate); err |= __get_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); err |= __get_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); - err |= __get_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + err |= __get_user(u_name, &((struct floppy_struct32 *)arg)->name); + f->name = (void *) (long) u_name; if (err) { err = -EFAULT; goto out; @@ -3810,13 +3812,15 @@ { struct blkpg_ioctl_arg a; struct blkpg_partition p; + u32 u_data; int err; mm_segment_t old_fs = get_fs(); err = get_user(a.op, &arg->op); err |= __get_user(a.flags, &arg->flags); err |= __get_user(a.datalen, &arg->datalen); - err |= __get_user((long)a.data, &arg->data); + err |= __get_user(u_data, &arg->data); + a.data = (void *) (long) u_data; if (err) return err; switch (a.op) { case BLKPG_ADD_PARTITION: @@ -4416,6 +4420,7 @@ COMPATIBLE_IOCTL(HDIO_SET_32BIT) COMPATIBLE_IOCTL(HDIO_SET_MULTCOUNT) COMPATIBLE_IOCTL(HDIO_DRIVE_CMD) +COMPATIBLE_IOCTL(HDIO_DRIVE_TASK) COMPATIBLE_IOCTL(HDIO_SET_PIO_MODE) COMPATIBLE_IOCTL(HDIO_SCAN_HWIF) COMPATIBLE_IOCTL(HDIO_SET_NICE) diff -urN linux-2.4.26/arch/sparc64/kernel/power.c linux-2.4.27/arch/sparc64/kernel/power.c --- linux-2.4.26/arch/sparc64/kernel/power.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/sparc64/kernel/power.c 2004-08-07 16:26:04.616345216 -0700 @@ -17,6 +17,12 @@ #define __KERNEL_SYSCALLS__ #include +/* + * sysctl - toggle power-off restriction for serial console + * systems in machine_power_off() + */ +int scons_pwroff = 1; + #ifdef CONFIG_PCI static unsigned long power_reg = 0UL; @@ -40,7 +46,7 @@ void machine_power_off(void) { - if (!serial_console) { + if (!serial_console || scons_pwroff) { #ifdef CONFIG_PCI if (power_reg != 0UL) { /* Both register bits seem to have the diff -urN linux-2.4.26/arch/sparc64/kernel/process.c linux-2.4.27/arch/sparc64/kernel/process.c --- linux-2.4.26/arch/sparc64/kernel/process.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/sparc64/kernel/process.c 2004-08-07 16:26:04.616345216 -0700 @@ -453,7 +453,7 @@ page = pmd_alloc_one(NULL, 0); pgd_set(pgd0, page); } - pgd_cache = pgd_val(*pgd0) << 11UL; + pgd_cache = ((unsigned long) pgd_val(*pgd0)) << 11UL; } __asm__ __volatile__("stxa %0, [%1] %2\n\t" "membar #Sync" diff -urN linux-2.4.26/arch/sparc64/kernel/signal32.c linux-2.4.27/arch/sparc64/kernel/signal32.c --- linux-2.4.26/arch/sparc64/kernel/signal32.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/arch/sparc64/kernel/signal32.c 2004-08-07 16:26:04.618345298 -0700 @@ -393,7 +393,7 @@ { struct rt_signal_frame32 *sf; unsigned int psr; - unsigned pc, npc, fpu_save; + unsigned pc, npc, fpu_save, u_ss_sp; mm_segment_t old_fs; sigset_t set; sigset_t32 seta; @@ -444,7 +444,8 @@ if (fpu_save) err |= restore_fpu_state32(regs, &sf->fpu_state); err |= copy_from_user(&seta, &sf->mask, sizeof(sigset_t32)); - err |= __get_user((long)st.ss_sp, &sf->stack.ss_sp); + err |= __get_user(u_ss_sp, &sf->stack.ss_sp); + st.ss_sp = (void *) (long) u_ss_sp; err |= __get_user(st.ss_flags, &sf->stack.ss_flags); err |= __get_user(st.ss_size, &sf->stack.ss_size); if (err) @@ -1030,7 +1031,7 @@ struct thread_struct *tp = ¤t->thread; svr4_gregset_t *gr; mm_segment_t old_fs; - u32 pc, npc, psr; + u32 pc, npc, psr, u_ss_sp; sigset_t set; svr4_sigset_t setv; int i, err; @@ -1075,7 +1076,8 @@ if (_NSIG_WORDS >= 2) set.sig[1] = setv.sigbits[2] | (((long)setv.sigbits[3]) << 32); - err |= __get_user((long)st.ss_sp, &c->stack.sp); + err |= __get_user(u_ss_sp, &c->stack.sp); + st.ss_sp = (void *) (long) u_ss_sp; err |= __get_user(st.ss_flags, &c->stack.flags); err |= __get_user(st.ss_size, &c->stack.size); if (err) @@ -1545,9 +1547,9 @@ /* Now see if we want to update the new state. */ if (ssptr) { - void *ss_sp; + u32 ss_sp; - if (get_user((long)ss_sp, &ssptr->the_stack)) + if (get_user(ss_sp, &ssptr->the_stack)) goto out; /* If the current stack was set with sigaltstack, don't swap stacks while we are on it. */ @@ -1570,13 +1572,15 @@ asmlinkage int do_sys32_sigaltstack(u32 ussa, u32 uossa, unsigned long sp) { stack_t uss, uoss; + u32 u_ss_sp = 0; int ret; mm_segment_t old_fs; - if (ussa && (get_user((long)uss.ss_sp, &((stack_t32 *)(long)ussa)->ss_sp) || + if (ussa && (get_user(u_ss_sp, &((stack_t32 *)(long)ussa)->ss_sp) || __get_user(uss.ss_flags, &((stack_t32 *)(long)ussa)->ss_flags) || __get_user(uss.ss_size, &((stack_t32 *)(long)ussa)->ss_size))) return -EFAULT; + uss.ss_sp = (void *) (long) u_ss_sp; old_fs = get_fs(); set_fs(KERNEL_DS); ret = do_sigaltstack(ussa ? &uss : NULL, uossa ? &uoss : NULL, sp); diff -urN linux-2.4.26/arch/sparc64/kernel/sparc64_ksyms.c linux-2.4.27/arch/sparc64/kernel/sparc64_ksyms.c --- linux-2.4.26/arch/sparc64/kernel/sparc64_ksyms.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/sparc64/kernel/sparc64_ksyms.c 2004-08-07 16:26:04.618345298 -0700 @@ -89,6 +89,7 @@ extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); extern int unregister_ioctl32_conversion(unsigned int cmd); extern int io_remap_page_range(unsigned long from, unsigned long offset, unsigned long size, pgprot_t prot, int space); +extern void (*prom_palette)(int); extern int __ashrdi3(int, int); @@ -371,3 +372,5 @@ #endif EXPORT_SYMBOL(tick_ops); + +EXPORT_SYMBOL(prom_palette); diff -urN linux-2.4.26/arch/sparc64/kernel/sys_sparc32.c linux-2.4.27/arch/sparc64/kernel/sys_sparc32.c --- linux-2.4.26/arch/sparc64/kernel/sys_sparc32.c 2004-04-14 06:05:27.000000000 -0700 +++ linux-2.4.27/arch/sparc64/kernel/sys_sparc32.c 2004-08-07 16:26:04.621345421 -0700 @@ -1316,7 +1316,7 @@ put_user(reclen, &dirent->d_reclen); copy_to_user(dirent->d_name, name, namlen); put_user(0, dirent->d_name + namlen); - ((char *) dirent) += reclen; + dirent = (void *) dirent + reclen; buf->current_dir = dirent; buf->count -= reclen; return 0; @@ -3056,9 +3056,12 @@ if (act) { old_sigset_t32 mask; + u32 u_handler, u_restorer; - ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); - ret |= __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer); + ret = get_user(u_handler, &act->sa_handler); + new_ka.sa.sa_handler = (void *) (long) u_handler; + ret |= __get_user(u_restorer, &act->sa_restorer); + new_ka.sa.sa_restorer = (void *) (long) u_restorer; ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); ret |= __get_user(mask, &act->sa_mask); if (ret) @@ -3097,8 +3100,11 @@ current->thread.flags |= SPARC_FLAG_NEWSIGNALS; if (act) { + u32 u_handler, u_restorer; + new_ka.ka_restorer = restorer; - ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret = get_user(u_handler, &act->sa_handler); + new_ka.sa.sa_handler = (void *) (long) u_handler; ret |= __copy_from_user(&set32, &act->sa_mask, sizeof(sigset_t32)); switch (_NSIG_WORDS) { case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] | (((long)set32.sig[7]) << 32); @@ -3107,7 +3113,8 @@ case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] | (((long)set32.sig[1]) << 32); } ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); - ret |= __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer); + ret |= __get_user(u_restorer, &act->sa_restorer); + new_ka.sa.sa_restorer = (void *) (long) u_restorer; if (ret) return -EFAULT; } diff -urN linux-2.4.26/arch/sparc64/kernel/sys_sunos32.c linux-2.4.27/arch/sparc64/kernel/sys_sunos32.c --- linux-2.4.26/arch/sparc64/kernel/sys_sunos32.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/sparc64/kernel/sys_sunos32.c 2004-08-07 16:26:04.622345463 -0700 @@ -296,7 +296,7 @@ put_user(reclen, &dirent->d_reclen); copy_to_user(dirent->d_name, name, namlen); put_user(0, dirent->d_name + namlen); - ((char *) dirent) += reclen; + dirent = (void *) dirent + reclen; buf->curr = dirent; buf->count -= reclen; return 0; @@ -376,7 +376,7 @@ put_user(reclen, &dirent->d_reclen); copy_to_user(dirent->d_name, name, namlen); put_user(0, dirent->d_name + namlen); - ((char *) dirent) += reclen; + dirent = (void *) dirent + reclen; buf->curr = dirent; buf->count -= reclen; return 0; @@ -1309,10 +1309,12 @@ if (act) { old_sigset_t32 mask; + u32 u_handler; - if (get_user((long)new_ka.sa.sa_handler, &((struct old_sigaction32 *)A(act))->sa_handler) || + if (get_user(u_handler, &((struct old_sigaction32 *)A(act))->sa_handler) || __get_user(new_ka.sa.sa_flags, &((struct old_sigaction32 *)A(act))->sa_flags)) return -EFAULT; + new_ka.sa.sa_handler = (void *) (long) u_handler; __get_user(mask, &((struct old_sigaction32 *)A(act))->sa_mask); new_ka.sa.sa_restorer = NULL; new_ka.ka_restorer = NULL; diff -urN linux-2.4.26/arch/sparc64/kernel/unaligned.c linux-2.4.27/arch/sparc64/kernel/unaligned.c --- linux-2.4.26/arch/sparc64/kernel/unaligned.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/arch/sparc64/kernel/unaligned.c 2004-08-07 16:26:04.623345504 -0700 @@ -149,8 +149,8 @@ } } -static inline unsigned long compute_effective_address(struct pt_regs *regs, - unsigned int insn, unsigned int rd) +unsigned long compute_effective_address(struct pt_regs *regs, + unsigned int insn, unsigned int rd) { unsigned int rs1 = (insn >> 14) & 0x1f; unsigned int rs2 = insn & 0x1f; @@ -166,7 +166,7 @@ } /* This is just to make gcc think die_if_kernel does return... */ -static void unaligned_panic(char *str, struct pt_regs *regs) +static void __attribute_used__ unaligned_panic(char *str, struct pt_regs *regs) { die_if_kernel(str, regs); } diff -urN linux-2.4.26/arch/sparc64/mm/fault.c linux-2.4.27/arch/sparc64/mm/fault.c --- linux-2.4.26/arch/sparc64/mm/fault.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/arch/sparc64/mm/fault.c 2004-08-07 16:26:04.624345545 -0700 @@ -203,14 +203,21 @@ return insn; } -static void do_fault_siginfo(int code, int sig, unsigned long address) +extern unsigned long compute_effective_address(struct pt_regs *, unsigned int, unsigned int); + +static void do_fault_siginfo(int code, int sig, struct pt_regs *regs, + unsigned int insn, int fault_code) { siginfo_t info; info.si_code = code; info.si_signo = sig; info.si_errno = 0; - info.si_addr = (void *) address; + if (fault_code & FAULT_CODE_ITLB) + info.si_addr = (void *) regs->tpc; + else + info.si_addr = (void *) + compute_effective_address(regs, insn, 0); info.si_trapno = 0; force_sig_info(sig, &info, current); } @@ -291,7 +298,7 @@ /* The si_code was set to make clear whether * this was a SEGV_MAPERR or SEGV_ACCERR fault. */ - do_fault_siginfo(si_code, SIGSEGV, address); + do_fault_siginfo(si_code, SIGSEGV, regs, insn, fault_code); return; } @@ -467,7 +474,7 @@ * Send a sigbus, regardless of whether we were in kernel * or user mode. */ - do_fault_siginfo(BUS_ADRERR, SIGBUS, address); + do_fault_siginfo(BUS_ADRERR, SIGBUS, regs, insn, fault_code); /* Kernel mode? Handle exceptions or die */ if (regs->tstate & TSTATE_PRIV) diff -urN linux-2.4.26/arch/sparc64/mm/init.c linux-2.4.27/arch/sparc64/mm/init.c --- linux-2.4.26/arch/sparc64/mm/init.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/arch/sparc64/mm/init.c 2004-08-07 16:26:04.625345586 -0700 @@ -1433,8 +1433,10 @@ /* Now can init the kernel/bad page tables. */ pgd_set(&swapper_pg_dir[0], swapper_pmd_dir + (shift / sizeof(pgd_t))); - sparc64_vpte_patchme1[0] |= (pgd_val(init_mm.pgd[0]) >> 10); - sparc64_vpte_patchme2[0] |= (pgd_val(init_mm.pgd[0]) & 0x3ff); + sparc64_vpte_patchme1[0] |= + (((unsigned long)pgd_val(init_mm.pgd[0])) >> 10); + sparc64_vpte_patchme2[0] |= + (((unsigned long)pgd_val(init_mm.pgd[0])) & 0x3ff); flushi((long)&sparc64_vpte_patchme1[0]); /* Setup bootmem... */ diff -urN linux-2.4.26/arch/sparc64/prom/memory.c linux-2.4.27/arch/sparc64/prom/memory.c --- linux-2.4.26/arch/sparc64/prom/memory.c 1999-08-31 11:23:30.000000000 -0700 +++ linux-2.4.27/arch/sparc64/prom/memory.c 2004-08-07 16:26:04.626345627 -0700 @@ -114,7 +114,7 @@ prom_prom_taken[iter].num_bytes = prom_reg_memlist[iter].reg_size; prom_prom_taken[iter].theres_more = - &prom_phys_total[iter+1]; + &prom_prom_taken[iter+1]; } prom_prom_taken[iter-1].theres_more = 0x0; diff -urN linux-2.4.26/arch/x86_64/boot/bootsect.S linux-2.4.27/arch/x86_64/boot/bootsect.S --- linux-2.4.26/arch/x86_64/boot/bootsect.S 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/arch/x86_64/boot/bootsect.S 2004-08-07 16:26:04.626345627 -0700 @@ -234,9 +234,10 @@ die: jne die # %es must be at 64kB boundary xorw %bx, %bx # %bx is starting address within segment rp_read: -#ifdef __BIG_KERNEL__ # look in setup.S for bootsect_kludge +#ifdef __BIG_KERNEL__ + # look in setup.S for bootsect_kludge bootsect_kludge = 0x220 # 0x200 + 0x20 which is the size of the - lcall bootsect_kludge # bootsector + bootsect_kludge offset + lcall *bootsect_kludge # bootsector + bootsect_kludge offset #else movw %es, %ax subw $SYSSEG, %ax diff -urN linux-2.4.26/arch/x86_64/ia32/ia32entry.S linux-2.4.27/arch/x86_64/ia32/ia32entry.S --- linux-2.4.26/arch/x86_64/ia32/ia32entry.S 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/arch/x86_64/ia32/ia32entry.S 2004-08-07 16:26:04.627345668 -0700 @@ -71,7 +71,7 @@ movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace LOAD_ARGS ARGOFFSET /* reload args from stack in case ptrace changed it */ - addq $ARGOFFSET,%rsp + RESTORE_REST cmpl $(IA32_NR_syscalls),%eax jae 1f IA32_ARG_FIXUP @@ -81,7 +81,7 @@ 1: SAVE_REST movq %rsp,%rdi /* &pt_regs -> arg1 */ call syscall_trace - addq $ARGOFFSET,%rsp + RESTORE_REST jmp int_ret_from_sys_call ia32_badsys: diff -urN linux-2.4.26/arch/x86_64/kernel/Makefile linux-2.4.27/arch/x86_64/kernel/Makefile --- linux-2.4.26/arch/x86_64/kernel/Makefile 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/Makefile 2004-08-07 16:26:04.627345668 -0700 @@ -14,7 +14,8 @@ O_TARGET := kernel.o -export-objs := mtrr.o msr.o cpuid.o x8664_ksyms.o pci-gart.o setup.o swiotlb.o +export-objs := mtrr.o msr.o cpuid.o x8664_ksyms.o pci-gart.o setup.o \ + swiotlb.o bluesmoke.o obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_x86_64.o \ diff -urN linux-2.4.26/arch/x86_64/kernel/acpi.c linux-2.4.27/arch/x86_64/kernel/acpi.c --- linux-2.4.26/arch/x86_64/kernel/acpi.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/acpi.c 2004-08-07 16:26:04.628345709 -0700 @@ -56,7 +56,10 @@ /* -------------------------------------------------------------------------- Boot-time Configuration -------------------------------------------------------------------------- */ - +#ifdef CONFIG_ACPI_PCI +int acpi_noirq __initdata; /* skip ACPI IRQ initialization */ +int acpi_pci_disabled __initdata; /* skip ACPI PCI scan and IRQ initialization */ +#endif int acpi_ht __initdata = 1; /* enable HT */ enum acpi_irq_model_id acpi_irq_model; @@ -119,11 +122,40 @@ #endif } +#ifdef CONFIG_ACPI_MMCONFIG + +u32 pci_mmcfg_base_addr; + +static int __init +acpi_parse_mcfg(unsigned long phys_addr, + unsigned long size) +{ + struct acpi_table_mcfg *mcfg = NULL; + + if (!phys_addr || !size) + return -EINVAL; + + mcfg = (struct acpi_table_mcfg *) __acpi_map_table(phys_addr, size); + if (!mcfg) { + printk(KERN_WARNING PREFIX "Unable to map MCFG\n"); + return -ENODEV; + } + + if (mcfg->base_reserved) { + printk(KERN_ERR PREFIX "MMCONFIG not in low 4GB of memory\n"); + return -ENODEV; + } + + pci_mmcfg_base_addr = mcfg->base_address; + + return 0; +} +#endif /* CONFIG_ACPI_MMCONFIG */ + #ifdef CONFIG_X86_LOCAL_APIC static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; - static int __init acpi_parse_madt ( unsigned long phys_addr, @@ -330,7 +362,7 @@ #endif /*CONFIG_X86_IO_APIC && CONFIG_ACPI_INTERPRETER*/ - +#ifdef CONFIG_HPET_TIMER static int __init acpi_parse_hpet ( unsigned long phys_addr, @@ -351,6 +383,7 @@ return 0; } +#endif /* CONFIG_HPET_TIMER */ #ifdef CONFIG_ACPI_BUS /* @@ -457,17 +490,16 @@ return result; } -#ifdef CONFIG_X86_LOCAL_APIC +#ifdef CONFIG_ACPI_MMCONFIG + result = acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); + if (result < 0) { + printk(KERN_ERR PREFIX "Error %d parsing MCFG\n", result); + } else if (result > 1) { + printk(KERN_WARNING PREFIX "Multiple MCFG tables exist\n"); + } +#endif - /* this check should not need to be here -lenb */ - /* If "nolocalapic" is specified don't look further */ - extern int apic_disabled; - if (apic_disabled) { - printk(KERN_INFO PREFIX "Skipping Local/IO-APIC probe due to \"nolocalapic\"\n"); - return 0; - } - printk(KERN_INFO PREFIX "Parsing Local APIC info in MADT\n"); - +#ifdef CONFIG_X86_LOCAL_APIC /* * MADT @@ -573,9 +605,6 @@ return result; } - /* Build a default routing table for legacy (ISA) interrupts. */ - mp_config_acpi_legacy_irqs(); - /* Record sci_int for use when looking for MADT sci_int override */ acpi_table_parse(ACPI_FADT, acpi_parse_fadt); @@ -593,6 +622,9 @@ if (!acpi_sci_override_gsi) acpi_sci_ioapic_setup(acpi_fadt.sci_int, 0, 0); + /* Fill in identity legacy mapings where no override */ + mp_config_acpi_legacy_irqs(); + result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); if (result < 0) { printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); @@ -609,9 +641,11 @@ if (acpi_lapic && acpi_ioapic) smp_found_config = 1; +#ifdef CONFIG_HPET_TIMER result = acpi_table_parse(ACPI_HPET, acpi_parse_hpet); if (result < 0) printk("ACPI: no HPET table found (%d).\n", result); +#endif #endif /*CONFIG_X86_IO_APIC && CONFIG_ACPI_INTERPRETER*/ diff -urN linux-2.4.26/arch/x86_64/kernel/bluesmoke.c linux-2.4.27/arch/x86_64/kernel/bluesmoke.c --- linux-2.4.26/arch/x86_64/kernel/bluesmoke.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/arch/x86_64/kernel/bluesmoke.c 2004-08-07 16:26:04.630345791 -0700 @@ -1,19 +1,25 @@ /* * Machine check handler. * K8 parts Copyright 2002,2003 Andi Kleen, SuSE Labs. - * Rest from unknown author(s). + * Additional K8 decoding and simplification Copyright 2003 Eric Morton, Newisys Inc + * Rest from unknown author(s). */ +#include +#include #include #include #include #include #include #include +#include +#include +#include +#include #include #include #include -#include -#include +#include static int mce_disabled __initdata; static unsigned long mce_cpus; @@ -25,18 +31,27 @@ static int banks; static unsigned long ignored_banks, disabled_banks; +struct notifier_block *mc_notifier_list = NULL; +EXPORT_SYMBOL(mc_notifier_list); + static void generic_machine_check(struct pt_regs * regs, long error_code) { int recover=1; u32 alow, ahigh, high, low; u32 mcgstl, mcgsth; int i; - + struct notifier_mc_err mc_err; + rdmsr(MSR_IA32_MCG_STATUS, mcgstl, mcgsth); if(mcgstl&(1<<0)) /* Recoverable ? */ recover=0; - printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", smp_processor_id(), mcgsth, mcgstl); + /* Make sure unrecoverable MCEs reach the console */ + if(recover & 3) + oops_in_progress++; + + printk(KERN_EMERG "CPU %d: Machine Check Exception: %08x%08x\n", + smp_processor_id(), mcgsth, mcgstl); if (regs && (mcgstl & 2)) printk(KERN_EMERG "RIP <%02lx>:%016lx RSP %016lx\n", @@ -50,6 +65,10 @@ rdmsr(MSR_IA32_MC0_STATUS+i*4,low, high); if(high&(1<<31)) { + memset(&mc_err, 0x00, sizeof(mc_err)); + mc_err.cpunum = safe_smp_processor_id(); + mc_err.banknum = i; + mc_err.mci_status = ((u64)high << 32) | low; if(high&(1<<29)) recover|=1; if(high&(1<<25)) @@ -59,19 +78,26 @@ if(high&(1<<27)) { rdmsr(MSR_IA32_MC0_MISC+i*4, alow, ahigh); + mc_err.mci_misc = ((u64)ahigh << 32) | alow; printk("[%08x%08x]", alow, ahigh); } if(high&(1<<26)) { rdmsr(MSR_IA32_MC0_ADDR+i*4, alow, ahigh); - printk(" at %08x%08x", + mc_err.mci_addr = ((u64)ahigh << 32) | alow; + printk(" at %08x%08x", ahigh, alow); } + rdmsr(MSR_IA32_MC0_CTL+i*4, alow, ahigh); + mc_err.mci_ctl = ((u64)ahigh << 32) | alow; + printk("\n"); + /* Clear it */ wrmsr(MSR_IA32_MC0_STATUS+i*4, 0UL, 0UL); /* Serialize */ wmb(); + notifier_call_chain(&mc_notifier_list, X86_VENDOR_INTEL, &mc_err); } } @@ -105,56 +131,51 @@ * K8 machine check. */ -static struct pci_dev *find_k8_nb(void) -{ - struct pci_dev *dev; - int cpu = smp_processor_id(); - pci_for_each_dev(dev) { - if (dev->bus->number==0 && PCI_FUNC(dev->devfn)==3 && - PCI_SLOT(dev->devfn) == (24+cpu)) - return dev; - } - return NULL; -} - +static char *k8bank[] = { + "data cache", + "instruction cache", + "bus unit", + "load/store unit", + "northbridge" +}; static char *transaction[] = { "instruction", "data", "generic", "reserved" }; static char *cachelevel[] = { - "level 0", "level 1", "level 2", "level generic" + "0", "1", "2", "generic" }; static char *memtrans[] = { "generic error", "generic read", "generic write", "data read", - "data write", "instruction fetch", "prefetch", "snoop", + "data write", "instruction fetch", "prefetch", "evict", "snoop", "?", "?", "?", "?", "?", "?", "?" }; static char *partproc[] = { "local node origin", "local node response", - "local node observed", "generic" + "local node observed", "generic participation" }; static char *timeout[] = { "request didn't time out", "request timed out" }; static char *memoryio[] = { - "memory access", "res.", "i/o access", "generic" -}; -static char *extendederr[] = { - "ecc error", - "crc error", - "sync error", - "mst abort", - "tgt abort", - "gart error", - "rmw error", - "wdog error", - "chipkill ecc error", + "memory", "res.", "i/o", "generic" +}; +static char *nbextendederr[] = { + "ECC error", + "CRC error", + "Sync error", + "Master abort", + "Target abort", + "GART error", + "RMW error", + "Watchdog error", + "Chipkill ECC error", "<9>","<10>","<11>","<12>", "<13>","<14>","<15>" -}; +}; static char *highbits[32] = { - [31] = "previous error lost", - [30] = "error overflow", + [31] = "valid", + [30] = "error overflow (multiple errors)", [29] = "error uncorrected", [28] = "error enable", [27] = "misc error valid", @@ -169,128 +190,290 @@ [11] = "res11", [10] = "res10", [9] = "res9", - [8] = "dram scrub error", + [8] = "error found by scrub", [7] = "res7", /* 6-4 ht link number of error */ [3] = "res3", [2] = "res2", - [1] = "err cpu0", - [0] = "err cpu1", + [1] = "err cpu1", + [0] = "err cpu0", }; -static void check_k8_nb(int header) -{ - struct pci_dev *nb; - nb = find_k8_nb(); - if (nb == NULL) - return; - - u32 statuslow, statushigh; - pci_read_config_dword(nb, 0x48, &statuslow); - pci_read_config_dword(nb, 0x4c, &statushigh); - if (!(statushigh & (1<<31))) - return; - if (header) - printk(KERN_ERR "CPU %d: Silent Northbridge MCE\n", smp_processor_id()); - printk(KERN_ERR "Northbridge status %08x:%08x\n", - statushigh,statuslow); +static void decode_k8_generic_errcode(unsigned int cpunum, u64 status) +{ + unsigned short errcode = status & 0xffff; + int i; - printk(KERN_ERR " Error %s\n", extendederr[(statuslow >> 16) & 0xf]); + for (i=0; i<32; i++) { + if (i==31 || i==28 || i==26) + continue; + if (highbits[i] && (status & (1UL<<(i+32)))) { + printk(KERN_ERR "CPU%d: bit%d = %s\n", cpunum, i+32, highbits[i]); + } + } - unsigned short errcode = statuslow & 0xffff; - switch ((statuslow >> 16) & 0xF) { - case 5: - printk(KERN_ERR " GART TLB error %s %s\n", - transaction[(errcode >> 2) & 3], + if ((errcode & 0xFFF0) == 0x0010) { + printk(KERN_ERR "CPU%d: TLB error '%s transaction, level %s'\n", + cpunum, + transaction[(errcode >> 2) & 3], cachelevel[errcode & 3]); - break; - case 8: - printk(KERN_ERR " ECC error syndrome %x\n", - (((statuslow >> 24) & 0xff) << 8) | ((statushigh >> 15) & 0x7f)); - /*FALL THROUGH*/ - default: - printk(KERN_ERR " bus error %s, %s\n %s\n %s, %s\n", + } + else if ((errcode & 0xFF00) == 0x0100) { + printk(KERN_ERR "CPU%d: memory/cache error '%s mem transaction, %s transaction, level %s'\n", + cpunum, + memtrans[(errcode >> 4) & 0xf], + transaction[(errcode >> 2) & 3], + cachelevel[errcode & 3]); + } + else if ((errcode & 0xF800) == 0x0800) { + printk(KERN_ERR "CPU%d: bus error '%s, %s\n %s mem transaction\n %s access, level %s'\n", + cpunum, partproc[(errcode >> 9) & 0x3], timeout[(errcode >> 8) & 1], - memtrans[(errcode >> 4) & 0xf], - memoryio[(errcode >> 2) & 0x3], - cachelevel[(errcode & 0x3)]); - /* should only print when it was a HyperTransport related error. */ - printk(KERN_ERR " link number %x\n", (statushigh >> 4) & 3); + memtrans[(errcode >> 4) & 0xf], + memoryio[(errcode >> 2) & 0x3], + cachelevel[(errcode & 0x3)]); + } +} + +static void decode_k8_dc_mc(unsigned int cpunum, u64 status) +{ + unsigned short exterrcode = (status >> 16) & 0x0f; + unsigned short errcode = status & 0xffff; + + if(status&(3UL<<45)) { + printk(KERN_ERR "CPU%d: Data cache ECC error (syndrome %x)", + cpunum, + (u32) (status >> 47) & 0xff); + if(status&(1UL<<40)) { + printk(" found by scrubber"); + } + printk("\n"); + } + + if ((errcode & 0xFFF0) == 0x0010) { + printk(KERN_ERR "CPU%d: TLB parity error in %s array\n", + cpunum, + (exterrcode == 0) ? "physical" : "virtual"); + } + + decode_k8_generic_errcode(cpunum, status); +} + +static void decode_k8_ic_mc(unsigned int cpunum, u64 status) +{ + unsigned short exterrcode = (status >> 16) & 0x0f; + unsigned short errcode = status & 0xffff; + + if(status&(3UL<<45)) { + printk(KERN_ERR "CPU%d: Instruction cache ECC error\n", + cpunum); + } + + if ((errcode & 0xFFF0) == 0x0010) { + printk(KERN_ERR "CPU%d: TLB parity error in %s array\n", + cpunum, + (exterrcode == 0) ? "physical" : "virtual"); + } + + decode_k8_generic_errcode(cpunum, status); +} + +static void decode_k8_bu_mc(unsigned int cpunum, u64 status) +{ + unsigned short exterrcode = (status >> 16) & 0x0f; + + if(status&(3UL<<45)) { + printk(KERN_ERR "CPU%d: L2 cache ECC error\n", + cpunum); + } + + printk(KERN_ERR "CPU%d: %s array error\n", + cpunum, + (exterrcode == 0) ? "Bus or cache" : "Cache tag"); + + decode_k8_generic_errcode(cpunum, status); +} + +static void decode_k8_ls_mc(unsigned int cpunum, u64 status) +{ + decode_k8_generic_errcode(cpunum, status); +} + +static void decode_k8_nb_mc(unsigned int cpunum, u64 status) +{ + unsigned short exterrcode = (status >> 16) & 0x0f; + + printk(KERN_ERR "CPU%d: Northbridge %s\n", cpunum, nbextendederr[exterrcode]); + + switch (exterrcode) { + case 0: + printk(KERN_ERR "CPU%d: ECC syndrome = %x\n", + cpunum, + (u32) (status >> 47) & 0xff); break; - } - + case 8: + printk(KERN_ERR "CPU%d: Chipkill ECC syndrome = %x\n", + cpunum, + (u32) ((((status >> 24) & 0xff) << 8) | ((status >> 47) & 0xff))); + break; + case 1: + case 2: + case 3: + case 4: + case 6: + printk(KERN_ERR "CPU%d: link number = %x\n", + cpunum, + (u32) (status >> 36) & 0x7); + break; + } - int i; - for (i = 0; i < 32; i++) { - if (i == 26 || i == 28) - continue; - if (highbits[i] && (statushigh & (1<:%016lx RSP %016lx %s\n", + cpunum, regs->cs, regs->rip, regs->rsp, + (mcgst & 1) ? "" : "!INEXACT!"); + } - rdmsrl(MSR_IA32_MC0_STATUS+4*4, nbstatus); - if ((nbstatus & (1UL<<63)) == 0) - goto others; - - printk(KERN_EMERG "Northbridge Machine Check %s %016lx %lx\n", - regs ? "exception" : "timer", - (unsigned long)nbstatus, error_code); - if (nbstatus & (1UL<<62)) - printk(KERN_EMERG "Lost at least one NB error condition\n"); - if (nbstatus & (1UL<<61)) - printk(KERN_EMERG "Uncorrectable condition\n"); - if (nbstatus & (1UL<57)) - printk(KERN_EMERG "Unrecoverable condition\n"); - - check_k8_nb(0); + for(banknum=0; banknum>16)&0x0f)==7)) { + /* NB watchdog, address reg has details but validity bit is not set */ + rdmsrl(MSR_IA32_MC0_ADDR+banknum*4, addr); + mc_err.mci_addr = addr; + printk(" error details %016Lx", addr); + } + printk("\n"); + rdmsrl(MSR_IA32_MC0_CTL+banknum*4, ctl); + mc_err.mci_ctl = ctl; + /* Clear it */ + /* Can't write anything but zeros to status, or K8 will GPF */ + wrmsrl(MSR_IA32_MC0_STATUS+banknum*4, 0UL); + /* Serialize */ + wmb(); + notifier_call_chain(&mc_notifier_list, X86_VENDOR_AMD, &mc_err); + } + } - if (nbstatus & (1UL<<58)) { - u64 adr; - rdmsrl(MSR_IA32_MC0_ADDR+4*4, adr); - printk(KERN_EMERG "Address: %016lx\n", (unsigned long)adr); + if(norecover&2) { + panic("CPU context corrupt"); } - - wrmsrl(MSR_IA32_MC0_STATUS+4*4, 0); - wrmsrl(MSR_IA32_MCG_STATUS, 0); - - others: - generic_machine_check(regs, error_code); -} + if(norecover&1) { + panic("Unable to continue"); + } + printk(KERN_EMERG "Attempting to continue.\n"); + mcgst&=~(1UL<<2); + wrmsrl(MSR_IA32_MCG_STATUS,mcgst); +} static struct timer_list mcheck_timer; -int mcheck_interval = 30*HZ; +int mcheck_interval = 60*HZ; #ifndef CONFIG_SMP static void mcheck_timer_handler(unsigned long data) { - k8_machine_check(NULL,0); + k8_poll_machine_check(); mcheck_timer.expires = jiffies + mcheck_interval; add_timer(&mcheck_timer); } @@ -301,13 +484,13 @@ static void mcheck_timer_other(void *data) { - k8_machine_check(NULL, 0); + k8_poll_machine_check(); } static void mcheck_timer_dist(void *data) { smp_call_function(mcheck_timer_other,0,0,0); - k8_machine_check(NULL, 0); + k8_poll_machine_check(); mcheck_timer.expires = jiffies + mcheck_interval; add_timer(&mcheck_timer); } @@ -334,17 +517,20 @@ rdmsrl(MSR_IA32_MCG_CAP, cap); banks = cap&0xff; - machine_check_vector = k8_machine_check; for (i = 0; i < banks; i++) { u64 val = ((1UL<x86 == 15 && !nok8) { k8_mcheck_init(c); - break; + } else { + generic_mcheck_init(c); } + break; /* FALL THROUGH */ default: case X86_VENDOR_INTEL: @@ -447,13 +636,13 @@ char *p; while ((p = strsep(&str,",")) != NULL) { if (isdigit(*p)) - mcheck_interval = simple_strtol(p,NULL,0) * HZ; + mcheck_interval = simple_strtol(p,NULL,0) * HZ; else if (!strcmp(p,"off")) mce_disabled = 1; else if (!strncmp(p,"enable",6)) - disabled_banks &= ~(1< #include +#include #include #include #include @@ -439,6 +440,7 @@ #endif #ifndef CONFIG_VISWS - setup_irq(2, &irq2); + if (!acpi_ioapic) + setup_irq(2, &irq2); #endif } diff -urN linux-2.4.26/arch/x86_64/kernel/io_apic.c linux-2.4.27/arch/x86_64/kernel/io_apic.c --- linux-2.4.26/arch/x86_64/kernel/io_apic.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/io_apic.c 2004-08-07 16:26:04.633345915 -0700 @@ -256,8 +256,9 @@ PCI_VENDOR_ID); vendor &= 0xffff; switch (vendor) { - case PCI_VENDOR_ID_NVIDIA: case PCI_VENDOR_ID_VIA: + return; + case PCI_VENDOR_ID_NVIDIA: printk(KERN_INFO "PCI bridge %02x:%02x from %x found. Setting \"noapic\". Overwrite with \"apic\"\n", num,slot,vendor); @@ -1684,18 +1685,10 @@ /* * - * IRQ's that are handled by the old PIC in all cases: + * IRQ's that are handled by the PIC in the MPS IOAPIC case. * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. * Linux doesn't really care, as it's not actually used * for any interrupt handling anyway. - * - There used to be IRQ13 here as well, but all - * MPS-compliant must not use it for FPU coupling and we - * want to use exception 16 anyway. And there are - * systems who connect it to an I/O APIC for other uses. - * Thus we don't mark it special any longer. - * - * Additionally, something is definitely wrong with irq9 - * on PIIX4 boards. */ #define PIC_IRQS (1<<2) @@ -1703,7 +1696,11 @@ { enable_IO_APIC(); - io_apic_irqs = ~PIC_IRQS; + if (acpi_ioapic) + io_apic_irqs = ~0; /* all IRQs go through IOAPIC */ + else + io_apic_irqs = ~PIC_IRQS; + printk("ENABLING IO-APIC IRQs\n"); /* @@ -1858,7 +1855,7 @@ entry.vector = assign_irq_vector(irq); - printk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " + Dprintk(KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " "IRQ %d Mode:%i Active:%i)\n", ioapic, mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, edge_level, active_high_low); diff -urN linux-2.4.26/arch/x86_64/kernel/mpparse.c linux-2.4.27/arch/x86_64/kernel/mpparse.c --- linux-2.4.26/arch/x86_64/kernel/mpparse.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/mpparse.c 2004-08-07 16:26:04.634345956 -0700 @@ -793,8 +793,6 @@ u32 global_irq) { struct mpc_config_intsrc intsrc; - int i = 0; - int found = 0; int ioapic = -1; int pin = -1; @@ -827,23 +825,9 @@ (intsrc.mpc_irqflag >> 2) & 3, intsrc.mpc_srcbus, intsrc.mpc_srcbusirq, intsrc.mpc_dstapic, intsrc.mpc_dstirq); - /* - * If an existing [IOAPIC.PIN -> IRQ] routing entry exists we override it. - * Otherwise create a new entry (e.g. global_irq == 2). - */ - for (i = 0; i < mp_irq_entries; i++) { - if ((mp_irqs[i].mpc_srcbus == intsrc.mpc_srcbus) - && (mp_irqs[i].mpc_dstirq == intsrc.mpc_dstirq)) { - mp_irqs[i] = intsrc; - found = 1; - break; - } - } - if (!found) { - mp_irqs[mp_irq_entries] = intsrc; - if (++mp_irq_entries == MAX_IRQ_SOURCES) - panic("Max # of irq sources exceeded!\n"); - } + mp_irqs[mp_irq_entries] = intsrc; + if (++mp_irq_entries == MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!\n"); return; } @@ -874,13 +858,23 @@ intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; /* - * Use the default configuration for the IRQs 0-15. These may be + * Use the default configuration for the IRQs 0-15. Unless * overriden by (MADT) interrupt source override entries. */ for (i = 0; i < 16; i++) { + int idx; - if (i == 2) - continue; /* Don't connect IRQ2 */ + for (idx = 0; idx < mp_irq_entries; idx++) + if (mp_irqs[idx].mpc_srcbus == MP_ISA_BUS && + (mp_irqs[idx].mpc_dstapic == intsrc.mpc_dstapic) && + (mp_irqs[idx].mpc_srcbusirq == i || + mp_irqs[idx].mpc_dstirq == i)) + break; + + if (idx != mp_irq_entries) { + printk(KERN_DEBUG "ACPI: IRQ%d used by override.\n", i); + continue; /* IRQ already used */ + } intsrc.mpc_irqtype = mp_INT; intsrc.mpc_srcbusirq = i; /* Identity mapped */ @@ -942,8 +936,6 @@ irq = entry->link.index; } - irq = entry->link.index; - /* Don't set up the ACPI SCI because it's already set up */ if (acpi_fadt.sci_int == irq) { entry->irq = irq; /*we still need to set entry's irq*/ @@ -982,10 +974,11 @@ entry->irq = irq; printk(KERN_DEBUG "%02x:%02x:%02x[%c] -> %d-%d -> vector 0x%02x" - " -> IRQ %d\n", entry->id.segment, entry->id.bus, - entry->id.device, ('A' + entry->pin), - mp_ioapic_routing[ioapic].apic_id, ioapic_pin, vector, - entry->irq); + " -> IRQ %d %s %s\n", entry->id.segment, entry->id.bus, + entry->id.device, ('A' + entry->pin), + mp_ioapic_routing[ioapic].apic_id, ioapic_pin, vector, + entry->irq, edge_level ? "level" : "edge", + active_high_low ? "low" : "high"); } print_IO_APIC(); diff -urN linux-2.4.26/arch/x86_64/kernel/mtrr.c linux-2.4.27/arch/x86_64/kernel/mtrr.c --- linux-2.4.26/arch/x86_64/kernel/mtrr.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/mtrr.c 2004-08-07 16:26:04.635345997 -0700 @@ -961,16 +961,19 @@ static ssize_t mtrr_read (struct file *file, char *buf, size_t len, loff_t * ppos) { - if (*ppos >= ascii_buf_bytes) + loff_t n = *ppos; + unsigned pos = n; + + if (pos != n || pos >= ascii_buf_bytes) return 0; - if (*ppos + len > ascii_buf_bytes) - len = ascii_buf_bytes - *ppos; + if (len > ascii_buf_bytes - pos) + len = ascii_buf_bytes - pos; - if (copy_to_user (buf, ascii_buffer + *ppos, len)) + if (copy_to_user (buf, ascii_buffer + pos, len)) return -EFAULT; - *ppos += len; + *ppos = pos + len; return len; } diff -urN linux-2.4.26/arch/x86_64/kernel/pci-gart.c linux-2.4.27/arch/x86_64/kernel/pci-gart.c --- linux-2.4.26/arch/x86_64/kernel/pci-gart.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/pci-gart.c 2004-08-07 16:26:04.635345997 -0700 @@ -503,12 +503,13 @@ no_agp = no_agp || (agp_init() < 0) || (agp_copy_info(&info) < 0); #endif +#ifdef CONFIG_SWIOTLB if (swiotlb) { no_iommu = 1; printk(KERN_INFO "PCI-DMA: Using SWIOTLB\n"); return; - } - + } +#endif if (no_iommu || (!force_mmu && end_pfn < 0xffffffff>>PAGE_SHIFT) || !iommu_aperture) { printk(KERN_INFO "PCI-DMA: Disabling IOMMU.\n"); diff -urN linux-2.4.26/arch/x86_64/kernel/pci-pc.c linux-2.4.27/arch/x86_64/kernel/pci-pc.c --- linux-2.4.26/arch/x86_64/kernel/pci-pc.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/pci-pc.c 2004-08-07 16:26:04.636346038 -0700 @@ -646,7 +646,7 @@ pcibios_last_bus = simple_strtol(str+8, NULL, 0); return NULL; } else if (!strncmp(str, "noacpi", 6)) { - acpi_noirq_set(); + acpi_disable_pci(); return NULL; } return str; diff -urN linux-2.4.26/arch/x86_64/kernel/setup.c linux-2.4.27/arch/x86_64/kernel/setup.c --- linux-2.4.26/arch/x86_64/kernel/setup.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/arch/x86_64/kernel/setup.c 2004-08-07 16:26:04.637346079 -0700 @@ -48,11 +48,8 @@ #include #include -int acpi_disabled = 0; -#ifdef CONFIG_ACPI_BOOT -int acpi_noirq __initdata = 0; /* skip ACPI IRQ initialization */ -#endif - +int acpi_disabled; +EXPORT_SYMBOL(acpi_disabled); int swiotlb; diff -urN linux-2.4.26/crypto/Config.in linux-2.4.27/crypto/Config.in --- linux-2.4.26/crypto/Config.in 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/crypto/Config.in 2004-08-07 16:26:04.637346079 -0700 @@ -72,6 +72,7 @@ tristate ' AES cipher algorithms' CONFIG_CRYPTO_AES tristate ' CAST5 (CAST-128) cipher algorithm' CONFIG_CRYPTO_CAST5 tristate ' CAST6 (CAST-256) cipher algorithm' CONFIG_CRYPTO_CAST6 + tristate ' TEA and XTEA cipher algorithms' CONFIG_CRYPTO_TEA tristate ' ARC4 cipher algorithm' CONFIG_CRYPTO_ARC4 if [ "$CONFIG_INET_IPCOMP" = "y" -o \ "$CONFIG_INET_IPCOMP" = "m" -o \ @@ -81,6 +82,7 @@ else tristate ' Deflate compression algorithm' CONFIG_CRYPTO_DEFLATE fi + tristate ' Michael MIC keyed digest algorithm' CONFIG_CRYPTO_MICHAEL_MIC tristate ' Testing module' CONFIG_CRYPTO_TEST fi diff -urN linux-2.4.26/crypto/Makefile linux-2.4.27/crypto/Makefile --- linux-2.4.26/crypto/Makefile 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/crypto/Makefile 2004-08-07 16:26:04.638346120 -0700 @@ -27,7 +27,9 @@ obj-$(CONFIG_CRYPTO_CAST5) += cast5.o obj-$(CONFIG_CRYPTO_CAST6) += cast6.o obj-$(CONFIG_CRYPTO_ARC4) += arc4.o +obj-$(CONFIG_CRYPTO_TEA) += tea.o obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o +obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o diff -urN linux-2.4.26/crypto/cipher.c linux-2.4.27/crypto/cipher.c --- linux-2.4.26/crypto/cipher.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/crypto/cipher.c 2004-08-07 16:26:04.638346120 -0700 @@ -52,8 +52,8 @@ { struct scatter_walk walk_in, walk_out; const unsigned int bsize = crypto_tfm_alg_blocksize(tfm); - u8 tmp_src[nbytes > src->length ? bsize : 0]; - u8 tmp_dst[nbytes > dst->length ? bsize : 0]; + u8 tmp_src[bsize]; + u8 tmp_dst[bsize]; if (!nbytes) return 0; @@ -68,19 +68,20 @@ for(;;) { u8 *src_p, *dst_p; + int in_place; scatterwalk_map(&walk_in, 0); scatterwalk_map(&walk_out, 1); src_p = scatterwalk_whichbuf(&walk_in, bsize, tmp_src); dst_p = scatterwalk_whichbuf(&walk_out, bsize, tmp_dst); + in_place = scatterwalk_samebuf(&walk_in, &walk_out, + src_p, dst_p); nbytes -= bsize; scatterwalk_copychunks(src_p, &walk_in, bsize, 0); - prfn(tfm, dst_p, src_p, crfn, enc, info, - scatterwalk_samebuf(&walk_in, &walk_out, - src_p, dst_p)); + prfn(tfm, dst_p, src_p, crfn, enc, info, in_place); scatterwalk_done(&walk_in, 0, nbytes); diff -urN linux-2.4.26/crypto/digest.c linux-2.4.27/crypto/digest.c --- linux-2.4.26/crypto/digest.c 2003-08-25 04:44:40.000000000 -0700 +++ linux-2.4.27/crypto/digest.c 2004-08-07 16:26:04.639346161 -0700 @@ -27,13 +27,28 @@ struct scatterlist *sg, unsigned int nsg) { unsigned int i; - + for (i = 0; i < nsg; i++) { - char *p = crypto_kmap(sg[i].page, 0) + sg[i].offset; - tfm->__crt_alg->cra_digest.dia_update(crypto_tfm_ctx(tfm), - p, sg[i].length); - crypto_kunmap(p, 0); - crypto_yield(tfm); + + struct page *pg = sg[i].page; + unsigned int offset = sg[i].offset; + unsigned int l = sg[i].length; + + do { + unsigned int bytes_from_page = min(l, ((unsigned int) + (PAGE_SIZE)) - + offset); + char *p = crypto_kmap(pg, 0) + offset; + + tfm->__crt_alg->cra_digest.dia_update + (crypto_tfm_ctx(tfm), p, + bytes_from_page); + crypto_kunmap(p, 0); + crypto_yield(tfm); + offset = 0; + pg++; + l -= bytes_from_page; + } while (l > 0); } } @@ -42,6 +57,15 @@ tfm->__crt_alg->cra_digest.dia_final(crypto_tfm_ctx(tfm), out); } +static int setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen) +{ + u32 flags; + if (tfm->__crt_alg->cra_digest.dia_setkey == NULL) + return -ENOSYS; + return tfm->__crt_alg->cra_digest.dia_setkey(crypto_tfm_ctx(tfm), + key, keylen, &flags); +} + static void digest(struct crypto_tfm *tfm, struct scatterlist *sg, unsigned int nsg, u8 *out) { @@ -72,6 +96,7 @@ ops->dit_update = update; ops->dit_final = final; ops->dit_digest = digest; + ops->dit_setkey = setkey; return crypto_alloc_hmac_block(tfm); } diff -urN linux-2.4.26/crypto/michael_mic.c linux-2.4.27/crypto/michael_mic.c --- linux-2.4.26/crypto/michael_mic.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/crypto/michael_mic.c 2004-08-07 16:26:04.640346202 -0700 @@ -0,0 +1,193 @@ +/* + * Cryptographic API + * + * Michael MIC (IEEE 802.11i/TKIP) keyed digest + * + * Copyright (c) 2004 Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + + +struct michael_mic_ctx { + u8 pending[4]; + size_t pending_len; + + u32 l, r; +}; + + +static inline u32 rotl(u32 val, int bits) +{ + return (val << bits) | (val >> (32 - bits)); +} + + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + + +static inline u32 xswap(u32 val) +{ + return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8); +} + + +#define michael_block(l, r) \ +do { \ + r ^= rotl(l, 17); \ + l += r; \ + r ^= xswap(l); \ + l += r; \ + r ^= rotl(l, 3); \ + l += r; \ + r ^= rotr(l, 2); \ + l += r; \ +} while (0) + + +static inline u32 get_le32(const u8 *p) +{ + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + + +static inline void put_le32(u8 *p, u32 v) +{ + p[0] = v; + p[1] = v >> 8; + p[2] = v >> 16; + p[3] = v >> 24; +} + + +static void michael_init(void *ctx) +{ + struct michael_mic_ctx *mctx = ctx; + mctx->pending_len = 0; +} + + +static void michael_update(void *ctx, const u8 *data, unsigned int len) +{ + struct michael_mic_ctx *mctx = ctx; + + if (mctx->pending_len) { + int flen = 4 - mctx->pending_len; + if (flen > len) + flen = len; + memcpy(&mctx->pending[mctx->pending_len], data, flen); + mctx->pending_len += flen; + data += flen; + len -= flen; + + if (mctx->pending_len < 4) + return; + + mctx->l ^= get_le32(mctx->pending); + michael_block(mctx->l, mctx->r); + mctx->pending_len = 0; + } + + while (len >= 4) { + mctx->l ^= get_le32(data); + michael_block(mctx->l, mctx->r); + data += 4; + len -= 4; + } + + if (len > 0) { + mctx->pending_len = len; + memcpy(mctx->pending, data, len); + } +} + + +static void michael_final(void *ctx, u8 *out) +{ + struct michael_mic_ctx *mctx = ctx; + u8 *data = mctx->pending; + + /* Last block and padding (0x5a, 4..7 x 0) */ + switch (mctx->pending_len) { + case 0: + mctx->l ^= 0x5a; + break; + case 1: + mctx->l ^= data[0] | 0x5a00; + break; + case 2: + mctx->l ^= data[0] | (data[1] << 8) | 0x5a0000; + break; + case 3: + mctx->l ^= data[0] | (data[1] << 8) | (data[2] << 16) | + 0x5a000000; + break; + } + michael_block(mctx->l, mctx->r); + /* l ^= 0; */ + michael_block(mctx->l, mctx->r); + + put_le32(out, mctx->l); + put_le32(out + 4, mctx->r); +} + + +static int michael_setkey(void *ctx, const u8 *key, unsigned int keylen, + u32 *flags) +{ + struct michael_mic_ctx *mctx = ctx; + if (keylen != 8) { + if (flags) + *flags = CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; + } + mctx->l = get_le32(key); + mctx->r = get_le32(key + 4); + return 0; +} + + +static struct crypto_alg michael_mic_alg = { + .cra_name = "michael_mic", + .cra_flags = CRYPTO_ALG_TYPE_DIGEST, + .cra_blocksize = 8, + .cra_ctxsize = sizeof(struct michael_mic_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(michael_mic_alg.cra_list), + .cra_u = { .digest = { + .dia_digestsize = 8, + .dia_init = michael_init, + .dia_update = michael_update, + .dia_final = michael_final, + .dia_setkey = michael_setkey } } +}; + + +static int __init michael_mic_init(void) +{ + return crypto_register_alg(&michael_mic_alg); +} + + +static void __exit michael_mic_exit(void) +{ + crypto_unregister_alg(&michael_mic_alg); +} + + +module_init(michael_mic_init); +module_exit(michael_mic_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Michael MIC"); +MODULE_AUTHOR("Jouni Malinen "); diff -urN linux-2.4.26/crypto/scatterwalk.h linux-2.4.27/crypto/scatterwalk.h --- linux-2.4.26/crypto/scatterwalk.h 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/crypto/scatterwalk.h 2004-08-07 16:26:04.640346202 -0700 @@ -38,6 +38,7 @@ void *src_p, void *dst_p) { return walk_in->page == walk_out->page && + walk_in->offset == walk_out->offset && walk_in->data == src_p && walk_out->data == dst_p; } diff -urN linux-2.4.26/crypto/tcrypt.c linux-2.4.27/crypto/tcrypt.c --- linux-2.4.26/crypto/tcrypt.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/crypto/tcrypt.c 2004-08-07 16:26:04.641346243 -0700 @@ -63,7 +63,7 @@ static char *check[] = { "des", "md5", "des3_ede", "rot13", "sha1", "sha256", "blowfish", "twofish", "serpent", "sha384", "sha512", "md4", "aes", "cast6", - "arc4", "deflate", NULL + "arc4", "michael_mic", "deflate", "tea", "xtea", NULL }; static void @@ -114,6 +114,10 @@ sg[0].length = hash_tv[i].psize; crypto_digest_init (tfm); + if (tfm->crt_u.digest.dit_setkey) { + crypto_digest_setkey (tfm, hash_tv[i].key, + hash_tv[i].ksize); + } crypto_digest_update (tfm, sg, 1); crypto_digest_final (tfm, result); @@ -562,6 +566,15 @@ test_cipher ("arc4", MODE_ECB, ENCRYPT, arc4_enc_tv_template, ARC4_ENC_TEST_VECTORS); test_cipher ("arc4", MODE_ECB, DECRYPT, arc4_dec_tv_template, ARC4_DEC_TEST_VECTORS); + //TEA + test_cipher ("tea", MODE_ECB, ENCRYPT, tea_enc_tv_template, TEA_ENC_TEST_VECTORS); + test_cipher ("tea", MODE_ECB, DECRYPT, tea_dec_tv_template, TEA_DEC_TEST_VECTORS); + + + //XTEA + test_cipher ("xtea", MODE_ECB, ENCRYPT, xtea_enc_tv_template, XTEA_ENC_TEST_VECTORS); + test_cipher ("xtea", MODE_ECB, DECRYPT, xtea_dec_tv_template, XTEA_DEC_TEST_VECTORS); + test_hash("sha384", sha384_tv_template, SHA384_TEST_VECTORS); test_hash("sha512", sha512_tv_template, SHA512_TEST_VECTORS); test_deflate(); @@ -570,6 +583,8 @@ test_hmac("sha1", hmac_sha1_tv_template, HMAC_SHA1_TEST_VECTORS); test_hmac("sha256", hmac_sha256_tv_template, HMAC_SHA256_TEST_VECTORS); #endif + + test_hash("michael_mic", michael_mic_tv_template, MICHAEL_MIC_TEST_VECTORS); break; case 1: @@ -649,6 +664,20 @@ test_cipher ("arc4", MODE_ECB, DECRYPT, arc4_dec_tv_template, ARC4_DEC_TEST_VECTORS); break; + case 17: + test_hash("michael_mic", michael_mic_tv_template, MICHAEL_MIC_TEST_VECTORS); + break; + + case 19: + test_cipher ("tea", MODE_ECB, ENCRYPT, tea_enc_tv_template, TEA_ENC_TEST_VECTORS); + test_cipher ("tea", MODE_ECB, DECRYPT, tea_dec_tv_template, TEA_DEC_TEST_VECTORS); + break; + + case 20: + test_cipher ("xtea", MODE_ECB, ENCRYPT, xtea_enc_tv_template, XTEA_ENC_TEST_VECTORS); + test_cipher ("xtea", MODE_ECB, DECRYPT, xtea_dec_tv_template, XTEA_DEC_TEST_VECTORS); + break; + #ifdef CONFIG_CRYPTO_HMAC case 100: test_hmac("md5", hmac_md5_tv_template, HMAC_MD5_TEST_VECTORS); diff -urN linux-2.4.26/crypto/tcrypt.h linux-2.4.27/crypto/tcrypt.h --- linux-2.4.26/crypto/tcrypt.h 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/crypto/tcrypt.h 2004-08-07 16:26:04.643346325 -0700 @@ -30,6 +30,8 @@ char digest[MAX_DIGEST_SIZE]; unsigned char np; unsigned char tap[MAX_TAP]; + char key[128]; /* only used with keyed hash algorithms */ + unsigned char ksize; }; struct hmac_testvec { @@ -1628,6 +1630,195 @@ }, }; +/* + * TEA test vectors + */ +#define TEA_ENC_TEST_VECTORS 4 +#define TEA_DEC_TEST_VECTORS 4 + +struct cipher_testvec tea_enc_tv_template[] = +{ + { + .key = { [0 ... 15] = 0x00 }, + .klen = 16, + .input = { [0 ... 8] = 0x00 }, + .ilen = 8, + .result = { 0x0a, 0x3a, 0xea, 0x41, 0x40, 0xa9, 0xba, 0x94 }, + .rlen = 8, + }, { + .key = { 0x2b, 0x02, 0x05, 0x68, 0x06, 0x14, 0x49, 0x76, + 0x77, 0x5d, 0x0e, 0x26, 0x6c, 0x28, 0x78, 0x43 }, + .klen = 16, + .input = { 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x2e }, + .ilen = 8, + .result = { 0x77, 0x5d, 0x2a, 0x6a, 0xf6, 0xce, 0x92, 0x09 }, + .rlen = 8, + }, { + .key = { 0x09, 0x65, 0x43, 0x11, 0x66, 0x44, 0x39, 0x25, + 0x51, 0x3a, 0x16, 0x10, 0x0a, 0x08, 0x12, 0x6e }, + .klen = 16, + .input = { 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x5f, 0x74, + 0x65, 0x73, 0x74, 0x5f, 0x76, 0x65, 0x63, 0x74 }, + .ilen = 16, + .result = { 0xbe, 0x7a, 0xbb, 0x81, 0x95, 0x2d, 0x1f, 0x1e, + 0xdd, 0x89, 0xa1, 0x25, 0x04, 0x21, 0xdf, 0x95 }, + .rlen = 16, + }, { + .key = { 0x4d, 0x76, 0x32, 0x17, 0x05, 0x3f, 0x75, 0x2c, + 0x5d, 0x04, 0x16, 0x36, 0x15, 0x72, 0x63, 0x2f }, + .klen = 16, + .input = { 0x54, 0x65, 0x61, 0x20, 0x69, 0x73, 0x20, 0x67, + 0x6f, 0x6f, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x79, 0x6f, 0x75, 0x21, 0x21, 0x21, 0x20, 0x72, + 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x21, 0x21, 0x21 }, + .ilen = 32, + .result = { 0xe0, 0x4d, 0x5d, 0x3c, 0xb7, 0x8c, 0x36, 0x47, + 0x94, 0x18, 0x95, 0x91, 0xa9, 0xfc, 0x49, 0xf8, + 0x44, 0xd1, 0x2d, 0xc2, 0x99, 0xb8, 0x08, 0x2a, + 0x07, 0x89, 0x73, 0xc2, 0x45, 0x92, 0xc6, 0x90 }, + .rlen = 32, + } +}; + +struct cipher_testvec tea_dec_tv_template[] = +{ + { + .key = { [0 ... 15] = 0x00 }, + .klen = 16, + .input = { 0x0a, 0x3a, 0xea, 0x41, 0x40, 0xa9, 0xba, 0x94 }, + .ilen = 8, + .result = { [0 ... 8] = 0x00 }, + .rlen = 8, + }, { + .key = { 0x2b, 0x02, 0x05, 0x68, 0x06, 0x14, 0x49, 0x76, + 0x77, 0x5d, 0x0e, 0x26, 0x6c, 0x28, 0x78, 0x43 }, + .klen = 16, + .input = { 0x77, 0x5d, 0x2a, 0x6a, 0xf6, 0xce, 0x92, 0x09 }, + .ilen = 8, + .result = { 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x2e }, + .rlen = 8, + }, { + .key = { 0x09, 0x65, 0x43, 0x11, 0x66, 0x44, 0x39, 0x25, + 0x51, 0x3a, 0x16, 0x10, 0x0a, 0x08, 0x12, 0x6e }, + .klen = 16, + .input = { 0xbe, 0x7a, 0xbb, 0x81, 0x95, 0x2d, 0x1f, 0x1e, + 0xdd, 0x89, 0xa1, 0x25, 0x04, 0x21, 0xdf, 0x95 }, + .ilen = 16, + .result = { 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x5f, 0x74, + 0x65, 0x73, 0x74, 0x5f, 0x76, 0x65, 0x63, 0x74 }, + .rlen = 16, + }, { + .key = { 0x4d, 0x76, 0x32, 0x17, 0x05, 0x3f, 0x75, 0x2c, + 0x5d, 0x04, 0x16, 0x36, 0x15, 0x72, 0x63, 0x2f }, + .klen = 16, + .input = { 0xe0, 0x4d, 0x5d, 0x3c, 0xb7, 0x8c, 0x36, 0x47, + 0x94, 0x18, 0x95, 0x91, 0xa9, 0xfc, 0x49, 0xf8, + 0x44, 0xd1, 0x2d, 0xc2, 0x99, 0xb8, 0x08, 0x2a, + 0x07, 0x89, 0x73, 0xc2, 0x45, 0x92, 0xc6, 0x90 }, + .ilen = 32, + .result = { 0x54, 0x65, 0x61, 0x20, 0x69, 0x73, 0x20, 0x67, + 0x6f, 0x6f, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x79, 0x6f, 0x75, 0x21, 0x21, 0x21, 0x20, 0x72, + 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x21, 0x21, 0x21 }, + .rlen = 32, + } +}; + +/* + * XTEA test vectors + */ +#define XTEA_ENC_TEST_VECTORS 4 +#define XTEA_DEC_TEST_VECTORS 4 + +struct cipher_testvec xtea_enc_tv_template[] = +{ + { + .key = { [0 ... 15] = 0x00 }, + .klen = 16, + .input = { [0 ... 8] = 0x00 }, + .ilen = 8, + .result = { 0xaa, 0x22, 0x96, 0xe5, 0x6c, 0x61, 0xf3, 0x45 }, + .rlen = 8, + }, { + .key = { 0x2b, 0x02, 0x05, 0x68, 0x06, 0x14, 0x49, 0x76, + 0x77, 0x5d, 0x0e, 0x26, 0x6c, 0x28, 0x78, 0x43 }, + .klen = 16, + .input = { 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x2e }, + .ilen = 8, + .result = { 0x82, 0x3e, 0xeb, 0x35, 0xdc, 0xdd, 0xd9, 0xc3 }, + .rlen = 8, + }, { + .key = { 0x09, 0x65, 0x43, 0x11, 0x66, 0x44, 0x39, 0x25, + 0x51, 0x3a, 0x16, 0x10, 0x0a, 0x08, 0x12, 0x6e }, + .klen = 16, + .input = { 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x5f, 0x74, + 0x65, 0x73, 0x74, 0x5f, 0x76, 0x65, 0x63, 0x74 }, + .ilen = 16, + .result = { 0xe2, 0x04, 0xdb, 0xf2, 0x89, 0x85, 0x9e, 0xea, + 0x61, 0x35, 0xaa, 0xed, 0xb5, 0xcb, 0x71, 0x2c }, + .rlen = 16, + }, { + .key = { 0x4d, 0x76, 0x32, 0x17, 0x05, 0x3f, 0x75, 0x2c, + 0x5d, 0x04, 0x16, 0x36, 0x15, 0x72, 0x63, 0x2f }, + .klen = 16, + .input = { 0x54, 0x65, 0x61, 0x20, 0x69, 0x73, 0x20, 0x67, + 0x6f, 0x6f, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x79, 0x6f, 0x75, 0x21, 0x21, 0x21, 0x20, 0x72, + 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x21, 0x21, 0x21 }, + .ilen = 32, + .result = { 0x0b, 0x03, 0xcd, 0x8a, 0xbe, 0x95, 0xfd, 0xb1, + 0xc1, 0x44, 0x91, 0x0b, 0xa5, 0xc9, 0x1b, 0xb4, + 0xa9, 0xda, 0x1e, 0x9e, 0xb1, 0x3e, 0x2a, 0x8f, + 0xea, 0xa5, 0x6a, 0x85, 0xd1, 0xf4, 0xa8, 0xa5 }, + .rlen = 32, + } +}; + +struct cipher_testvec xtea_dec_tv_template[] = +{ + { + .key = { [0 ... 15] = 0x00 }, + .klen = 16, + .input = { 0xaa, 0x22, 0x96, 0xe5, 0x6c, 0x61, 0xf3, 0x45 }, + .ilen = 8, + .result = { [0 ... 8] = 0x00 }, + .rlen = 8, + }, { + .key = { 0x2b, 0x02, 0x05, 0x68, 0x06, 0x14, 0x49, 0x76, + 0x77, 0x5d, 0x0e, 0x26, 0x6c, 0x28, 0x78, 0x43 }, + .klen = 16, + .input = { 0x82, 0x3e, 0xeb, 0x35, 0xdc, 0xdd, 0xd9, 0xc3 }, + .ilen = 8, + .result = { 0x74, 0x65, 0x73, 0x74, 0x20, 0x6d, 0x65, 0x2e }, + .rlen = 8, + }, { + .key = { 0x09, 0x65, 0x43, 0x11, 0x66, 0x44, 0x39, 0x25, + 0x51, 0x3a, 0x16, 0x10, 0x0a, 0x08, 0x12, 0x6e }, + .klen = 16, + .input = { 0xe2, 0x04, 0xdb, 0xf2, 0x89, 0x85, 0x9e, 0xea, + 0x61, 0x35, 0xaa, 0xed, 0xb5, 0xcb, 0x71, 0x2c }, + .ilen = 16, + .result = { 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x5f, 0x74, + 0x65, 0x73, 0x74, 0x5f, 0x76, 0x65, 0x63, 0x74 }, + .rlen = 16, + }, { + .key = { 0x4d, 0x76, 0x32, 0x17, 0x05, 0x3f, 0x75, 0x2c, + 0x5d, 0x04, 0x16, 0x36, 0x15, 0x72, 0x63, 0x2f }, + .klen = 16, + .input = { 0x0b, 0x03, 0xcd, 0x8a, 0xbe, 0x95, 0xfd, 0xb1, + 0xc1, 0x44, 0x91, 0x0b, 0xa5, 0xc9, 0x1b, 0xb4, + 0xa9, 0xda, 0x1e, 0x9e, 0xb1, 0x3e, 0x2a, 0x8f, + 0xea, 0xa5, 0x6a, 0x85, 0xd1, 0xf4, 0xa8, 0xa5 }, + .ilen = 32, + .result = { 0x54, 0x65, 0x61, 0x20, 0x69, 0x73, 0x20, 0x67, + 0x6f, 0x6f, 0x64, 0x20, 0x66, 0x6f, 0x72, 0x20, + 0x79, 0x6f, 0x75, 0x21, 0x21, 0x21, 0x20, 0x72, + 0x65, 0x61, 0x6c, 0x6c, 0x79, 0x21, 0x21, 0x21 }, + .rlen = 32, + } +}; + + /* * Compression stuff. @@ -1719,4 +1910,55 @@ }, }; +/* + * Michael MIC test vectors from IEEE 802.11i + */ +#define MICHAEL_MIC_TEST_VECTORS 6 + +struct hash_testvec michael_mic_tv_template[] = +{ + { + .key = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + .ksize = 8, + .plaintext = { }, + .psize = 0, + .digest = { 0x82, 0x92, 0x5c, 0x1c, 0xa1, 0xd1, 0x30, 0xb8 } + }, + { + .key = { 0x82, 0x92, 0x5c, 0x1c, 0xa1, 0xd1, 0x30, 0xb8 }, + .ksize = 8, + .plaintext = { 'M' }, + .psize = 1, + .digest = { 0x43, 0x47, 0x21, 0xca, 0x40, 0x63, 0x9b, 0x3f } + }, + { + .key = { 0x43, 0x47, 0x21, 0xca, 0x40, 0x63, 0x9b, 0x3f }, + .ksize = 8, + .plaintext = { 'M', 'i' }, + .psize = 2, + .digest = { 0xe8, 0xf9, 0xbe, 0xca, 0xe9, 0x7e, 0x5d, 0x29 } + }, + { + .key = { 0xe8, 0xf9, 0xbe, 0xca, 0xe9, 0x7e, 0x5d, 0x29 }, + .ksize = 8, + .plaintext = { 'M', 'i', 'c' }, + .psize = 3, + .digest = { 0x90, 0x03, 0x8f, 0xc6, 0xcf, 0x13, 0xc1, 0xdb } + }, + { + .key = { 0x90, 0x03, 0x8f, 0xc6, 0xcf, 0x13, 0xc1, 0xdb }, + .ksize = 8, + .plaintext = { 'M', 'i', 'c', 'h' }, + .psize = 4, + .digest = { 0xd5, 0x5e, 0x10, 0x05, 0x10, 0x12, 0x89, 0x86 } + }, + { + .key = { 0xd5, 0x5e, 0x10, 0x05, 0x10, 0x12, 0x89, 0x86 }, + .ksize = 8, + .plaintext = { 'M', 'i', 'c', 'h', 'a', 'e', 'l' }, + .psize = 7, + .digest = { 0x0a, 0x94, 0x2b, 0x12, 0x4e, 0xca, 0xa5, 0x46 }, + } +}; + #endif /* _CRYPTO_TCRYPT_H */ diff -urN linux-2.4.26/crypto/tea.c linux-2.4.27/crypto/tea.c --- linux-2.4.26/crypto/tea.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/crypto/tea.c 2004-08-07 16:26:04.644346367 -0700 @@ -0,0 +1,246 @@ +/* + * Cryptographic API. + * + * TEA and Xtended TEA Algorithms + * + * The TEA and Xtended TEA algorithms were developed by David Wheeler + * and Roger Needham at the Computer Laboratory of Cambridge University. + * + * Copyright (c) 2004 Aaron Grothe ajgrothe@yahoo.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include + +#define TEA_KEY_SIZE 16 +#define TEA_BLOCK_SIZE 8 +#define TEA_ROUNDS 32 +#define TEA_DELTA 0x9e3779b9 + +#define XTEA_KEY_SIZE 16 +#define XTEA_BLOCK_SIZE 8 +#define XTEA_ROUNDS 32 +#define XTEA_DELTA 0x9e3779b9 + +#define u32_in(x) le32_to_cpu(*(const u32 *)(x)) +#define u32_out(to, from) (*(u32 *)(to) = cpu_to_le32(from)) + +struct tea_ctx { + u32 KEY[4]; +}; + +struct xtea_ctx { + u32 KEY[4]; +}; + +static int tea_setkey(void *ctx_arg, const u8 *in_key, + unsigned int key_len, u32 *flags) +{ + + struct tea_ctx *ctx = ctx_arg; + + if (key_len != 16) + { + *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; + } + + ctx->KEY[0] = u32_in (in_key); + ctx->KEY[1] = u32_in (in_key + 4); + ctx->KEY[2] = u32_in (in_key + 8); + ctx->KEY[3] = u32_in (in_key + 12); + + return 0; + +} + +static void tea_encrypt(void *ctx_arg, u8 *dst, const u8 *src) +{ + u32 y, z, n, sum = 0; + u32 k0, k1, k2, k3; + + struct tea_ctx *ctx = ctx_arg; + + y = u32_in (src); + z = u32_in (src + 4); + + k0 = ctx->KEY[0]; + k1 = ctx->KEY[1]; + k2 = ctx->KEY[2]; + k3 = ctx->KEY[3]; + + n = TEA_ROUNDS; + + while (n-- > 0) { + sum += TEA_DELTA; + y += ((z << 4) + k0) ^ (z + sum) ^ ((z >> 5) + k1); + z += ((y << 4) + k2) ^ (y + sum) ^ ((y >> 5) + k3); + } + + u32_out (dst, y); + u32_out (dst + 4, z); +} + +static void tea_decrypt(void *ctx_arg, u8 *dst, const u8 *src) +{ + u32 y, z, n, sum; + u32 k0, k1, k2, k3; + + struct tea_ctx *ctx = ctx_arg; + + y = u32_in (src); + z = u32_in (src + 4); + + k0 = ctx->KEY[0]; + k1 = ctx->KEY[1]; + k2 = ctx->KEY[2]; + k3 = ctx->KEY[3]; + + sum = TEA_DELTA << 5; + + n = TEA_ROUNDS; + + while (n-- > 0) { + z -= ((y << 4) + k2) ^ (y + sum) ^ ((y >> 5) + k3); + y -= ((z << 4) + k0) ^ (z + sum) ^ ((z >> 5) + k1); + sum -= TEA_DELTA; + } + + u32_out (dst, y); + u32_out (dst + 4, z); + +} + +static int xtea_setkey(void *ctx_arg, const u8 *in_key, + unsigned int key_len, u32 *flags) +{ + + struct xtea_ctx *ctx = ctx_arg; + + if (key_len != 16) + { + *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; + } + + ctx->KEY[0] = u32_in (in_key); + ctx->KEY[1] = u32_in (in_key + 4); + ctx->KEY[2] = u32_in (in_key + 8); + ctx->KEY[3] = u32_in (in_key + 12); + + return 0; + +} + +static void xtea_encrypt(void *ctx_arg, u8 *dst, const u8 *src) +{ + + u32 y, z, sum = 0; + u32 limit = XTEA_DELTA * XTEA_ROUNDS; + + struct xtea_ctx *ctx = ctx_arg; + + y = u32_in (src); + z = u32_in (src + 4); + + while (sum != limit) { + y += (z << 4 ^ z >> 5) + (z ^ sum) + ctx->KEY[sum&3]; + sum += TEA_DELTA; + z += (y << 4 ^ y >> 5) + (y ^ sum) + ctx->KEY[sum>>11 &3]; + } + + u32_out (dst, y); + u32_out (dst + 4, z); + +} + +static void xtea_decrypt(void *ctx_arg, u8 *dst, const u8 *src) +{ + + u32 y, z, sum; + struct tea_ctx *ctx = ctx_arg; + + y = u32_in (src); + z = u32_in (src + 4); + + sum = XTEA_DELTA * XTEA_ROUNDS; + + while (sum) { + z -= (y << 4 ^ y >> 5) + (y ^ sum) + ctx->KEY[sum>>11 & 3]; + sum -= XTEA_DELTA; + y -= (z << 4 ^ z >> 5) + (z ^ sum) + ctx->KEY[sum & 3]; + } + + u32_out (dst, y); + u32_out (dst + 4, z); + +} + +static struct crypto_alg tea_alg = { + .cra_name = "tea", + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = TEA_BLOCK_SIZE, + .cra_ctxsize = sizeof (struct tea_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(tea_alg.cra_list), + .cra_u = { .cipher = { + .cia_min_keysize = TEA_KEY_SIZE, + .cia_max_keysize = TEA_KEY_SIZE, + .cia_setkey = tea_setkey, + .cia_encrypt = tea_encrypt, + .cia_decrypt = tea_decrypt } } +}; + +static struct crypto_alg xtea_alg = { + .cra_name = "xtea", + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = XTEA_BLOCK_SIZE, + .cra_ctxsize = sizeof (struct xtea_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(xtea_alg.cra_list), + .cra_u = { .cipher = { + .cia_min_keysize = XTEA_KEY_SIZE, + .cia_max_keysize = XTEA_KEY_SIZE, + .cia_setkey = xtea_setkey, + .cia_encrypt = xtea_encrypt, + .cia_decrypt = xtea_decrypt } } +}; + +static int __init init(void) +{ + int ret = 0; + + ret = crypto_register_alg(&tea_alg); + if (ret < 0) + goto out; + + ret = crypto_register_alg(&xtea_alg); + if (ret < 0) { + crypto_unregister_alg(&tea_alg); + goto out; + } + +out: + return ret; +} + +static void __exit fini(void) +{ + crypto_unregister_alg(&tea_alg); + crypto_unregister_alg(&xtea_alg); +} + +module_init(init); +module_exit(fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TEA & XTEA Cryptographic Algorithms"); diff -urN linux-2.4.26/crypto/twofish.c linux-2.4.27/crypto/twofish.c --- linux-2.4.26/crypto/twofish.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/crypto/twofish.c 2004-08-07 16:26:04.644346367 -0700 @@ -663,7 +663,10 @@ /* Check key length. */ if (key_len != 16 && key_len != 24 && key_len != 32) + { + *flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; return -EINVAL; /* unsupported key length */ + } /* Compute the first two words of the S vector. The magic numbers are * the entries of the RS matrix, preprocessed through poly_to_exp. The diff -urN linux-2.4.26/drivers/acpi/Config.in linux-2.4.27/drivers/acpi/Config.in --- linux-2.4.26/drivers/acpi/Config.in 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/drivers/acpi/Config.in 2004-08-07 16:26:04.645346408 -0700 @@ -16,6 +16,7 @@ define_bool CONFIG_ACPI_POWER y if [ "$CONFIG_PCI" = "y" ]; then define_bool CONFIG_ACPI_PCI y + define_bool CONFIG_ACPI_MMCONFIG y fi define_bool CONFIG_ACPI_SLEEP y define_bool CONFIG_ACPI_SYSTEM y @@ -28,7 +29,6 @@ tristate ' ASUS Laptop Extras' CONFIG_ACPI_ASUS tristate ' Toshiba Laptop Extras' CONFIG_ACPI_TOSHIBA bool ' Debug Statements' CONFIG_ACPI_DEBUG - bool ' Relaxed AML Checking' CONFIG_ACPI_RELAXED_AML else if [ "$CONFIG_SMP" = "y" ]; then define_bool CONFIG_ACPI_BOOT y diff -urN linux-2.4.26/drivers/acpi/ac.c linux-2.4.27/drivers/acpi/ac.c --- linux-2.4.26/drivers/acpi/ac.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/ac.c 2004-08-07 16:26:04.646346449 -0700 @@ -156,6 +156,7 @@ acpi_ac_dir); if (!acpi_device_dir(device)) return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; } /* 'state' [R] */ @@ -168,6 +169,7 @@ else { entry->read_proc = acpi_ac_read_state; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } return_VALUE(0); @@ -181,6 +183,8 @@ ACPI_FUNCTION_TRACE("acpi_ac_remove_fs"); if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_AC_FILE_STATE, + acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), acpi_ac_dir); acpi_device_dir(device) = NULL; } @@ -318,6 +322,7 @@ acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); if (!acpi_ac_dir) return_VALUE(-ENODEV); + acpi_ac_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_ac_driver); if (result < 0) { diff -urN linux-2.4.26/drivers/acpi/asus_acpi.c linux-2.4.27/drivers/acpi/asus_acpi.c --- linux-2.4.26/drivers/acpi/asus_acpi.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/asus_acpi.c 2004-08-07 16:26:04.648346531 -0700 @@ -40,8 +40,9 @@ #include #include #include +#include -#define ASUS_ACPI_VERSION "0.27" +#define ASUS_ACPI_VERSION "0.28" #define PROC_ASUS "asus" //the directory #define PROC_MLED "mled" @@ -125,12 +126,11 @@ L5x, //L5800C L8L, //L8400L M1A, //M1300A - M2E, //M2400E + M2E, //M2400E, L4400L + P30, //Samsung P30 S1x, //S1300A, but also L1400B and M2400A (L84F) S2x, //S200 (J1 reported), Victor MP-XP7210 - //TODO A1370D does not seem to have an ATK device - // L8400 model doesn't have ATK - xxN, //M2400N, M3700N, S1300N (Centrino) + xxN, //M2400N, M3700N, M6800N, S1300N, S5200N (Centrino) END_MODEL } model; //Models currently supported u16 event_count[128]; //count for each event TODO make this better @@ -140,6 +140,7 @@ #define A1x_PREFIX "\\_SB.PCI0.ISA.EC0." #define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0." #define M1A_PREFIX "\\_SB.PCI0.PX40.EC0." +#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0." #define S1x_PREFIX "\\_SB.PCI0.PX40." #define S2x_PREFIX A1x_PREFIX #define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0." @@ -166,7 +167,7 @@ .mt_lcd_switch = A1x_PREFIX "_Q10", .lcd_status = "\\BKLI", .brightness_up = A1x_PREFIX "_Q0E", - .brightness_down = A1x_PREFIX "_Q0F", + .brightness_down = A1x_PREFIX "_Q0F" }, { @@ -176,11 +177,8 @@ .wled_status = "\\SG66", .mt_lcd_switch = "\\Q10", .lcd_status = "\\BAOF", - .brightness_up = "\\Q0E", - .brightness_down = "\\Q0F", .brightness_set = "SPLV", .brightness_get = "GPLV", - .brightness_status = "\\CMOD", .display_set = "SDSP", .display_get = "\\INFB" }, @@ -217,11 +215,8 @@ .mt_wled = "WLED", .mt_lcd_switch = L3C_PREFIX "_Q10", .lcd_status = "\\GL32", - .brightness_up = L3C_PREFIX "_Q0F", - .brightness_down = L3C_PREFIX "_Q0E", .brightness_set = "SPLV", .brightness_get = "GPLV", - .brightness_status = "\\BLVL", .display_set = "SDSP", .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP" }, @@ -233,11 +228,8 @@ .mt_wled = "WLED", .mt_lcd_switch = "\\Q10", .lcd_status = "\\BKLG", - .brightness_up = "\\Q0E", - .brightness_down = "\\Q0F", .brightness_set = "SPLV", .brightness_get = "GPLV", - .brightness_status = "\\BLVL", .display_set = "SDSP", .display_get = "\\INFB" }, @@ -257,14 +249,10 @@ { .name = "L5x", .mt_mled = "MLED", -// .mt_wled = "WLED", -// .wled_status = "\\WRED", -/* Present, but not controlled by ACPI */ +/* WLED present, but not controlled by ACPI */ .mt_tled = "TLED", .mt_lcd_switch = "\\Q0D", .lcd_status = "\\BAOF", - .brightness_up = "\\Q0C", - .brightness_down = "\\Q0B", .brightness_set = "SPLV", .brightness_get = "GPLV", .display_set = "SDSP", @@ -294,8 +282,6 @@ .mt_wled = "WLED", .mt_lcd_switch = "\\Q10", .lcd_status = "\\GP06", - .brightness_up = "\\Q0E", - .brightness_down = "\\Q0F", .brightness_set = "SPLV", .brightness_get = "GPLV", .display_set = "SDSP", @@ -303,17 +289,26 @@ }, { + .name = "P30", + .mt_wled = "WLED", + .mt_lcd_switch = P30_PREFIX "_Q0E", + .lcd_status = "\\BKLT", + .brightness_up = P30_PREFIX "_Q68", + .brightness_down = P30_PREFIX "_Q69", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\DNXT" + }, + + { .name = "S1x", .mt_mled = "MLED", .mled_status = "\\EMLE", .mt_wled = "WLED", .mt_lcd_switch = S1x_PREFIX "Q10" , .lcd_status = "\\PNOF", - .brightness_up = S1x_PREFIX "Q0F", - .brightness_down = S1x_PREFIX "Q0E", .brightness_set = "SPLV", - .brightness_get = "GPLV", - .brightness_status = "\\BRIT", + .brightness_get = "GPLV" }, { @@ -323,22 +318,17 @@ .mt_lcd_switch = S2x_PREFIX "_Q10", .lcd_status = "\\BKLI", .brightness_up = S2x_PREFIX "_Q0B", - .brightness_down = S2x_PREFIX "_Q0A", + .brightness_down = S2x_PREFIX "_Q0A" }, { .name = "xxN", .mt_mled = "MLED", -// .mt_wled = "WLED", -// .wled_status = "\\PO33", -/* Present, but not controlled by ACPI */ +/* WLED present, but not controlled by ACPI */ .mt_lcd_switch = xxN_PREFIX "_Q10", .lcd_status = "\\BKLT", - .brightness_up = xxN_PREFIX "_Q0F", - .brightness_down = xxN_PREFIX "_Q0E", .brightness_set = "SPLV", .brightness_get = "GPLV", - .brightness_status = "\\LBTN", .display_set = "SDSP", .display_get = "\\ADVG" } @@ -466,6 +456,21 @@ return len; } + +static int parse_arg(const char *buf, unsigned long count, int *val) +{ + char s[32]; + if (!count) + return 0; + if (count > 31) + return -EINVAL; + if (copy_from_user(s, buf, count)) + return -EFAULT; + s[count] = 0; + if (sscanf(s, "%i", val) != 1) + return -EINVAL; + return count; +} /* @@ -497,11 +502,14 @@ write_led(const char *buffer, unsigned long count, struct asus_hotk *hotk, char *ledname, int ledmask, int invert) { - int value; + int value, retval; int led_out = 0; - if (sscanf(buffer, "%i", &value) == 1) - led_out = value ? 1 : 0; + retval = parse_arg(buffer, count, &value); + if (retval <= 0) + return retval; + count = retval; + led_out = value ? 1 : 0; hotk->status = (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask); @@ -654,15 +662,33 @@ proc_write_lcd(struct file *file, const char *buffer, unsigned long count, void *data) { - int value; + int value, retval; struct asus_hotk *hotk = (struct asus_hotk *) data; - if (sscanf(buffer, "%i", &value) == 1) + retval = parse_arg(buffer, count, &value); + if (retval > 0) set_lcd_state(hotk, value); - return count; + return retval; } +static int read_brightness(struct asus_hotk *hotk) +{ + int value; + + if(hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ + if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, + &value)) + printk(KERN_WARNING "Asus ACPI: Error reading brightness\n"); + } else if (hotk->methods->brightness_status) { /* For D1 for example */ + if (!read_acpi_int(NULL, hotk->methods->brightness_status, + &value)) + printk(KERN_WARNING "Asus ACPI: Error reading brightness\n"); + } else /* No GPLV method */ + value = hotk->brightness; + return value; +} + /* * Change the brightness level */ @@ -679,7 +705,7 @@ } /* No SPLV method if we are here, act as appropriate */ - value -= hotk->brightness; + value -= read_brightness(hotk); while (value != 0) { status = acpi_evaluate_object(NULL, (value > 0) ? hotk->methods->brightness_up : @@ -692,23 +718,6 @@ return; } -static int read_brightness(struct asus_hotk *hotk) -{ - int value; - - if(hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ - if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, - &value)) - printk(KERN_WARNING "Asus ACPI: Error reading brightness\n"); - } else if (hotk->methods->brightness_status) { /* For D1 for example */ - if (!read_acpi_int(NULL, hotk->methods->brightness_status, - &value)) - printk(KERN_WARNING "Asus ACPI: Error reading brightness\n"); - } else /* No GPLV method */ - value = hotk->brightness; - return value; -} - static int proc_read_brn(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -721,10 +730,11 @@ proc_write_brn(struct file *file, const char *buffer, unsigned long count, void *data) { - int value; + int value, retval; struct asus_hotk *hotk = (struct asus_hotk *) data; - if (sscanf(buffer, "%d", &value) == 1) { + retval = parse_arg(buffer, count, &value); + if (retval > 0) { value = (0 < value) ? ((15 < value) ? 15 : value) : 0; /* 0 <= value <= 15 */ set_brightness(value, hotk); @@ -732,7 +742,7 @@ printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); } - return count; + return retval; } static void set_display(int value, struct asus_hotk *hotk) @@ -770,16 +780,17 @@ proc_write_disp(struct file *file, const char *buffer, unsigned long count, void *data) { - int value; + int value, retval; struct asus_hotk *hotk = (struct asus_hotk *) data; - if (sscanf(buffer, "%d", &value) == 1) + retval = parse_arg(buffer, count, &value); + if (retval > 0) set_display(value, hotk); else { printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); } - return count; + return retval; } @@ -874,6 +885,28 @@ return 0; } +static int asus_hotk_remove_fs(struct acpi_device* device) +{ + struct asus_hotk* hotk = acpi_driver_data(device); + + if(acpi_device_dir(device)){ + remove_proc_entry(PROC_INFO,acpi_device_dir(device)); + if (hotk->methods->mt_wled) + remove_proc_entry(PROC_WLED,acpi_device_dir(device)); + if (hotk->methods->mt_mled) + remove_proc_entry(PROC_MLED,acpi_device_dir(device)); + if (hotk->methods->mt_tled) + remove_proc_entry(PROC_TLED,acpi_device_dir(device)); + if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) + remove_proc_entry(PROC_LCD, acpi_device_dir(device)); + if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || (hotk->methods->brightness_get && hotk->methods->brightness_get)) + remove_proc_entry(PROC_BRN, acpi_device_dir(device)); + if (hotk->methods->display_set) + remove_proc_entry(PROC_DISP, acpi_device_dir(device)); + } + return 0; +} + static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) { @@ -929,12 +962,29 @@ return -ENODEV; } - /* For testing purposes */ + /* This needs to be called for some laptops to init properly */ if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result)) printk(KERN_WARNING " Error calling BSTS\n"); else if (bsts_result) printk(KERN_NOTICE " BSTS called, 0x%02x returned\n", bsts_result); + /* Samsung P30 has a device with a valid _HID whose INIT does not + * return anything. Catch this one and any similar here */ + if (buffer.pointer == NULL) { + if (asus_info && /* Samsung P30 */ + strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { + hotk->model = P30; + printk(KERN_NOTICE " Samsung P30 detected, supported\n"); + } else { + hotk->model = M2E; + printk(KERN_WARNING " no string returned by INIT\n"); + printk(KERN_WARNING " trying default values, supply " + "the developers with your DSDT\n"); + } + hotk->methods = &model_conf[hotk->model]; + return AE_OK; + } + model = (union acpi_object *) buffer.pointer; if (model->type == ACPI_TYPE_STRING) { printk(KERN_NOTICE " %s model detected, ", model->string.pointer); @@ -953,12 +1003,14 @@ hotk->model = L8L; else if (strncmp(model->string.pointer, "M2N", 3) == 0 || strncmp(model->string.pointer, "M3N", 3) == 0 || + strncmp(model->string.pointer, "M6N", 3) == 0 || strncmp(model->string.pointer, "S1N", 3) == 0 || strncmp(model->string.pointer, "S5N", 3) == 0) hotk->model = xxN; else if (strncmp(model->string.pointer, "M1", 2) == 0) hotk->model = M1A; - else if (strncmp(model->string.pointer, "M2", 2) == 0) + else if (strncmp(model->string.pointer, "M2", 2) == 0 || + strncmp(model->string.pointer, "L4E", 3) == 0) hotk->model = M2E; else if (strncmp(model->string.pointer, "L2", 2) == 0) hotk->model = L2D; @@ -994,6 +1046,13 @@ else if (strncmp(model->string.pointer, "S5N", 3) == 0) hotk->methods->mt_mled = NULL; /* S5N has no MLED */ + else if (strncmp(model->string.pointer, "M6N", 3) == 0) { + hotk->methods->display_get = NULL; //TODO + hotk->methods->lcd_status = "\\_SB.BKLT"; + hotk->methods->mt_wled = "WLED"; + hotk->methods->wled_status = "\\_SB.PCI0.SBRG.SG13"; + /* M6N differs slightly and has a usable WLED */ + } else if (asus_info) { if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) hotk->methods->mled_status = NULL; @@ -1049,8 +1108,8 @@ memset(hotk, 0, sizeof(struct asus_hotk)); hotk->handle = device->handle; - sprintf(acpi_device_name(device), "%s", ACPI_HOTK_DEVICE_NAME); - sprintf(acpi_device_class(device), "%s", ACPI_HOTK_CLASS); + strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_HOTK_CLASS); acpi_driver_data(device) = hotk; hotk->device = device; @@ -1112,6 +1171,8 @@ if (ACPI_FAILURE(status)) printk(KERN_ERR "Asus ACPI: Error removing notify handler\n"); + asus_hotk_remove_fs(device); + kfree(hotk); return(0); diff -urN linux-2.4.26/drivers/acpi/battery.c linux-2.4.27/drivers/acpi/battery.c --- linux-2.4.26/drivers/acpi/battery.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/battery.c 2004-08-07 16:26:04.649346572 -0700 @@ -474,14 +474,18 @@ else p += sprintf(p, "capacity state: critical\n"); - if ((bst->state & 0x01) && (bst->state & 0x02)) + if ((bst->state & 0x01) && (bst->state & 0x02)){ p += sprintf(p, "charging state: charging/discharging\n"); + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Battery Charging and Discharging?\n")); + } else if (bst->state & 0x01) p += sprintf(p, "charging state: discharging\n"); else if (bst->state & 0x02) p += sprintf(p, "charging state: charging\n"); - else - p += sprintf(p, "charging state: unknown\n"); + else { + p += sprintf(p, "charging state: charged\n"); + } if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN) p += sprintf(p, "present rate: unknown\n"); @@ -609,6 +613,7 @@ acpi_battery_dir); if (!acpi_device_dir(device)) return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; } /* 'info' [R] */ @@ -621,6 +626,7 @@ else { entry->read_proc = acpi_battery_read_info; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'status' [R] */ @@ -633,6 +639,7 @@ else { entry->read_proc = acpi_battery_read_state; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'alarm' [R/W] */ @@ -646,6 +653,7 @@ entry->read_proc = acpi_battery_read_alarm; entry->write_proc = acpi_battery_write_alarm; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } return_VALUE(0); @@ -659,6 +667,12 @@ ACPI_FUNCTION_TRACE("acpi_battery_remove_fs"); if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_BATTERY_FILE_ALARM, + acpi_device_dir(device)); + remove_proc_entry(ACPI_BATTERY_FILE_STATUS, + acpi_device_dir(device)); + remove_proc_entry(ACPI_BATTERY_FILE_INFO, + acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); acpi_device_dir(device) = NULL; } @@ -797,6 +811,7 @@ acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); if (!acpi_battery_dir) return_VALUE(-ENODEV); + acpi_battery_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_battery_driver); if (result < 0) { diff -urN linux-2.4.26/drivers/acpi/bus.c linux-2.4.27/drivers/acpi/bus.c --- linux-2.4.26/drivers/acpi/bus.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/drivers/acpi/bus.c 2004-08-07 16:26:04.650346613 -0700 @@ -1964,8 +1964,10 @@ acpi_ec_init(); /* ACPI Embedded Controller */ #endif #ifdef CONFIG_ACPI_PCI - acpi_pci_link_init(); /* ACPI PCI Interrupt Link */ - acpi_pci_root_init(); /* ACPI PCI Root Bridge */ + if (!acpi_pci_disabled) { + acpi_pci_link_init(); /* ACPI PCI Interrupt Link */ + acpi_pci_root_init(); /* ACPI PCI Root Bridge */ + } #endif /* * Enumerate devices in the ACPI namespace. diff -urN linux-2.4.26/drivers/acpi/button.c linux-2.4.27/drivers/acpi/button.c --- linux-2.4.26/drivers/acpi/button.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/button.c 2004-08-07 16:26:04.651346654 -0700 @@ -171,10 +171,15 @@ acpi_button_dir); break; } + + if (!entry) + return_VALUE(-ENODEV); + entry->owner = THIS_MODULE; acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry); if (!acpi_device_dir(device)) return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; /* 'info' [R] */ entry = create_proc_entry(ACPI_BUTTON_FILE_INFO, @@ -186,6 +191,7 @@ else { entry->read_proc = acpi_button_read_info; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } if (button->type==ACPI_BUTTON_TYPE_LID){ @@ -199,6 +205,7 @@ else { entry->read_proc = acpi_button_lid_read_state; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } } @@ -210,10 +217,37 @@ acpi_button_remove_fs ( struct acpi_device *device) { + struct acpi_button *button = NULL; + ACPI_FUNCTION_TRACE("acpi_button_remove_fs"); + button = acpi_driver_data(device); if (acpi_device_dir(device)) { - remove_proc_entry(acpi_device_bid(device), acpi_button_dir); + if (button->type == ACPI_BUTTON_TYPE_LID) + remove_proc_entry(ACPI_BUTTON_FILE_STATE, + acpi_device_dir(device)); + remove_proc_entry(ACPI_BUTTON_FILE_INFO, + acpi_device_dir(device)); + + remove_proc_entry(acpi_device_bid(device), + acpi_device_dir(device)->parent); + + switch (button->type) { + case ACPI_BUTTON_TYPE_POWER: + case ACPI_BUTTON_TYPE_POWERF: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, + acpi_button_dir); + break; + case ACPI_BUTTON_TYPE_SLEEP: + case ACPI_BUTTON_TYPE_SLEEPF: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, + acpi_button_dir); + break; + case ACPI_BUTTON_TYPE_LID: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, + acpi_button_dir); + break; + } acpi_device_dir(device) = NULL; } @@ -470,6 +504,7 @@ acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); if (!acpi_button_dir) return_VALUE(-ENODEV); + acpi_button_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_button_driver); if (result < 0) { diff -urN linux-2.4.26/drivers/acpi/ec.c linux-2.4.27/drivers/acpi/ec.c --- linux-2.4.26/drivers/acpi/ec.c 2004-04-14 06:05:28.000000000 -0700 +++ linux-2.4.27/drivers/acpi/ec.c 2004-08-07 16:26:04.651346654 -0700 @@ -544,6 +544,12 @@ { ACPI_FUNCTION_TRACE("acpi_ec_remove_fs"); + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), acpi_ec_dir); + acpi_device_dir(device) = NULL; + } + return_VALUE(0); } diff -urN linux-2.4.26/drivers/acpi/fan.c linux-2.4.27/drivers/acpi/fan.c --- linux-2.4.26/drivers/acpi/fan.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/fan.c 2004-08-07 16:26:04.652346695 -0700 @@ -151,6 +151,7 @@ acpi_fan_dir); if (!acpi_device_dir(device)) return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; } /* 'status' [R/W] */ @@ -164,6 +165,7 @@ entry->read_proc = acpi_fan_read_state; entry->write_proc = acpi_fan_write_state; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } return_VALUE(0); @@ -177,6 +179,8 @@ ACPI_FUNCTION_TRACE("acpi_fan_remove_fs"); if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_FAN_FILE_STATE, + acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), acpi_fan_dir); acpi_device_dir(device) = NULL; } @@ -267,6 +271,7 @@ acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); if (!acpi_fan_dir) return_VALUE(-ENODEV); + acpi_fan_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_fan_driver); if (result < 0) { diff -urN linux-2.4.26/drivers/acpi/pci_irq.c linux-2.4.27/drivers/acpi/pci_irq.c --- linux-2.4.26/drivers/acpi/pci_irq.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/acpi/pci_irq.c 2004-08-07 16:26:04.653346736 -0700 @@ -373,7 +373,7 @@ if (!irq) { printk(KERN_WARNING PREFIX "No IRQ known for interrupt pin %c of device %s", ('A' + pin), dev->slot_name); /* Interrupt Line values above 0xF are forbidden */ - if (dev->irq && dev->irq >= 0xF) { + if (dev->irq && (dev->irq <= 0xF)) { printk(" - using IRQ %d\n", dev->irq); return_VALUE(dev->irq); } diff -urN linux-2.4.26/drivers/acpi/pci_link.c linux-2.4.27/drivers/acpi/pci_link.c --- linux-2.4.26/drivers/acpi/pci_link.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/acpi/pci_link.c 2004-08-07 16:26:04.654346777 -0700 @@ -90,6 +90,9 @@ PCI Link Device Management -------------------------------------------------------------------------- */ +/* + * set context (link) possible list from resource list + */ static acpi_status acpi_pci_link_check_possible ( struct acpi_resource *resource, @@ -128,7 +131,7 @@ struct acpi_resource_ext_irq *p = &resource->data.extended_irq; if (!p || !p->number_of_interrupts) { ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Blank IRQ resource\n")); + "Blank EXT IRQ resource\n")); return AE_OK; } for (i = 0; (inumber_of_interrupts && idata.irq; if (!p || !p->number_of_interrupts) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Blank IRQ resource\n")); + /* + * IRQ descriptors may have no IRQ# bits set, + * particularly those those w/ _STA disabled + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Blank IRQ resource\n")); return AE_OK; } *irq = p->interrupts[0]; @@ -204,8 +211,12 @@ { struct acpi_resource_ext_irq *p = &resource->data.extended_irq; if (!p || !p->number_of_interrupts) { + /* + * extended IRQ descriptors must + * return at least 1 IRQ + */ ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "Blank IRQ resource\n")); + "Blank EXT IRQ resource\n")); return AE_OK; } *irq = p->interrupts[0]; @@ -219,6 +230,13 @@ return AE_CTRL_TERMINATE; } +/* + * Run _CRS and set link->irq.active + * + * return value: + * 0 - success + * !0 - failure + */ static int acpi_pci_link_get_current ( struct acpi_pci_link *link) @@ -234,15 +252,19 @@ link->irq.active = 0; - /* Make sure the link is enabled (no use querying if it isn't). */ - result = acpi_bus_get_status(link->device); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n")); - goto end; - } - if (!link->device->status.enabled) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n")); - return_VALUE(0); + /* in practice, status disabled is meaningless, ignore it */ + if (acpi_strict) { + /* Query _STA, set link->device->status */ + result = acpi_bus_get_status(link->device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n")); + goto end; + } + + if (!link->device->status.enabled) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n")); + return_VALUE(0); + } } /* @@ -257,18 +279,11 @@ goto end; } - if (!irq) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No IRQ resource found\n")); + if (acpi_strict && !irq) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "_CRS returned 0\n")); result = -ENODEV; - goto end; } - /* - * Note that we don't validate that the current IRQ (_CRS) exists - * within the possible IRQs (_PRS): we blindly assume that whatever - * IRQ a boot-enabled Link device is set to is the correct one. - * (Required to support systems such as the Toshiba 5005-S504.) - */ link->irq.active = irq; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active)); @@ -278,32 +293,6 @@ } static int -acpi_pci_link_try_get_current ( - struct acpi_pci_link *link, - int irq) -{ - int result; - - ACPI_FUNCTION_TRACE("acpi_pci_link_try_get_current"); - - result = acpi_pci_link_get_current(link); - if (result && link->irq.active) { - return_VALUE(result); - } - - if (!link->irq.active) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No active IRQ resource found\n")); - printk(KERN_WARNING "_CRS returns NULL! Using IRQ %d for" - "device (%s [%s]).\n", irq, - acpi_device_name(link->device), - acpi_device_bid(link->device)); - link->irq.active = irq; - } - - return 0; -} - -static int acpi_pci_link_set ( struct acpi_pci_link *link, int irq) @@ -315,59 +304,24 @@ struct acpi_resource end; } resource; struct acpi_buffer buffer = {sizeof(resource)+1, &resource}; - int i = 0; - int valid = 0; - int resource_type = 0; - + ACPI_FUNCTION_TRACE("acpi_pci_link_set"); if (!link || !irq) return_VALUE(-EINVAL); - /* We don't check irqs the first time around */ - if (link->irq.setonboot) { - /* See if we're already at the target IRQ. */ - if (irq == link->irq.active) - return_VALUE(0); - - /* Make sure the target IRQ in the list of possible IRQs. */ - for (i=0; iirq.possible_count; i++) { - if (irq == link->irq.possible[i]) - valid = 1; - } - if (!valid) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Target IRQ %d invalid\n", irq)); - return_VALUE(-EINVAL); - } - } - - resource_type = link->irq.resource_type; - - if (resource_type != ACPI_RSTYPE_IRQ && - resource_type != ACPI_RSTYPE_EXT_IRQ){ - /* If IRQ<=15, first try with a "normal" IRQ descriptor. If that fails, try with - * an extended one */ - if (irq <= 15) { - resource_type = ACPI_RSTYPE_IRQ; - } else { - resource_type = ACPI_RSTYPE_EXT_IRQ; - } - } - -retry_programming: - memset(&resource, 0, sizeof(resource)); - /* NOTE: PCI interrupts are always level / active_low / shared. But not all - interrupts > 15 are PCI interrupts. Rely on the ACPI IRQ definition for - parameters */ - switch(resource_type) { + switch(link->irq.resource_type) { case ACPI_RSTYPE_IRQ: resource.res.id = ACPI_RSTYPE_IRQ; resource.res.length = sizeof(struct acpi_resource); resource.res.data.irq.edge_level = link->irq.edge_level; resource.res.data.irq.active_high_low = link->irq.active_high_low; - resource.res.data.irq.shared_exclusive = ACPI_SHARED; + if (link->irq.edge_level == ACPI_EDGE_SENSITIVE) + resource.res.data.irq.shared_exclusive = ACPI_EXCLUSIVE; + else + resource.res.data.irq.shared_exclusive = ACPI_SHARED; resource.res.data.irq.number_of_interrupts = 1; resource.res.data.irq.interrupts[0] = irq; break; @@ -378,55 +332,63 @@ resource.res.data.extended_irq.producer_consumer = ACPI_CONSUMER; resource.res.data.extended_irq.edge_level = link->irq.edge_level; resource.res.data.extended_irq.active_high_low = link->irq.active_high_low; - resource.res.data.extended_irq.shared_exclusive = ACPI_SHARED; + if (link->irq.edge_level == ACPI_EDGE_SENSITIVE) + resource.res.data.irq.shared_exclusive = ACPI_EXCLUSIVE; + else + resource.res.data.irq.shared_exclusive = ACPI_SHARED; resource.res.data.extended_irq.number_of_interrupts = 1; resource.res.data.extended_irq.interrupts[0] = irq; /* ignore resource_source, it's optional */ break; + default: + printk("ACPI BUG: resource_type %d\n", link->irq.resource_type); + return_VALUE(-EINVAL); } resource.end.id = ACPI_RSTYPE_END_TAG; /* Attempt to set the resource */ status = acpi_set_current_resources(link->handle, &buffer); - - /* if we failed and IRQ <= 15, try again with an extended descriptor */ - if (ACPI_FAILURE(status) && (resource_type == ACPI_RSTYPE_IRQ)) { - resource_type = ACPI_RSTYPE_EXT_IRQ; - printk(PREFIX "Retrying with extended IRQ descriptor\n"); - goto retry_programming; - } - /* check for total failure */ if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n")); return_VALUE(-ENODEV); } - /* Make sure the device is enabled. */ + /* Query _STA, set device->status */ result = acpi_bus_get_status(link->device); if (result) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n")); return_VALUE(result); } if (!link->device->status.enabled) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n")); - return_VALUE(-ENODEV); + printk(KERN_WARNING PREFIX + "%s [%s] disabled and referenced, BIOS bug.\n", + acpi_device_name(link->device), + acpi_device_bid(link->device)); } - /* Make sure the active IRQ is the one we requested. */ - result = acpi_pci_link_try_get_current(link, irq); + /* Query _CRS, set link->irq.active */ + result = acpi_pci_link_get_current(link); if (result) { return_VALUE(result); } - + + /* + * Is current setting not what we set? + * set link->irq.active + */ if (link->irq.active != irq) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "Attempt to enable at IRQ %d resulted in IRQ %d\n", - irq, link->irq.active)); - link->irq.active = 0; - acpi_ut_evaluate_object (link->handle, "_DIS", 0, NULL); - return_VALUE(-ENODEV); + /* + * policy: when _CRS doesn't return what we just _SRS + * assume _SRS worked and override _CRS value. + */ + printk(KERN_WARNING PREFIX + "%s [%s] BIOS reported IRQ %d, using IRQ %d\n", + acpi_device_name(link->device), + acpi_device_bid(link->device), + link->irq.active, irq); + link->irq.active = irq; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active)); @@ -480,7 +442,7 @@ #define PIRQ_PENALTY_ISA_USED (16*16*16*16*16) #define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16) -static int acpi_irq_penalty[ACPI_MAX_IRQS] = { +static int __initdata acpi_irq_penalty[ACPI_MAX_IRQS] = { PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */ PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */ @@ -493,7 +455,7 @@ PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ9 PCI, often acpi */ PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ10 PCI */ PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ11 PCI */ - PIRQ_PENALTY_ISA_TYPICAL, /* IRQ12 mouse */ + PIRQ_PENALTY_ISA_USED, /* IRQ12 mouse */ PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */ PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */ PIRQ_PENALTY_ISA_USED, /* IRQ15 ide1 */ @@ -553,10 +515,30 @@ if (link->irq.setonboot) return_VALUE(0); + /* + * search for active IRQ in list of possible IRQs. + */ + for (i = 0; i < link->irq.possible_count; ++i) { + if (link->irq.active == link->irq.possible[i]) + break; + } + /* + * forget active IRQ that is not in possible list + */ + if (i == link->irq.possible_count) { + if (acpi_strict) + printk(KERN_WARNING PREFIX "_CRS %d not found" + " in _PRS\n", link->irq.active); + link->irq.active = 0; + } + + /* + * if active found, use it; else pick entry from end of possible list. + */ if (link->irq.active) { irq = link->irq.active; } else { - irq = link->irq.possible[0]; + irq = link->irq.possible[link->irq.possible_count - 1]; } if (acpi_irq_balance || !link->irq.active) { @@ -572,7 +554,8 @@ /* Attempt to enable the link device at this IRQ. */ if (acpi_pci_link_set(link, irq)) { - printk(PREFIX "Unable to set IRQ for %s [%s] (likely buggy ACPI BIOS). Aborting ACPI-based IRQ routing. Try pci=noacpi or acpi=off\n", + printk(PREFIX "Unable to set IRQ for %s [%s] (likely buggy ACPI BIOS).\n" + "Try pci=noacpi or acpi=off\n", acpi_device_name(link->device), acpi_device_bid(link->device)); return_VALUE(-ENODEV); @@ -624,7 +607,7 @@ return_VALUE(0); if (!link->irq.active) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link disabled\n")); + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); return_VALUE(0); } @@ -670,7 +653,6 @@ /* query and set link->irq.active */ acpi_pci_link_get_current(link); -//#ifdef CONFIG_ACPI_DEBUG printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device), acpi_device_bid(device)); for (i = 0; i < link->irq.possible_count; i++) { @@ -681,14 +663,25 @@ else printk(" %d", link->irq.possible[i]); } - printk(")\n"); -//#endif /* CONFIG_ACPI_DEBUG */ + + printk(")"); + + if (!found) + printk(" *%d", link->irq.active); + + if(!link->device->status.enabled) + printk(", disabled."); + + printk("\n"); /* TBD: Acquire/release lock */ list_add_tail(&link->node, &acpi_link.entries); acpi_link.count++; end: + /* disable all links -- to be activated on use */ + acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); + if (result) kfree(link); diff -urN linux-2.4.26/drivers/acpi/pci_root.c linux-2.4.27/drivers/acpi/pci_root.c --- linux-2.4.26/drivers/acpi/pci_root.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/pci_root.c 2004-08-07 16:26:04.655346819 -0700 @@ -112,12 +112,47 @@ } } +static acpi_status +get_root_bridge_busnr_callback (struct acpi_resource *resource, void *data) +{ + int *busnr = (int *)data; + struct acpi_resource_address64 address; + + if (resource->id != ACPI_RSTYPE_ADDRESS16 && + resource->id != ACPI_RSTYPE_ADDRESS32 && + resource->id != ACPI_RSTYPE_ADDRESS64) + return AE_OK; + + acpi_resource_to_address64(resource, &address); + if ((address.address_length > 0) && + (address.resource_type == ACPI_BUS_NUMBER_RANGE)) + *busnr = address.min_address_range; + + return AE_OK; +} + +static acpi_status +try_get_root_bridge_busnr(acpi_handle handle, int *busnum) +{ + acpi_status status; + + *busnum = -1; + status = acpi_walk_resources(handle, METHOD_NAME__CRS, get_root_bridge_busnr_callback, busnum); + if (ACPI_FAILURE(status)) + return status; + /* Check if we really get a bus number from _CRS */ + if (*busnum == -1) + return AE_ERROR; + return AE_OK; +} + static int acpi_pci_root_add ( struct acpi_device *device) { int result = 0; struct acpi_pci_root *root = NULL; + struct acpi_pci_root *tmp; acpi_status status = AE_OK; unsigned long value = 0; acpi_handle handle = NULL; @@ -152,8 +187,6 @@ switch (status) { case AE_OK: root->id.segment = (u16) value; - printk("_SEG exists! Unsupported. Abort.\n"); - BUG(); break; case AE_NOT_FOUND: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -187,6 +220,27 @@ goto end; } + /* Some systems have wrong _BBN */ + list_for_each_entry(tmp, &acpi_pci_roots, node) { + if ((tmp->id.segment == root->id.segment) + && (tmp->id.bus == root->id.bus)) { + int bus = 0; + acpi_status status; + + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Wrong _BBN value, please reboot and using option 'pci=noacpi'\n")); + + status = try_get_root_bridge_busnr(root->handle, &bus); + if (ACPI_FAILURE(status)) + break; + if (bus != root->id.bus) { + printk(KERN_INFO PREFIX "PCI _CRS %d overrides _BBN 0\n", bus); + root->id.bus = bus; + } + break; + } + } + /* * Device & Function * ----------------- @@ -213,7 +267,12 @@ * PCI namespace does not get created until this call is made (and * thus the root bridge's pci_dev does not exist). */ +#ifdef CONFIG_X86 root->bus = pcibios_scan_root(root->id.bus); +#else + root->bus = pcibios_scan_root(root->handle, + root->id.segment, root->id.bus); +#endif if (!root->bus) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Bus %02x:%02x not present in PCI namespace\n", diff -urN linux-2.4.26/drivers/acpi/power.c linux-2.4.27/drivers/acpi/power.c --- linux-2.4.26/drivers/acpi/power.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/power.c 2004-08-07 16:26:04.656346860 -0700 @@ -469,6 +469,8 @@ ACPI_FUNCTION_TRACE("acpi_power_remove_fs"); if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_POWER_FILE_STATUS, + acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), acpi_power_dir); acpi_device_dir(device) = NULL; } diff -urN linux-2.4.26/drivers/acpi/processor.c linux-2.4.27/drivers/acpi/processor.c --- linux-2.4.26/drivers/acpi/processor.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/acpi/processor.c 2004-08-07 16:26:04.657346901 -0700 @@ -2011,6 +2011,7 @@ if (!acpi_device_dir(device)) return_VALUE(-ENODEV); } + acpi_device_dir(device)->owner = THIS_MODULE; /* 'info' [R] */ entry = create_proc_entry(ACPI_PROCESSOR_FILE_INFO, @@ -2022,6 +2023,7 @@ else { entry->read_proc = acpi_processor_read_info; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'power' [R] */ @@ -2034,6 +2036,7 @@ else { entry->read_proc = acpi_processor_read_power; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'performance' [R/W] */ @@ -2047,6 +2050,7 @@ entry->read_proc = acpi_processor_read_performance; entry->write_proc = acpi_processor_write_performance; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'throttling' [R/W] */ @@ -2060,6 +2064,7 @@ entry->read_proc = acpi_processor_read_throttling; entry->write_proc = acpi_processor_write_throttling; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'limit' [R/W] */ @@ -2073,6 +2078,7 @@ entry->read_proc = acpi_processor_read_limit; entry->write_proc = acpi_processor_write_limit; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } return_VALUE(0); @@ -2349,6 +2355,7 @@ acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); if (!acpi_processor_dir) return_VALUE(-ENODEV); + acpi_processor_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_processor_driver); if (result < 0) { diff -urN linux-2.4.26/drivers/acpi/tables.c linux-2.4.27/drivers/acpi/tables.c --- linux-2.4.26/drivers/acpi/tables.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/tables.c 2004-08-07 16:26:04.658346942 -0700 @@ -58,6 +58,7 @@ [ACPI_SSDT] = "SSDT", [ACPI_SPMI] = "SPMI", [ACPI_HPET] = "HPET", + [ACPI_MCFG] = "MCFG", }; static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" }; diff -urN linux-2.4.26/drivers/acpi/thermal.c linux-2.4.27/drivers/acpi/thermal.c --- linux-2.4.26/drivers/acpi/thermal.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/acpi/thermal.c 2004-08-07 16:26:04.659346983 -0700 @@ -227,6 +227,13 @@ status = acpi_get_handle(tz->handle, "_SCP", &handle); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); + status = acpi_get_handle(tz->handle, "_PSV", &handle); + if(!ACPI_FAILURE(status)) { + tz->cooling_mode = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n", + mode?"passive":"active")); + return_VALUE(0); + } return_VALUE(-ENODEV); } @@ -1051,6 +1058,7 @@ acpi_thermal_dir); if (!acpi_device_dir(device)) return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; } /* 'state' [R] */ @@ -1063,6 +1071,7 @@ else { entry->read_proc = acpi_thermal_read_state; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'temperature' [R] */ @@ -1075,6 +1084,7 @@ else { entry->read_proc = acpi_thermal_read_temperature; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'trip_points' [R/W] */ @@ -1088,6 +1098,7 @@ entry->read_proc = acpi_thermal_read_trip_points; entry->write_proc = acpi_thermal_write_trip_points; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'cooling_mode' [R/W] */ @@ -1101,6 +1112,7 @@ entry->read_proc = acpi_thermal_read_cooling_mode; entry->write_proc = acpi_thermal_write_cooling_mode; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } /* 'polling_frequency' [R/W] */ @@ -1114,6 +1126,7 @@ entry->read_proc = acpi_thermal_read_polling; entry->write_proc = acpi_thermal_write_polling; entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; } return_VALUE(0); @@ -1127,6 +1140,16 @@ ACPI_FUNCTION_TRACE("acpi_thermal_remove_fs"); if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_STATE, + acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir); acpi_device_dir(device) = NULL; } @@ -1332,6 +1355,7 @@ acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); if (!acpi_thermal_dir) return_VALUE(-ENODEV); + acpi_thermal_dir->owner = THIS_MODULE; result = acpi_bus_register_driver(&acpi_thermal_driver); if (result < 0) { diff -urN linux-2.4.26/drivers/acpi/toshiba_acpi.c linux-2.4.27/drivers/acpi/toshiba_acpi.c --- linux-2.4.26/drivers/acpi/toshiba_acpi.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/acpi/toshiba_acpi.c 2004-08-07 16:26:04.660347024 -0700 @@ -503,6 +503,8 @@ proc = create_proc_read_entry(item->name, S_IFREG | S_IRUGO | S_IWUSR, toshiba_proc_dir, (read_proc_t*)dispatch_read, item); + if (proc) + proc->owner = THIS_MODULE; if (proc && item->write_func) proc->write_proc = (write_proc_t*)dispatch_write; } @@ -526,6 +528,8 @@ acpi_status status = AE_OK; u32 hci_result; + if (acpi_disabled) + return -ENODEV; /* simple device detection: look for HCI method */ if (is_valid_acpi_path(METHOD_HCI_1)) method_hci = METHOD_HCI_1; @@ -548,6 +552,7 @@ if (!toshiba_proc_dir) { status = AE_ERROR; } else { + toshiba_proc_dir->owner = THIS_MODULE; status = add_device(); if (ACPI_FAILURE(status)) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); diff -urN linux-2.4.26/drivers/atm/Config.in linux-2.4.27/drivers/atm/Config.in --- linux-2.4.26/drivers/atm/Config.in 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.27/drivers/atm/Config.in 2004-08-07 16:26:04.660347024 -0700 @@ -83,6 +83,7 @@ fi if [ "$CONFIG_ATM_FORE200E_PCA" = "y" -o "$CONFIG_ATM_FORE200E_SBA" = "y" ]; \ then + bool ' Defer interrupt work to a tasklet' CONFIG_ATM_FORE200E_USE_TASKLET int ' Maximum number of tx retries' CONFIG_ATM_FORE200E_TX_RETRY 16 int ' Debugging level (0-3)' CONFIG_ATM_FORE200E_DEBUG 0 if [ "$CONFIG_ATM_FORE200E_MAYBE" = "y" ]; then diff -urN linux-2.4.26/drivers/atm/fore200e.c linux-2.4.27/drivers/atm/fore200e.c --- linux-2.4.26/drivers/atm/fore200e.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/drivers/atm/fore200e.c 2004-08-07 16:26:04.666347271 -0700 @@ -2,7 +2,7 @@ $Id: fore200e.c,v 1.5 2000/04/14 10:10:34 davem Exp $ A FORE Systems 200E-series driver for ATM on Linux. - Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2000. + Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003. Based on the PCA-200E driver from Uwe Dannowski (Uwe.Dannowski@inf.tu-dresden.de). @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include #include #include @@ -46,7 +48,6 @@ #include #include #include -#include #ifdef CONFIG_ATM_FORE200E_SBA #include @@ -56,25 +57,33 @@ #include #endif -#include +#if defined(CONFIG_ATM_FORE200E_USE_TASKLET) /* defer interrupt work to a tasklet */ +#define FORE200E_USE_TASKLET +#endif -#include "fore200e.h" -#include "suni.h" +#if 0 /* enable the debugging code of the buffer supply queues */ +#define FORE200E_BSQ_DEBUG +#endif -#if 1 /* ensure correct handling of 52-byte AAL0 SDUs used by atmdump-like apps */ +#if 1 /* ensure correct handling of 52-byte AAL0 SDUs expected by atmdump-like apps */ #define FORE200E_52BYTE_AAL0_SDU #endif -#define FORE200E_VERSION "0.2d" +#include "fore200e.h" +#include "suni.h" +#define FORE200E_VERSION "0.3e" #define FORE200E "fore200e: " +#if 0 /* override .config */ +#define CONFIG_ATM_FORE200E_DEBUG 1 +#endif #if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG > 0) #define DPRINTK(level, format, args...) do { if (CONFIG_ATM_FORE200E_DEBUG >= (level)) \ - printk(FORE200E format, ##args); } while(0) + printk(FORE200E format, ##args); } while (0) #else -#define DPRINTK(level, format, args...) while(0) +#define DPRINTK(level, format, args...) do {} while (0) #endif @@ -85,18 +94,28 @@ #define FORE200E_INDEX(virt_addr, type, index) (&((type *)(virt_addr))[ index ]) -#define FORE200E_NEXT_ENTRY(index, modulo) (index = ++(index) % (modulo)) +#define FORE200E_NEXT_ENTRY(index, modulo) (index = ++(index) % (modulo)) #define MSECS(ms) (((ms)*HZ/1000)+1) +#if 1 +#define ASSERT(expr) if (!(expr)) { \ + printk(FORE200E "assertion failed! %s[%d]: %s\n", \ + __FUNCTION__, __LINE__, #expr); \ + panic(FORE200E "%s", __FUNCTION__); \ + } +#else +#define ASSERT(expr) do {} while (0) +#endif + + extern const struct atmdev_ops fore200e_ops; extern const struct fore200e_bus fore200e_bus[]; static struct fore200e* fore200e_boards = NULL; - #ifdef MODULE MODULE_AUTHOR("Christophe Lizzi - credits to Uwe Dannowski and Heikki Vatiainen"); MODULE_DESCRIPTION("FORE Systems 200E-series ATM driver - version " FORE200E_VERSION); @@ -225,29 +244,6 @@ } - -#if 0 /* currently unused */ -static int -fore200e_checkup(struct fore200e* fore200e) -{ - u32 hb1, hb2; - - hb1 = fore200e->bus->read(&fore200e->cp_queues->heartbeat); - fore200e_spin(10); - hb2 = fore200e->bus->read(&fore200e->cp_queues->heartbeat); - - if (hb2 <= hb1) { - printk(FORE200E "device %s heartbeat is not counting upwards, hb1 = %x; hb2 = %x\n", - fore200e->name, hb1, hb2); - return -EIO; - } - printk(FORE200E "device %s heartbeat is ok\n", fore200e->name); - - return 0; -} -#endif - - static void fore200e_spin(int msecs) { @@ -444,7 +440,6 @@ } - #ifdef CONFIG_ATM_FORE200E_PCA static u32 fore200e_pca_read(volatile u32* addr) @@ -501,20 +496,16 @@ fore200e_pca_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, int size, int nbr, int alignment) { -#if defined(__sparc_v9__) /* returned chunks are page-aligned */ + chunk->alloc_size = size * nbr; chunk->alloc_addr = pci_alloc_consistent((struct pci_dev*)fore200e->bus_dev, chunk->alloc_size, &chunk->dma_addr); - if (chunk->alloc_addr == NULL || chunk->dma_addr == 0) + if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0)) return -ENOMEM; chunk->align_addr = chunk->alloc_addr; -#else - if (fore200e_chunk_alloc(fore200e, chunk, size * nbr, alignment, FORE200E_DMA_BIDIRECTIONAL) < 0) - return -ENOMEM; -#endif return 0; } @@ -525,14 +516,10 @@ static void fore200e_pca_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk) { -#if defined(__sparc_v9__) pci_free_consistent((struct pci_dev*)fore200e->bus_dev, chunk->alloc_size, chunk->alloc_addr, chunk->dma_addr); -#else - fore200e_chunk_free(fore200e, chunk); -#endif } @@ -540,7 +527,15 @@ fore200e_pca_irq_check(struct fore200e* fore200e) { /* this is a 1 bit register */ - return readl(fore200e->regs.pca.psr); + int irq_posted = readl(fore200e->regs.pca.psr); + +#if defined(CONFIG_ATM_FORE200E_DEBUG) && (CONFIG_ATM_FORE200E_DEBUG == 2) + if (irq_posted && (readl(fore200e->regs.pca.hcr) & PCA200E_HCR_OUTFULL)) { + DPRINTK(2,"FIFO OUT full, device %d\n", fore200e->atm_dev->number); + } +#endif + + return irq_posted; } @@ -574,7 +569,7 @@ DPRINTK(1, "device %s mapped to 0x%p\n", fore200e->name, fore200e->virt_base); - /* gain access to the PCA-200E specific registers */ + /* gain access to the PCA specific registers */ fore200e->regs.pca.hcr = (u32*)(fore200e->virt_base + PCA200E_HCR_OFFSET); fore200e->regs.pca.imr = (u32*)(fore200e->virt_base + PCA200E_IMR_OFFSET); fore200e->regs.pca.psr = (u32*)(fore200e->virt_base + PCA200E_PSR_OFFSET); @@ -589,8 +584,6 @@ { DPRINTK(2, "device %s being unmapped from memory\n", fore200e->name); - /* XXX iounmap() does nothing on PowerPC (at least in 2.2.12 and 2.3.41), - this leads to a kernel panic if the module is loaded and unloaded several times */ if (fore200e->virt_base != NULL) iounmap(fore200e->virt_base); } @@ -600,7 +593,7 @@ fore200e_pca_configure(struct fore200e* fore200e) { struct pci_dev* pci_dev = (struct pci_dev*)fore200e->bus_dev; - u8 master_ctrl; + u8 master_ctrl, latency; DPRINTK(2, "device %s being configured\n", fore200e->name); @@ -609,21 +602,29 @@ return -EIO; } - pci_read_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, &master_ctrl); + pci_read_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, &master_ctrl); master_ctrl = master_ctrl -#if 0 - | PCA200E_CTRL_DIS_CACHE_RD - | PCA200E_CTRL_DIS_WRT_INVAL -#endif #if defined(__BIG_ENDIAN) /* request the PCA board to convert the endianess of slave RAM accesses */ | PCA200E_CTRL_CONVERT_ENDIAN #endif +#if 0 + | PCA200E_CTRL_DIS_CACHE_RD + | PCA200E_CTRL_DIS_WRT_INVAL + | PCA200E_CTRL_ENA_CONT_REQ_MODE + | PCA200E_CTRL_2_CACHE_WRT_INVAL +#endif | PCA200E_CTRL_LARGE_PCI_BURSTS; pci_write_config_byte(pci_dev, PCA200E_PCI_MASTER_CTRL, master_ctrl); + /* raise latency from 32 (default) to 192, as this seems to prevent NIC + lockups (under heavy rx loads) due to continuous 'FIFO OUT full' condition. + this may impact the performances of other PCI devices on the same bus, though */ + latency = 192; + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); + fore200e->state = FORE200E_STATE_CONFIGURE; return 0; } @@ -657,11 +658,7 @@ fore200e->bus = bus; fore200e->bus_dev = pci_dev; fore200e->irq = pci_dev->irq; - fore200e->phys_base = pci_resource_start (pci_dev, 0); - -#if defined(__powerpc__) - fore200e->phys_base += KERNELBASE; -#endif + fore200e->phys_base = pci_resource_start(pci_dev, 0); sprintf(fore200e->name, "%s-%d", bus->model_name, index - 1); @@ -729,8 +726,6 @@ #endif /* CONFIG_ATM_FORE200E_PCA */ - - #ifdef CONFIG_ATM_FORE200E_SBA static u32 @@ -792,7 +787,7 @@ chunk->alloc_size, &chunk->dma_addr); - if (chunk->alloc_addr == NULL || chunk->dma_addr == 0) + if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0)) return -ENOMEM; chunk->align_addr = chunk->alloc_addr; @@ -851,8 +846,7 @@ struct sbus_dev* sbus_dev = (struct sbus_dev*)fore200e->bus_dev; unsigned int bursts; - /* gain access to the SBA-200E specific registers */ - + /* gain access to the SBA specific registers */ fore200e->regs.sba.hcr = (u32*)sbus_ioremap(&sbus_dev->resource[0], 0, SBA200E_HCR_LENGTH, "SBA HCR"); fore200e->regs.sba.bsr = (u32*)sbus_ioremap(&sbus_dev->resource[1], 0, SBA200E_BSR_LENGTH, "SBA BSR"); fore200e->regs.sba.isr = (u32*)sbus_ioremap(&sbus_dev->resource[2], 0, SBA200E_ISR_LENGTH, "SBA ISR"); @@ -873,17 +867,6 @@ if (sbus_can_dma_64bit(sbus_dev)) sbus_set_sbus64(sbus_dev, bursts); -#if 0 - if (bursts & DMA_BURST16) - fore200e->bus->write(SBA200E_BSR_BURST16, fore200e->regs.sba.bsr); - else - if (bursts & DMA_BURST8) - fore200e->bus->write(SBA200E_BSR_BURST8, fore200e->regs.sba.bsr); - else - if (bursts & DMA_BURST4) - fore200e->bus->write(SBA200E_BSR_BURST4, fore200e->regs.sba.bsr); -#endif - fore200e->state = FORE200E_STATE_MAP; return 0; } @@ -928,13 +911,11 @@ return NULL; found: -#if 1 if (sbus_dev->num_registers != 4) { printk(FORE200E "this %s device has %d instead of 4 registers\n", bus->model_name, sbus_dev->num_registers); return NULL; } -#endif fore200e = fore200e_kmalloc(sizeof(struct fore200e), GFP_KERNEL); if (fore200e == NULL) @@ -987,46 +968,143 @@ static void -fore200e_irq_tx(struct fore200e* fore200e) +fore200e_tx_irq(struct fore200e* fore200e) { - struct host_txq_entry* entry; - int i; - - entry = fore200e->host_txq.host_entry; + struct host_txq* txq = &fore200e->host_txq; + struct host_txq_entry* entry; + struct atm_vcc* vcc; + struct fore200e_vc_map* vc_map; - for (i = 0; i < QUEUE_SIZE_TX; i++) { + if (fore200e->host_txq.txing == 0) + return; + + for (;;) { + + entry = &txq->host_entry[ txq->tail ]; - if (*entry->status & STATUS_COMPLETE) { + if ((*entry->status & STATUS_COMPLETE) == 0) { + break; + } - DPRINTK(3, "TX COMPLETED: entry = %p, vcc = %p, skb = %p\n", entry, entry->vcc, entry->skb); + DPRINTK(3, "TX COMPLETED: entry = %p [tail = %d], vc_map = %p, skb = %p\n", + entry, txq->tail, entry->vc_map, entry->skb); - /* free copy of misaligned data */ - if (entry->data) - kfree(entry->data); + /* free copy of misaligned data */ + if (entry->data) + kfree(entry->data); + + /* remove DMA mapping */ + fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length, + FORE200E_DMA_TODEVICE); - /* remove DMA mapping */ - fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length, - FORE200E_DMA_TODEVICE); + vc_map = entry->vc_map; - /* notify tx completion */ - if (entry->vcc->pop) - entry->vcc->pop(entry->vcc, entry->skb); - else - dev_kfree_skb_irq(entry->skb); + /* vcc closed since the time the entry was submitted for tx? */ + if ((vc_map->vcc == NULL) || + (test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) { - /* check error condition */ - if (*entry->status & STATUS_ERROR) - atomic_inc(&entry->vcc->stats->tx_err); - else - atomic_inc(&entry->vcc->stats->tx); + DPRINTK(1, "no ready vcc found for PDU sent on device %d\n", + fore200e->atm_dev->number); - *entry->status = STATUS_FREE; - - fore200e->host_txq.txing--; + dev_kfree_skb_any(entry->skb); + } + else { + ASSERT(vc_map->vcc); + + /* vcc closed then immediately re-opened? */ + if (vc_map->incarn != entry->incarn) { + + /* when a vcc is closed, some PDUs may be still pending in the tx queue. + if the same vcc is immediately re-opened, those pending PDUs must + not be popped after the completion of their emission, as they refer + to the prior incarnation of that vcc. otherwise, vcc->sk->wmem_alloc + would be decremented by the size of the (unrelated) skb, possibly + leading to a negative sk->wmem_alloc count, ultimately freezing the vcc. + we thus bind the tx entry to the current incarnation of the vcc + when the entry is submitted for tx. When the tx later completes, + if the incarnation number of the tx entry does not match the one + of the vcc, then this implies that the vcc has been closed then re-opened. + we thus just drop the skb here. */ + + DPRINTK(1, "vcc closed-then-re-opened; dropping PDU sent on device %d\n", + fore200e->atm_dev->number); + + dev_kfree_skb_any(entry->skb); + } + else { + vcc = vc_map->vcc; + ASSERT(vcc); + + /* notify tx completion */ + if (vcc->pop) { + vcc->pop(vcc, entry->skb); + } + else { + dev_kfree_skb_any(entry->skb); + } +#if 1 + /* race fixed by the above incarnation mechanism, but... */ + if (atomic_read(&vcc->sk->wmem_alloc) < 0) { + atomic_set(&vcc->sk->wmem_alloc, 0); + } +#endif + /* check error condition */ + if (*entry->status & STATUS_ERROR) + atomic_inc(&vcc->stats->tx_err); + else + atomic_inc(&vcc->stats->tx); + } + } + + *entry->status = STATUS_FREE; + + fore200e->host_txq.txing--; + + FORE200E_NEXT_ENTRY(txq->tail, QUEUE_SIZE_TX); + } +} + + +#ifdef FORE200E_BSQ_DEBUG +int bsq_audit(int where, struct host_bsq* bsq, int scheme, int magn) +{ + struct buffer* buffer; + int count = 0; + + buffer = bsq->freebuf; + while (buffer) { + + if (buffer->supplied) { + printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld supplied but in free list!\n", + where, scheme, magn, buffer->index); + } + + if (buffer->magn != magn) { + printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected magn = %d\n", + where, scheme, magn, buffer->index, buffer->magn); + } + + if (buffer->scheme != scheme) { + printk(FORE200E "bsq_audit(%d): queue %d.%d, buffer %ld, unexpected scheme = %d\n", + where, scheme, magn, buffer->index, buffer->scheme); + } + + if ((buffer->index < 0) || (buffer->index >= fore200e_rx_buf_nbr[ scheme ][ magn ])) { + printk(FORE200E "bsq_audit(%d): queue %d.%d, out of range buffer index = %ld !\n", + where, scheme, magn, buffer->index); } - entry++; + + count++; + buffer = buffer->next; + } + + if (count != bsq->freebuf_count) { + printk(FORE200E "bsq_audit(%d): queue %d.%d, %d bufs in free list, but freebuf_count = %d\n", + where, scheme, magn, count, bsq->freebuf_count); } + return 0; } +#endif static void @@ -1043,28 +1121,42 @@ bsq = &fore200e->host_bsq[ scheme ][ magn ]; - if (fore200e_rx_buf_nbr[ scheme ][ magn ] - bsq->count > RBD_BLK_SIZE) { +#ifdef FORE200E_BSQ_DEBUG + bsq_audit(1, bsq, scheme, magn); +#endif + while (bsq->freebuf_count >= RBD_BLK_SIZE) { - DPRINTK(2, "supplying rx buffers to queue %d / %d, count = %d\n", - scheme, magn, bsq->count); + DPRINTK(2, "supplying %d rx buffers to queue %d / %d, freebuf_count = %d\n", + RBD_BLK_SIZE, scheme, magn, bsq->freebuf_count); entry = &bsq->host_entry[ bsq->head ]; - - FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS); for (i = 0; i < RBD_BLK_SIZE; i++) { - buffer = &bsq->buffer[ bsq->free ]; - - FORE200E_NEXT_ENTRY(bsq->free, fore200e_rx_buf_nbr[ scheme ][ magn ]); + /* take the first buffer in the free buffer list */ + buffer = bsq->freebuf; + if (!buffer) { + printk(FORE200E "no more free bufs in queue %d.%d, but freebuf_count = %d\n", + scheme, magn, bsq->freebuf_count); + return; + } + bsq->freebuf = buffer->next; +#ifdef FORE200E_BSQ_DEBUG + if (buffer->supplied) + printk(FORE200E "queue %d.%d, buffer %lu already supplied\n", + scheme, magn, buffer->index); + buffer->supplied = 1; +#endif entry->rbd_block->rbd[ i ].buffer_haddr = buffer->data.dma_addr; entry->rbd_block->rbd[ i ].handle = FORE200E_BUF2HDL(buffer); } - /* increase the number of supplied rx buffers */ - bsq->count += RBD_BLK_SIZE; - + FORE200E_NEXT_ENTRY(bsq->head, QUEUE_SIZE_BS); + + /* decrease accordingly the number of free rx buffers */ + bsq->freebuf_count -= RBD_BLK_SIZE; + *entry->status = STATUS_PENDING; fore200e->bus->write(entry->rbd_block_dma, &entry->cp_entry->rbd_block_haddr); } @@ -1073,33 +1165,9 @@ } - -static struct atm_vcc* -fore200e_find_vcc(struct fore200e* fore200e, struct rpd* rpd) -{ - struct sock *s; - struct atm_vcc* vcc; - - read_lock(&vcc_sklist_lock); - for(s = vcc_sklist; s; s = s->next) { - vcc = s->protinfo.af_atm; - if (vcc->dev != fore200e->atm_dev) - continue; - if (vcc->vpi == rpd->atm_header.vpi && vcc->vci == rpd->atm_header.vci) { - read_unlock(&vcc_sklist_lock); - return vcc; - } - } - read_unlock(&vcc_sklist_lock); - - return NULL; -} - - -static void -fore200e_push_rpd(struct fore200e* fore200e, struct rpd* rpd) +static int +fore200e_push_rpd(struct fore200e* fore200e, struct atm_vcc* vcc, struct rpd* rpd) { - struct atm_vcc* vcc; struct sk_buff* skb; struct buffer* buffer; struct fore200e_vcc* fore200e_vcc; @@ -1108,15 +1176,10 @@ u32 cell_header = 0; #endif - vcc = fore200e_find_vcc(fore200e, rpd); - if (vcc == NULL) { - - printk(FORE200E "no vcc found for PDU received on %d.%d.%d\n", - fore200e->atm_dev->number, rpd->atm_header.vpi, rpd->atm_header.vci); - return; - } - + ASSERT(vcc); + fore200e_vcc = FORE200E_VCC(vcc); + ASSERT(fore200e_vcc); #ifdef FORE200E_52BYTE_AAL0_SDU if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.rxtp.max_sdu == ATM_AAL0_SDU)) { @@ -1136,10 +1199,10 @@ skb = alloc_skb(pdu_len, GFP_ATOMIC); if (skb == NULL) { - - printk(FORE200E "unable to alloc new skb, rx PDU length = %d\n", pdu_len); + DPRINTK(2, "unable to alloc new skb, rx PDU length = %d\n", pdu_len); + atomic_inc(&vcc->stats->rx_drop); - return; + return -ENOMEM; } skb->stamp = xtime; @@ -1161,13 +1224,14 @@ memcpy(skb_put(skb, rpd->rsd[ i ].length), buffer->data.align_addr, rpd->rsd[ i ].length); } - + DPRINTK(3, "rx skb: len = %d, truesize = %d\n", skb->len, skb->truesize); if (pdu_len < fore200e_vcc->rx_min_pdu) fore200e_vcc->rx_min_pdu = pdu_len; if (pdu_len > fore200e_vcc->rx_max_pdu) fore200e_vcc->rx_max_pdu = pdu_len; + fore200e_vcc->rx_pdu++; /* push PDU */ if (atm_charge(vcc, skb->truesize) == 0) { @@ -1175,37 +1239,63 @@ DPRINTK(2, "receive buffers saturated for %d.%d.%d - PDU dropped\n", vcc->itf, vcc->vpi, vcc->vci); - dev_kfree_skb_irq(skb); - return; + dev_kfree_skb_any(skb); + + atomic_inc(&vcc->stats->rx_drop); + return -ENOMEM; } + ASSERT(atomic_read(&vcc->sk->wmem_alloc) >= 0); + vcc->push(vcc, skb); atomic_inc(&vcc->stats->rx); + + ASSERT(atomic_read(&vcc->sk->wmem_alloc) >= 0); + + return 0; } static void fore200e_collect_rpd(struct fore200e* fore200e, struct rpd* rpd) { - struct buffer* buffer; - int i; + struct host_bsq* bsq; + struct buffer* buffer; + int i; for (i = 0; i < rpd->nseg; i++) { /* rebuild rx buffer address from rsd handle */ buffer = FORE200E_HDL2BUF(rpd->rsd[ i ].handle); - /* decrease the number of supplied rx buffers */ - fore200e->host_bsq[ buffer->scheme ][ buffer->magn ].count--; + bsq = &fore200e->host_bsq[ buffer->scheme ][ buffer->magn ]; + +#ifdef FORE200E_BSQ_DEBUG + bsq_audit(2, bsq, buffer->scheme, buffer->magn); + + if (buffer->supplied == 0) + printk(FORE200E "queue %d.%d, buffer %ld was not supplied\n", + buffer->scheme, buffer->magn, buffer->index); + buffer->supplied = 0; +#endif + + /* re-insert the buffer into the free buffer list */ + buffer->next = bsq->freebuf; + bsq->freebuf = buffer; + + /* then increment the number of free rx buffers */ + bsq->freebuf_count++; } } static void -fore200e_irq_rx(struct fore200e* fore200e) +fore200e_rx_irq(struct fore200e* fore200e) { - struct host_rxq* rxq = &fore200e->host_rxq; - struct host_rxq_entry* entry; + struct host_rxq* rxq = &fore200e->host_rxq; + struct host_rxq_entry* entry; + struct atm_vcc* vcc; + struct fore200e_vc_map* vc_map; for (;;) { @@ -1215,28 +1305,61 @@ if ((*entry->status & STATUS_COMPLETE) == 0) break; - FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX); + vc_map = FORE200E_VC_MAP(fore200e, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); - if ((*entry->status & STATUS_ERROR) == 0) { + if ((vc_map->vcc == NULL) || + (test_bit(ATM_VF_READY, &vc_map->vcc->flags) == 0)) { - fore200e_push_rpd(fore200e, entry->rpd); + DPRINTK(1, "no ready VC found for PDU received on %d.%d.%d\n", + fore200e->atm_dev->number, + entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); } else { - printk(FORE200E "damaged PDU on %d.%d.%d\n", - fore200e->atm_dev->number, entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); + vcc = vc_map->vcc; + ASSERT(vcc); + + if ((*entry->status & STATUS_ERROR) == 0) { + + fore200e_push_rpd(fore200e, vcc, entry->rpd); + } + else { + DPRINTK(2, "damaged PDU on %d.%d.%d\n", + fore200e->atm_dev->number, + entry->rpd->atm_header.vpi, entry->rpd->atm_header.vci); + atomic_inc(&vcc->stats->rx_err); + } } - fore200e_collect_rpd(fore200e, entry->rpd); + FORE200E_NEXT_ENTRY(rxq->head, QUEUE_SIZE_RX); - fore200e_supply(fore200e); + fore200e_collect_rpd(fore200e, entry->rpd); /* rewrite the rpd address to ack the received PDU */ fore200e->bus->write(entry->rpd_dma, &entry->cp_entry->rpd_haddr); *entry->status = STATUS_FREE; + + fore200e_supply(fore200e); } } +#ifndef FORE200E_USE_TASKLET +static void +fore200e_irq(struct fore200e* fore200e) +{ + unsigned long flags; + + spin_lock_irqsave(&fore200e->q_lock, flags); + fore200e_rx_irq(fore200e); + spin_unlock_irqrestore(&fore200e->q_lock, flags); + + spin_lock_irqsave(&fore200e->q_lock, flags); + fore200e_tx_irq(fore200e); + spin_unlock_irqrestore(&fore200e->q_lock, flags); +} +#endif + + static void fore200e_interrupt(int irq, void* dev, struct pt_regs* regs) { @@ -1244,57 +1367,65 @@ if (fore200e->bus->irq_check(fore200e) == 0) { - DPRINTK(3, "unexpected interrupt on device %c\n", fore200e->name[9]); + DPRINTK(3, "interrupt NOT triggered by device %d\n", fore200e->atm_dev->number); return; } - DPRINTK(3, "valid interrupt on device %c\n", fore200e->name[9]); + DPRINTK(3, "interrupt triggered by device %d\n", fore200e->atm_dev->number); - tasklet_schedule(&fore200e->tasklet); +#ifdef FORE200E_USE_TASKLET + tasklet_schedule(&fore200e->tx_tasklet); + tasklet_schedule(&fore200e->rx_tasklet); +#else + fore200e_irq(fore200e); +#endif fore200e->bus->irq_ack(fore200e); } +#ifdef FORE200E_USE_TASKLET static void -fore200e_tasklet(unsigned long data) +fore200e_tx_tasklet(unsigned long data) { struct fore200e* fore200e = (struct fore200e*) data; + unsigned long flags; - fore200e_irq_rx(fore200e); - - if (fore200e->host_txq.txing) - fore200e_irq_tx(fore200e); + DPRINTK(3, "tx tasklet scheduled for device %d\n", fore200e->atm_dev->number); + + spin_lock_irqsave(&fore200e->q_lock, flags); + fore200e_tx_irq(fore200e); + spin_unlock_irqrestore(&fore200e->q_lock, flags); } +static void +fore200e_rx_tasklet(unsigned long data) +{ + struct fore200e* fore200e = (struct fore200e*) data; + unsigned long flags; + + DPRINTK(3, "rx tasklet scheduled for device %d\n", fore200e->atm_dev->number); + + spin_lock_irqsave(&fore200e->q_lock, flags); + fore200e_rx_irq((struct fore200e*) data); + spin_unlock_irqrestore(&fore200e->q_lock, flags); +} +#endif + static int fore200e_select_scheme(struct atm_vcc* vcc) { - int scheme; - -#if 1 - /* fairly balance VCs over (identical) buffer schemes */ - scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO; -#else - /* bit 7 of VPI magically selects the second buffer scheme */ - if (vcc->vpi & (1<<7)) { - vcc->vpi &= ((1<<7) - 1); /* reset the magic bit */ - scheme = BUFFER_SCHEME_TWO; - } - else { - scheme = BUFFER_SCHEME_ONE; - } -#endif + /* fairly balance the VCs over (identical) buffer schemes */ + int scheme = vcc->vci % 2 ? BUFFER_SCHEME_ONE : BUFFER_SCHEME_TWO; - DPRINTK(1, "vpvc %d.%d.%d uses the %s buffer scheme\n", - vcc->itf, vcc->vpi, vcc->vci, scheme == BUFFER_SCHEME_ONE ? "first" : "second"); + DPRINTK(1, "VC %d.%d.%d uses buffer scheme %d\n", + vcc->itf, vcc->vpi, vcc->vci, scheme); return scheme; } - static int fore200e_activate_vcin(struct fore200e* fore200e, int activate, struct atm_vcc* vcc, int mtu) { @@ -1331,7 +1462,7 @@ #ifdef FORE200E_52BYTE_AAL0_SDU mtu = 48; #endif - /* the MTU is unused by the cp, except in the case of AAL0 */ + /* the MTU is not used by the cp, except in the case of AAL0 */ fore200e->bus->write(mtu, &entry->cp_entry->cmd.activate_block.mtu); fore200e->bus->write(*(u32*)&vpvc, (u32*)&entry->cp_entry->cmd.activate_block.vpvc); fore200e->bus->write(*(u32*)&activ_opcode, (u32*)&entry->cp_entry->cmd.activate_block.opcode); @@ -1346,13 +1477,13 @@ *entry->status = STATUS_FREE; if (ok == 0) { - printk(FORE200E "unable to %s vpvc %d.%d on device %s\n", - activate ? "open" : "close", vcc->vpi, vcc->vci, fore200e->name); + printk(FORE200E "unable to %s VC %d.%d.%d\n", + activate ? "open" : "close", vcc->itf, vcc->vpi, vcc->vci); return -EIO; } - DPRINTK(1, "vpvc %d.%d %sed on device %s\n", vcc->vpi, vcc->vci, - activate ? "open" : "clos", fore200e->name); + DPRINTK(1, "VC %d.%d.%d %sed\n", vcc->itf, vcc->vpi, vcc->vci, + activate ? "open" : "clos"); return 0; } @@ -1410,7 +1541,7 @@ { if (qos->txtp.max_pcr < ATM_OC3_PCR) { - /* compute the data cells to idle cells ratio from the PCR */ + /* compute the data cells to idle cells ratio from the tx PCR */ rate->data_cells = qos->txtp.max_pcr * FORE200E_MAX_BACK2BACK_CELLS / ATM_OC3_PCR; rate->idle_cells = FORE200E_MAX_BACK2BACK_CELLS - rate->data_cells; } @@ -1424,21 +1555,38 @@ static int fore200e_open(struct atm_vcc *vcc, short vpi, int vci) { - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); - struct fore200e_vcc* fore200e_vcc; - - /* find a free VPI/VCI */ + struct fore200e* fore200e = FORE200E_DEV(vcc->dev); + struct fore200e_vcc* fore200e_vcc; + struct fore200e_vc_map* vc_map; + unsigned long flags; + fore200e_walk_vccs(vcc, &vpi, &vci); + + ASSERT((vpi >= 0) && (vpi < 1<= 0) && (vci < 1<vpi = vpi; - vcc->vci = vci; + spin_lock_irqsave(&fore200e->q_lock, flags); - /* ressource checking only? */ - if (vci == ATM_VCI_UNSPEC || vpi == ATM_VPI_UNSPEC) - return 0; + vc_map = FORE200E_VC_MAP(fore200e, vpi, vci); + if (vc_map->vcc) { - set_bit(ATM_VF_ADDR, &vcc->flags); - vcc->itf = vcc->dev->number; + spin_unlock_irqrestore(&fore200e->q_lock, flags); + + printk(FORE200E "VC %d.%d.%d already in use\n", + fore200e->atm_dev->number, vpi, vci); + + return -EINVAL; + } + + vc_map->vcc = vcc; + + spin_unlock_irqrestore(&fore200e->q_lock, flags); + + fore200e_vcc = fore200e_kmalloc(sizeof(struct fore200e_vcc), GFP_ATOMIC); + if (fore200e_vcc == NULL) { + vc_map->vcc = NULL; + return -ENOMEM; + } DPRINTK(2, "opening %d.%d.%d:%d QoS = (tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; " "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d)\n", @@ -1448,44 +1596,52 @@ fore200e_traffic_class[ vcc->qos.rxtp.traffic_class ], vcc->qos.rxtp.min_pcr, vcc->qos.rxtp.max_pcr, vcc->qos.rxtp.max_cdv, vcc->qos.rxtp.max_sdu); + /* pseudo-CBR bandwidth requested? */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { down(&fore200e->rate_sf); if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) { up(&fore200e->rate_sf); + + fore200e_kfree(fore200e_vcc); + vc_map->vcc = NULL; return -EAGAIN; } - /* reserving the pseudo-CBR bandwidth at this point grants us - to reduce the length of the critical section protected - by 'rate_sf'. in counterpart, we have to reset the available - bandwidth if we later encounter an error */ + /* reserve bandwidth */ fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr; up(&fore200e->rate_sf); } - - fore200e_vcc = fore200e_kmalloc(sizeof(struct fore200e_vcc), GFP_KERNEL); - if (fore200e_vcc == NULL) { - down(&fore200e->rate_sf); - fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - up(&fore200e->rate_sf); - return -ENOMEM; - } + + vcc->itf = vcc->dev->number; + vcc->vpi = vpi; + vcc->vci = vci; + + set_bit(ATM_VF_PARTIAL,&vcc->flags); + set_bit(ATM_VF_ADDR, &vcc->flags); FORE200E_VCC(vcc) = fore200e_vcc; - + if (fore200e_activate_vcin(fore200e, 1, vcc, vcc->qos.rxtp.max_sdu) < 0) { - kfree(fore200e_vcc); - down(&fore200e->rate_sf); + + vc_map->vcc = NULL; + + clear_bit(ATM_VF_ADDR, &vcc->flags); + clear_bit(ATM_VF_PARTIAL,&vcc->flags); + + FORE200E_VCC(vcc) = NULL; + fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - up(&fore200e->rate_sf); - return -EBUSY; + + fore200e_kfree(fore200e_vcc); + return -EINVAL; } /* compute rate control parameters */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { fore200e_rate_ctrl(&vcc->qos, &fore200e_vcc->rate); + set_bit(ATM_VF_HASQOS, &vcc->flags); DPRINTK(3, "tx on %d.%d.%d:%d, tx PCR = %d, rx PCR = %d, data_cells = %u, idle_cells = %u\n", vcc->itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), @@ -1493,57 +1649,99 @@ fore200e_vcc->rate.data_cells, fore200e_vcc->rate.idle_cells); } - fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = 65536; + fore200e_vcc->tx_min_pdu = fore200e_vcc->rx_min_pdu = MAX_PDU_SIZE + 1; fore200e_vcc->tx_max_pdu = fore200e_vcc->rx_max_pdu = 0; - + fore200e_vcc->tx_pdu = fore200e_vcc->rx_pdu = 0; + + /* new incarnation of the vcc */ + vc_map->incarn = ++fore200e->incarn_count; + + /* VC unusable before this flag is set */ set_bit(ATM_VF_READY, &vcc->flags); + return 0; } - static void fore200e_close(struct atm_vcc* vcc) { - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); - + struct fore200e* fore200e = FORE200E_DEV(vcc->dev); + struct fore200e_vcc* fore200e_vcc; + struct fore200e_vc_map* vc_map; + unsigned long flags; + + ASSERT(vcc); + ASSERT((vcc->vpi >= 0) && (vcc->vpi < 1<vci >= 0) && (vcc->vci < 1<itf, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal)); - + + clear_bit(ATM_VF_READY, &vcc->flags); + fore200e_activate_vcin(fore200e, 0, vcc, 0); - - kfree(FORE200E_VCC(vcc)); - + + spin_lock_irqsave(&fore200e->q_lock, flags); + + vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci); + + /* the vc is no longer considered as "in use" by fore200e_open() */ + vc_map->vcc = NULL; + + vcc->itf = vcc->vci = vcc->vpi = 0; + + fore200e_vcc = FORE200E_VCC(vcc); + FORE200E_VCC(vcc) = NULL; + + spin_unlock_irqrestore(&fore200e->q_lock, flags); + + /* release reserved bandwidth, if any */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { + down(&fore200e->rate_sf); fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; up(&fore200e->rate_sf); - } - clear_bit(ATM_VF_READY, &vcc->flags); -} + clear_bit(ATM_VF_HASQOS, &vcc->flags); + } + clear_bit(ATM_VF_ADDR, &vcc->flags); + clear_bit(ATM_VF_PARTIAL,&vcc->flags); -#if 0 -#define FORE200E_SYNC_SEND /* wait tx completion before returning */ -#endif + ASSERT(fore200e_vcc); + fore200e_kfree(fore200e_vcc); +} static int fore200e_send(struct atm_vcc *vcc, struct sk_buff *skb) { - struct fore200e* fore200e = FORE200E_DEV(vcc->dev); - struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc); - struct host_txq* txq = &fore200e->host_txq; - struct host_txq_entry* entry; - struct tpd* tpd; - struct tpd_haddr tpd_haddr; - //unsigned long flags; - int retry = CONFIG_ATM_FORE200E_TX_RETRY; - int tx_copy = 0; - int tx_len = skb->len; - u32* cell_header = NULL; - unsigned char* skb_data; - int skb_len; + struct fore200e* fore200e = FORE200E_DEV(vcc->dev); + struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc); + struct fore200e_vc_map* vc_map; + struct host_txq* txq = &fore200e->host_txq; + struct host_txq_entry* entry; + struct tpd* tpd; + struct tpd_haddr tpd_haddr; + int retry = CONFIG_ATM_FORE200E_TX_RETRY; + int tx_copy = 0; + int tx_len = skb->len; + u32* cell_header = NULL; + unsigned char* skb_data; + int skb_len; + unsigned char* data; + unsigned long flags; + + ASSERT(vcc); + ASSERT(atomic_read(&vcc->sk->wmem_alloc) >= 0); + ASSERT(fore200e); + ASSERT(fore200e_vcc); + + if (!test_bit(ATM_VF_READY, &vcc->flags)) { + DPRINTK(1, "VC %d.%d.%d not ready for tx\n", vcc->itf, vcc->vpi, vcc->vpi); + dev_kfree_skb_any(skb); + return -EINVAL; + } #ifdef FORE200E_52BYTE_AAL0_SDU if ((vcc->qos.aal == ATM_AAL0) && (vcc->qos.txtp.max_sdu == ATM_AAL0_SDU)) { @@ -1551,7 +1749,7 @@ skb_data = skb->data + 4; /* skip 4-byte cell header */ skb_len = tx_len = skb->len - 4; - DPRINTK(3, "skipping user-supplied cell header 0x%08x", *cell_header); + DPRINTK(3, "user-supplied cell header = 0x%08x\n", *cell_header); } else #endif @@ -1560,39 +1758,6 @@ skb_len = skb->len; } - retry_here: - - tasklet_disable(&fore200e->tasklet); - - entry = &txq->host_entry[ txq->head ]; - - if (*entry->status != STATUS_FREE) { - - /* try to free completed tx queue entries */ - fore200e_irq_tx(fore200e); - - if (*entry->status != STATUS_FREE) { - - tasklet_enable(&fore200e->tasklet); - - /* retry once again? */ - if(--retry > 0) - goto retry_here; - - atomic_inc(&vcc->stats->tx_err); - - printk(FORE200E "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n", - fore200e->name, fore200e->cp_queues->heartbeat); - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); - return -EIO; - } - } - - tpd = entry->tpd; - if (((unsigned long)skb_data) & 0x3) { DPRINTK(2, "misaligned tx PDU on device %s\n", fore200e->name); @@ -1602,43 +1767,87 @@ if ((vcc->qos.aal == ATM_AAL0) && (skb_len % ATM_CELL_PAYLOAD)) { - /* this simply NUKES the PCA-200E board */ + /* this simply NUKES the PCA board */ DPRINTK(2, "incomplete tx AAL0 PDU on device %s\n", fore200e->name); tx_copy = 1; tx_len = ((skb_len / ATM_CELL_PAYLOAD) + 1) * ATM_CELL_PAYLOAD; } if (tx_copy) { - - entry->data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA); - if (entry->data == NULL) { - - tasklet_enable(&fore200e->tasklet); - if (vcc->pop) + data = kmalloc(tx_len, GFP_ATOMIC | GFP_DMA); + if (data == NULL) { + if (vcc->pop) { vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); + } + else { + dev_kfree_skb_any(skb); + } return -ENOMEM; } - memcpy(entry->data, skb_data, skb_len); + memcpy(data, skb_data, skb_len); if (skb_len < tx_len) - memset(entry->data + skb_len, 0x00, tx_len - skb_len); - - tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, entry->data, tx_len, FORE200E_DMA_TODEVICE); + memset(data + skb_len, 0x00, tx_len - skb_len); } else { - entry->data = NULL; - tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, skb_data, tx_len, FORE200E_DMA_TODEVICE); + data = skb_data; } + vc_map = FORE200E_VC_MAP(fore200e, vcc->vpi, vcc->vci); + ASSERT(vc_map->vcc == vcc); + + retry_here: + + spin_lock_irqsave(&fore200e->q_lock, flags); + + entry = &txq->host_entry[ txq->head ]; + + if ((*entry->status != STATUS_FREE) || (txq->txing >= QUEUE_SIZE_TX - 2)) { + + /* try to free completed tx queue entries */ + fore200e_tx_irq(fore200e); + + if (*entry->status != STATUS_FREE) { + + spin_unlock_irqrestore(&fore200e->q_lock, flags); + + /* retry once again? */ + if(--retry > 0) { + schedule(); + goto retry_here; + } + + atomic_inc(&vcc->stats->tx_err); + + fore200e->tx_sat++; + DPRINTK(2, "tx queue of device %s is saturated, PDU dropped - heartbeat is %08x\n", + fore200e->name, fore200e->cp_queues->heartbeat); + if (vcc->pop) { + vcc->pop(vcc, skb); + } + else { + dev_kfree_skb_any(skb); + } + + if (tx_copy) + kfree(data); + + return -ENOBUFS; + } + } + + entry->incarn = vc_map->incarn; + entry->vc_map = vc_map; + entry->skb = skb; + entry->data = tx_copy ? data : NULL; + + tpd = entry->tpd; + tpd->tsd[ 0 ].buffer = fore200e->bus->dma_map(fore200e, data, tx_len, FORE200E_DMA_TODEVICE); tpd->tsd[ 0 ].length = tx_len; FORE200E_NEXT_ENTRY(txq->head, QUEUE_SIZE_TX); txq->txing++; - tasklet_enable(&fore200e->tasklet); - /* ensure DMA synchronisation */ fore200e->bus->dma_sync(fore200e, tpd->tsd[ 0 ].buffer, tpd->tsd[ 0 ].length, FORE200E_DMA_TODEVICE); @@ -1650,9 +1859,7 @@ fore200e_vcc->tx_min_pdu = skb_len; if (skb_len > fore200e_vcc->tx_max_pdu) fore200e_vcc->tx_max_pdu = skb_len; - - entry->vcc = vcc; - entry->skb = skb; + fore200e_vcc->tx_pdu++; /* set tx rate control information */ tpd->rate.data_cells = fore200e_vcc->rate.data_cells; @@ -1677,49 +1884,16 @@ tpd->spec.length = tx_len; tpd->spec.nseg = 1; tpd->spec.aal = fore200e_atm2fore_aal(vcc->qos.aal); -#ifdef FORE200E_SYNC_SEND - tpd->spec.intr = 0; -#else tpd->spec.intr = 1; -#endif - tpd_haddr.size = sizeof(struct tpd) / 32; /* size is expressed in 32 byte blocks */ + tpd_haddr.size = sizeof(struct tpd) / (1<tpd_dma >> 5; /* shift the address, as we are in a bitfield */ + tpd_haddr.haddr = entry->tpd_dma >> TPD_HADDR_SHIFT; /* shift the address, as we are in a bitfield */ *entry->status = STATUS_PENDING; fore200e->bus->write(*(u32*)&tpd_haddr, (u32*)&entry->cp_entry->tpd_haddr); - -#ifdef FORE200E_SYNC_SEND - { - int ok = fore200e_poll(fore200e, entry->status, STATUS_COMPLETE, 10); - - fore200e->bus->dma_unmap(fore200e, entry->tpd->tsd[ 0 ].buffer, entry->tpd->tsd[ 0 ].length, - FORE200E_DMA_TODEVICE); - - /* free tmp copy of misaligned data */ - if (entry->data) - kfree(entry->data); - - /* notify tx completion */ - if (vcc->pop) - vcc->pop(vcc, skb); - else - dev_kfree_skb(skb); - - if (ok == 0) { - printk(FORE200E "synchronous tx on %d:%d:%d failed\n", vcc->itf, vcc->vpi, vcc->vci); - - atomic_inc(&entry->vcc->stats->tx_err); - return -EIO; - } - atomic_inc(&entry->vcc->stats->tx); - - DPRINTK(3, "synchronous tx on %d:%d:%d succeeded\n", vcc->itf, vcc->vpi, vcc->vci); - - } -#endif + spin_unlock_irqrestore(&fore200e->q_lock, flags); return 0; } @@ -1740,7 +1914,8 @@ return -ENOMEM; } - stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats, sizeof(struct stats), FORE200E_DMA_FROMDEVICE); + stats_dma_addr = fore200e->bus->dma_map(fore200e, fore200e->stats, + sizeof(struct stats), FORE200E_DMA_FROMDEVICE); FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); @@ -1769,9 +1944,9 @@ static int -fore200e_getsockopt (struct atm_vcc* vcc, int level, int optname, void* optval, int optlen) +fore200e_getsockopt(struct atm_vcc* vcc, int level, int optname, void* optval, int optlen) { - // struct fore200e* fore200e = FORE200E_DEV(vcc->dev); + /* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */ DPRINTK(2, "getsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n", vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen); @@ -1783,7 +1958,7 @@ static int fore200e_setsockopt(struct atm_vcc* vcc, int level, int optname, void* optval, int optlen) { - // struct fore200e* fore200e = FORE200E_DEV(vcc->dev); + /* struct fore200e* fore200e = FORE200E_DEV(vcc->dev); */ DPRINTK(2, "setsockopt %d.%d.%d, level = %d, optname = 0x%x, optval = 0x%p, optlen = %d\n", vcc->itf, vcc->vpi, vcc->vci, level, optname, optval, optlen); @@ -1841,6 +2016,8 @@ struct oc3_opcode opcode; int ok; + DPRINTK(2, "set OC-3 reg = 0x%02x, value = 0x%02x, mask = 0x%02x\n", reg, value, mask); + FORE200E_NEXT_ENTRY(cmdq->head, QUEUE_SIZE_CMD); opcode.opcode = OPCODE_SET_OC3; @@ -1896,7 +2073,7 @@ } error = fore200e_set_oc3(fore200e, SUNI_MCT, mct_value, mct_mask); - if ( error == 0) + if (error == 0) fore200e->loop_mode = loop_mode; return error; @@ -1978,6 +2155,11 @@ struct fore200e_vcc* fore200e_vcc = FORE200E_VCC(vcc); struct fore200e* fore200e = FORE200E_DEV(vcc->dev); + if (!test_bit(ATM_VF_READY, &vcc->flags)) { + DPRINTK(1, "VC %d.%d.%d not ready for QoS change\n", vcc->itf, vcc->vpi, vcc->vpi); + return -EINVAL; + } + DPRINTK(2, "change_qos %d.%d.%d, " "(tx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d; " "rx: cl=%s, pcr=%d-%d, cdv=%d, max_sdu=%d), flags = 0x%x\n" @@ -1999,6 +2181,7 @@ fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; fore200e->available_cell_rate -= qos->txtp.max_pcr; + up(&fore200e->rate_sf); memcpy(&vcc->qos, qos, sizeof(struct atm_qos)); @@ -2007,6 +2190,7 @@ fore200e_rate_ctrl(qos, &fore200e_vcc->rate); set_bit(ATM_VF_HASQOS, &vcc->flags); + return 0; } @@ -2027,7 +2211,10 @@ printk(FORE200E "IRQ %s reserved for device %s\n", fore200e_irq_itoa(fore200e->irq), fore200e->name); - tasklet_init(&fore200e->tasklet, fore200e_tasklet, (unsigned long)fore200e); +#ifdef FORE200E_USE_TASKLET + tasklet_init(&fore200e->tx_tasklet, fore200e_tx_tasklet, (unsigned long)fore200e); + tasklet_init(&fore200e->rx_tasklet, fore200e_rx_tasklet, (unsigned long)fore200e); +#endif fore200e->state = FORE200E_STATE_IRQ; return 0; @@ -2042,6 +2229,7 @@ if (!prom) return -ENOMEM; + ok = fore200e->bus->prom_read(fore200e, prom); if (ok < 0) { fore200e_kfree(prom); @@ -2089,10 +2277,16 @@ if (buffer == NULL) return -ENOMEM; + bsq->freebuf = NULL; + for (i = 0; i < nbr; i++) { buffer[ i ].scheme = scheme; buffer[ i ].magn = magn; +#ifdef FORE200E_BSQ_DEBUG + buffer[ i ].index = i; + buffer[ i ].supplied = 0; +#endif /* allocate the receive buffer body */ if (fore200e_chunk_alloc(fore200e, @@ -2105,9 +2299,17 @@ return -ENOMEM; } + + /* insert the buffer into the free buffer list */ + buffer[ i ].next = bsq->freebuf; + bsq->freebuf = &buffer[ i ]; } - /* set next free buffer index */ - bsq->free = 0; + /* all the buffers are free, initially */ + bsq->freebuf_count = nbr; + +#ifdef FORE200E_BSQ_DEBUG + bsq_audit(3, bsq, scheme, magn); +#endif } } @@ -2164,9 +2366,9 @@ FORE200E_INDEX(bsq->rbd_block.align_addr, struct rbd_block, i); bsq->host_entry[ i ].rbd_block_dma = FORE200E_DMA_INDEX(bsq->rbd_block.dma_addr, struct rbd_block, i); - bsq->host_entry[ i ].cp_entry = &cp_entry[ i ]; + bsq->host_entry[ i ].cp_entry = &cp_entry[ i ]; - *bsq->host_entry[ i ].status = STATUS_FREE; + *bsq->host_entry[ i ].status = STATUS_FREE; fore200e->bus->write(FORE200E_DMA_INDEX(bsq->status.dma_addr, enum status, i), &cp_entry[ i ].status_haddr); @@ -2293,10 +2495,11 @@ we do not write here the DMA (physical) base address of each tpd into the related cp resident entry, because the cp relies on this write operation to detect that a new pdu has been submitted for tx */ -} + } - /* set the head entry of the queue */ + /* set the head and tail entries of the queue */ txq->head = 0; + txq->tail = 0; fore200e->state = FORE200E_STATE_INIT_TXQ; return 0; @@ -2315,9 +2518,9 @@ /* allocate and align the array of status words */ if (fore200e->bus->dma_chunk_alloc(fore200e, &cmdq->status, - sizeof(enum status), - QUEUE_SIZE_CMD, - fore200e->bus->status_alignment) < 0) { + sizeof(enum status), + QUEUE_SIZE_CMD, + fore200e->bus->status_alignment) < 0) { return -ENOMEM; } @@ -2353,12 +2556,6 @@ { struct bs_spec* bs_spec = &fore200e->cp_queues->init.bs_spec[ scheme ][ magn ]; - /* dumb value; the firmware doesn't allow us to activate a VC while - selecting a buffer scheme with zero-sized rbd pools */ - - if (pool_size == 0) - pool_size = 64; - fore200e->bus->write(queue_length, &bs_spec->queue_length); fore200e->bus->write(fore200e_rx_buf_size[ scheme ][ magn ], &bs_spec->buffer_size); fore200e->bus->write(pool_size, &bs_spec->pool_size); @@ -2375,7 +2572,8 @@ DPRINTK(2, "device %s being initialized\n", fore200e->name); init_MUTEX(&fore200e->rate_sf); - + spin_lock_init(&fore200e->q_lock); + cpq = fore200e->cp_queues = (struct cp_queues*) (fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET); /* enable cp to host interrupts */ @@ -2457,7 +2655,7 @@ static void __init fore200e_monitor_puts(struct fore200e* fore200e, char* str) { - while(*str) { + while (*str) { /* the i960 monitor doesn't accept any new character if it has something to say */ while (fore200e_monitor_getc(fore200e) >= 0); @@ -2478,6 +2676,11 @@ DPRINTK(2, "device %s firmware being started\n", fore200e->name); +#if defined(__sparc_v9__) + /* reported to be required by SBA cards on some sparc64 hosts */ + fore200e_spin(100); +#endif + sprintf(cmd, "\rgo %x\r", le32_to_cpu(fw_header->start_offset)); fore200e_monitor_puts(fore200e, cmd); @@ -2508,12 +2711,10 @@ DPRINTK(2, "device %s firmware being loaded at 0x%p (%d words)\n", fore200e->name, load_addr, fw_size); -#if 1 if (le32_to_cpu(fw_header->magic) != FW_HEADER_MAGIC) { printk(FORE200E "corrupted %s firmware image\n", fore200e->bus->model_name); return -ENODEV; } -#endif for (; fw_size--; fw_data++, load_addr++) fore200e->bus->write(le32_to_cpu(*fw_data), load_addr); @@ -2540,8 +2741,8 @@ FORE200E_DEV(atm_dev) = fore200e; fore200e->atm_dev = atm_dev; - atm_dev->ci_range.vpi_bits = 8; - atm_dev->ci_range.vci_bits = 10; + atm_dev->ci_range.vpi_bits = FORE200E_VPI_BITS; + atm_dev->ci_range.vci_bits = FORE200E_VCI_BITS; fore200e->available_cell_rate = ATM_OC3_PCR; @@ -2610,7 +2811,7 @@ struct fore200e* fore200e; int index, link; - printk(FORE200E "FORE Systems 200E-series driver - version " FORE200E_VERSION "\n"); + printk(FORE200E "FORE Systems 200E-series ATM driver - version " FORE200E_VERSION "\n"); /* for each configured bus interface */ for (link = 0, bus = fore200e_bus; bus->model_name; bus++) { @@ -2657,11 +2858,13 @@ static int -fore200e_proc_read(struct atm_dev *dev,loff_t* pos,char* page) +fore200e_proc_read(struct atm_dev *dev, loff_t* pos, char* page) { - struct sock *s; - struct fore200e* fore200e = FORE200E_DEV(dev); - int len, left = *pos; + struct fore200e* fore200e = FORE200E_DEV(dev); + struct fore200e_vcc* fore200e_vcc; + struct atm_vcc* vcc; + int i, len, left = *pos; + unsigned long flags; if (!left--) { @@ -2694,14 +2897,15 @@ if (!left--) return sprintf(page, - " supplied small bufs (1):\t%d\n" - " supplied large bufs (1):\t%d\n" - " supplied small bufs (2):\t%d\n" - " supplied large bufs (2):\t%d\n", - fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].count, - fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].count, - fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].count, - fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].count); + " free small bufs, scheme 1:\t%d\n" + " free large bufs, scheme 1:\t%d\n" + " free small bufs, scheme 2:\t%d\n" + " free large bufs, scheme 2:\t%d\n", + fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_SMALL ].freebuf_count, + fore200e->host_bsq[ BUFFER_SCHEME_ONE ][ BUFFER_MAGN_LARGE ].freebuf_count, + fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_SMALL ].freebuf_count, + fore200e->host_bsq[ BUFFER_SCHEME_TWO ][ BUFFER_MAGN_LARGE ].freebuf_count); + if (!left--) { u32 hb = fore200e->bus->read(&fore200e->cp_queues->heartbeat); @@ -2740,7 +2944,7 @@ u32 media_index = FORE200E_MEDIA_INDEX(fore200e->bus->read(&fore200e->cp_queues->media_type)); u32 oc3_index; - if (media_index < 0 || media_index > 4) + if ((media_index < 0) || (media_index > 4)) media_index = 5; switch (fore200e->loop_mode) { @@ -2887,49 +3091,60 @@ " large b1:\t\t\t%10u\n" " small b2:\t\t\t%10u\n" " large b2:\t\t\t%10u\n" - " RX PDUs:\t\t\t%10u\n", + " RX PDUs:\t\t\t%10u\n" + " TX PDUs:\t\t\t%10lu\n", fore200e_swap(fore200e->stats->aux.small_b1_failed), fore200e_swap(fore200e->stats->aux.large_b1_failed), fore200e_swap(fore200e->stats->aux.small_b2_failed), fore200e_swap(fore200e->stats->aux.large_b2_failed), - fore200e_swap(fore200e->stats->aux.rpd_alloc_failed)); - + fore200e_swap(fore200e->stats->aux.rpd_alloc_failed), + fore200e->tx_sat); + if (!left--) return sprintf(page,"\n" " receive carrier:\t\t\t%s\n", fore200e->stats->aux.receive_carrier ? "ON" : "OFF!"); if (!left--) { - struct atm_vcc *vcc; - struct fore200e_vcc* fore200e_vcc; - - len = sprintf(page,"\n" - " VCCs:\n address\tVPI.VCI:AAL\t(min/max tx PDU size) (min/max rx PDU size)\n"); - - read_lock(&vcc_sklist_lock); - for (s = vcc_sklist; s; s = s->next) { - vcc = s->protinfo.af_atm; + return sprintf(page,"\n" + " VCCs:\n address VPI VCI AAL " + "TX PDUs TX min/max size RX PDUs RX min/max size\n"); + } - if (vcc->dev != fore200e->atm_dev) - continue; + + for (i = 0; i < NBR_CONNECT; i++) { + + vcc = fore200e->vc_map[i].vcc; + + if (vcc == NULL) + continue; + + spin_lock_irqsave(&fore200e->q_lock, flags); + + if (vcc && test_bit(ATM_VF_READY, &vcc->flags) && !left--) { fore200e_vcc = FORE200E_VCC(vcc); - - len += sprintf(page + len, - " %x\t%d.%d:%d\t\t(%d/%d)\t(%d/%d)\n", + ASSERT(fore200e_vcc); + + len = sprintf(page, + " %08x %03d %05d %1d %09lu %05d/%05d %09lu %05d/%05d\n", (u32)(unsigned long)vcc, vcc->vpi, vcc->vci, fore200e_atm2fore_aal(vcc->qos.aal), + fore200e_vcc->tx_pdu, fore200e_vcc->tx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->tx_min_pdu, fore200e_vcc->tx_max_pdu, + fore200e_vcc->rx_pdu, fore200e_vcc->rx_min_pdu > 0xFFFF ? 0 : fore200e_vcc->rx_min_pdu, fore200e_vcc->rx_max_pdu ); + + spin_unlock_irqrestore(&fore200e->q_lock, flags); + return len; } - read_unlock(&vcc_sklist_lock); - return len; + spin_unlock_irqrestore(&fore200e->q_lock, flags); } - + return 0; } @@ -2966,7 +3181,7 @@ send: fore200e_send, change_qos: fore200e_change_qos, proc_read: fore200e_proc_read, - owner: THIS_MODULE, + owner: THIS_MODULE }; @@ -3027,4 +3242,6 @@ {} }; +#ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); +#endif diff -urN linux-2.4.26/drivers/atm/fore200e.h linux-2.4.27/drivers/atm/fore200e.h --- linux-2.4.26/drivers/atm/fore200e.h 2000-12-11 13:22:12.000000000 -0800 +++ linux-2.4.27/drivers/atm/fore200e.h 2004-08-07 16:26:04.667347312 -0700 @@ -23,19 +23,21 @@ #define BUFFER_S2_SIZE SMALL_BUFFER_SIZE /* size of small buffers, scheme 2 */ #define BUFFER_L2_SIZE LARGE_BUFFER_SIZE /* size of large buffers, scheme 2 */ -#define BUFFER_S1_NBR (RBD_BLK_SIZE * 2) -#define BUFFER_L1_NBR (RBD_BLK_SIZE * 2) +#define BUFFER_S1_NBR (RBD_BLK_SIZE * 6) +#define BUFFER_L1_NBR (RBD_BLK_SIZE * 4) -#define BUFFER_S2_NBR (RBD_BLK_SIZE * 2) -#define BUFFER_L2_NBR (RBD_BLK_SIZE * 2) +#define BUFFER_S2_NBR (RBD_BLK_SIZE * 6) +#define BUFFER_L2_NBR (RBD_BLK_SIZE * 4) #define QUEUE_SIZE_CMD 16 /* command queue capacity */ #define QUEUE_SIZE_RX 64 /* receive queue capacity */ #define QUEUE_SIZE_TX 256 /* transmit queue capacity */ -#define QUEUE_SIZE_BS 16 /* buffer supply queue capacity */ +#define QUEUE_SIZE_BS 32 /* buffer supply queue capacity */ -#define NBR_CONNECT 1024 /* number of ATM connections */ +#define FORE200E_VPI_BITS 0 +#define FORE200E_VCI_BITS 10 +#define NBR_CONNECT (1 << (FORE200E_VPI_BITS + FORE200E_VCI_BITS)) /* number of connections */ #define TSD_FIXED 2 @@ -207,6 +209,7 @@ ) } tpd_haddr_t; +#define TPD_HADDR_SHIFT 5 /* addr aligned on 32 byte boundary */ /* cp resident transmit queue entry */ @@ -517,13 +520,15 @@ /* host resident transmit queue entry */ typedef struct host_txq_entry { - struct cp_txq_entry* cp_entry; /* addr of cp resident tx queue entry */ - enum status* status; /* addr of host resident status */ - struct tpd* tpd; /* addr of transmit PDU descriptor */ - u32 tpd_dma; /* DMA address of tpd */ - struct sk_buff* skb; /* related skb */ - struct atm_vcc* vcc; /* related vcc */ - void* data; /* copy of misaligned data */ + struct cp_txq_entry* cp_entry; /* addr of cp resident tx queue entry */ + enum status* status; /* addr of host resident status */ + struct tpd* tpd; /* addr of transmit PDU descriptor */ + u32 tpd_dma; /* DMA address of tpd */ + struct sk_buff* skb; /* related skb */ + void* data; /* copy of misaligned data */ + unsigned long incarn; /* vc_map incarnation when submitted for tx */ + struct fore200e_vc_map* vc_map; + } host_txq_entry_t; @@ -576,6 +581,10 @@ enum buffer_scheme scheme; /* buffer scheme */ enum buffer_magn magn; /* buffer magnitude */ struct chunk data; /* data buffer */ +#ifdef FORE200E_BSQ_DEBUG + unsigned long index; /* buffer # in queue */ + int supplied; /* 'buffer supplied' flag */ +#endif } buffer_t; @@ -602,6 +611,7 @@ typedef struct host_txq { struct host_txq_entry host_entry[ QUEUE_SIZE_TX ]; /* host resident tx queue entries */ int head; /* head of tx queue */ + int tail; /* tail of tx queue */ struct chunk tpd; /* array of tpds */ struct chunk status; /* arry of completion status */ int txing; /* number of pending PDUs in tx queue */ @@ -626,8 +636,8 @@ struct chunk rbd_block; /* array of rbds */ struct chunk status; /* array of completion status */ struct buffer* buffer; /* array of rx buffers */ - int free; /* index of first free rx buffer */ - volatile int count; /* count of supplied rx buffers */ + struct buffer* freebuf; /* list of free rx buffers */ + volatile int freebuf_count; /* count of free rx buffers */ } host_bsq_t; @@ -846,6 +856,17 @@ #endif +/* vc mapping */ + +typedef struct fore200e_vc_map { + struct atm_vcc* vcc; /* vcc entry */ + unsigned long incarn; /* vcc incarnation number */ +} fore200e_vc_map_t; + +#define FORE200E_VC_MAP(fore200e, vpi, vci) \ + (& (fore200e)->vc_map[ ((vpi) << FORE200E_VCI_BITS) | (vci) ]) + + /* per-device data */ typedef struct fore200e { @@ -879,20 +900,29 @@ struct stats* stats; /* last snapshot of the stats */ struct semaphore rate_sf; /* protects rate reservation ops */ - struct tasklet_struct tasklet; /* performs interrupt work */ + spinlock_t q_lock; /* protects queue ops */ +#ifdef FORE200E_USE_TASKLET + struct tasklet_struct tx_tasklet; /* performs tx interrupt work */ + struct tasklet_struct rx_tasklet; /* performs rx interrupt work */ +#endif + unsigned long tx_sat; /* tx queue saturation count */ + unsigned long incarn_count; + struct fore200e_vc_map vc_map[ NBR_CONNECT ]; /* vc mapping */ } fore200e_t; /* per-vcc data */ typedef struct fore200e_vcc { - enum buffer_scheme scheme; /* rx buffer scheme */ - struct tpd_rate rate; /* tx rate control data */ - int rx_min_pdu; /* size of smallest PDU received */ - int rx_max_pdu; /* size of largest PDU received */ - int tx_min_pdu; /* size of smallest PDU transmitted */ - int tx_max_pdu; /* size of largest PDU transmitted */ + enum buffer_scheme scheme; /* rx buffer scheme */ + struct tpd_rate rate; /* tx rate control data */ + int rx_min_pdu; /* size of smallest PDU received */ + int rx_max_pdu; /* size of largest PDU received */ + int tx_min_pdu; /* size of smallest PDU transmitted */ + int tx_max_pdu; /* size of largest PDU transmitted */ + unsigned long tx_pdu; /* nbr of tx pdus */ + unsigned long rx_pdu; /* nbr of rx pdus */ } fore200e_vcc_t; diff -urN linux-2.4.26/drivers/atm/nicstar.c linux-2.4.27/drivers/atm/nicstar.c --- linux-2.4.26/drivers/atm/nicstar.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/atm/nicstar.c 2004-08-07 16:26:04.670347435 -0700 @@ -760,7 +760,7 @@ for (j = 0; j < NUM_HB; j++) { struct sk_buff *hb; - hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); + hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL); if (hb == NULL) { printk("nicstar%d: can't allocate %dth of %d huge buffers.\n", @@ -780,7 +780,7 @@ for (j = 0; j < NUM_LB; j++) { struct sk_buff *lb; - lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); + lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); if (lb == NULL) { printk("nicstar%d: can't allocate %dth of %d large buffers.\n", @@ -816,7 +816,7 @@ for (j = 0; j < NUM_SB; j++) { struct sk_buff *sb; - sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); + sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); if (sb == NULL) { printk("nicstar%d: can't allocate %dth of %d small buffers.\n", @@ -1318,7 +1318,7 @@ card->index); for (i = 0; i < card->sbnr.min; i++) { - sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC); + sb = dev_alloc_skb(NS_SMSKBSIZE); if (sb == NULL) { writel(readl(card->membase + CFG) & ~NS_CFG_EFBIE, card->membase + CFG); @@ -1344,7 +1344,7 @@ card->index); for (i = 0; i < card->lbnr.min; i++) { - lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC); + lb = dev_alloc_skb(NS_LGSKBSIZE); if (lb == NULL) { writel(readl(card->membase + CFG) & ~NS_CFG_EFBIE, card->membase + CFG); @@ -2167,7 +2167,7 @@ cell = skb->data; for (i = ns_rsqe_cellcount(rsqe); i; i--) { - if ((sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC)) == NULL) + if ((sb = dev_alloc_skb(NS_SMSKBSIZE)) == NULL) { printk("nicstar%d: Can't allocate buffers for aal0.\n", card->index); @@ -2399,7 +2399,7 @@ if (hb == NULL) /* No buffers in the queue */ { - hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC); + hb = dev_alloc_skb(NS_HBUFSIZE); if (hb == NULL) { printk("nicstar%d: Out of huge buffers.\n", card->index); @@ -2413,7 +2413,7 @@ else if (card->hbpool.count < card->hbnr.min) { struct sk_buff *new_hb; - if ((new_hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC)) != NULL) + if ((new_hb = dev_alloc_skb(NS_HBUFSIZE)) != NULL) { skb_queue_tail(&card->hbpool.queue, new_hb); card->hbpool.count++; @@ -2424,14 +2424,14 @@ if (--card->hbpool.count < card->hbnr.min) { struct sk_buff *new_hb; - if ((new_hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC)) != NULL) + if ((new_hb = dev_alloc_skb(NS_HBUFSIZE)) != NULL) { skb_queue_tail(&card->hbpool.queue, new_hb); card->hbpool.count++; } if (card->hbpool.count < card->hbnr.min) { - if ((new_hb = alloc_skb(NS_HBUFSIZE, GFP_ATOMIC)) != NULL) + if ((new_hb = dev_alloc_skb(NS_HBUFSIZE)) != NULL) { skb_queue_tail(&card->hbpool.queue, new_hb); card->hbpool.count++; @@ -2513,7 +2513,7 @@ do { - sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); + sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); if (sb == NULL) break; skb_queue_tail(&card->sbpool.queue, sb); @@ -2536,7 +2536,7 @@ do { - lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); + lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); if (lb == NULL) break; skb_queue_tail(&card->lbpool.queue, lb); @@ -2555,7 +2555,7 @@ while (card->hbpool.count < card->hbnr.init) { - hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); + hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL); if (hb == NULL) break; skb_queue_tail(&card->hbpool.queue, hb); @@ -2627,7 +2627,7 @@ if (card->sbfqc < card->sbnr.init) { struct sk_buff *new_sb; - if ((new_sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC)) != NULL) + if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) { skb_queue_tail(&card->sbpool.queue, new_sb); skb_reserve(new_sb, NS_AAL0_HEADER); @@ -2639,7 +2639,7 @@ #endif /* NS_USE_DESTRUCTORS */ { struct sk_buff *new_sb; - if ((new_sb = alloc_skb(NS_SMSKBSIZE, GFP_ATOMIC)) != NULL) + if ((new_sb = dev_alloc_skb(NS_SMSKBSIZE)) != NULL) { skb_queue_tail(&card->sbpool.queue, new_sb); skb_reserve(new_sb, NS_AAL0_HEADER); @@ -2660,7 +2660,7 @@ if (card->lbfqc < card->lbnr.init) { struct sk_buff *new_lb; - if ((new_lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC)) != NULL) + if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) { skb_queue_tail(&card->lbpool.queue, new_lb); skb_reserve(new_lb, NS_SMBUFSIZE); @@ -2672,7 +2672,7 @@ #endif /* NS_USE_DESTRUCTORS */ { struct sk_buff *new_lb; - if ((new_lb = alloc_skb(NS_LGSKBSIZE, GFP_ATOMIC)) != NULL) + if ((new_lb = dev_alloc_skb(NS_LGSKBSIZE)) != NULL) { skb_queue_tail(&card->lbpool.queue, new_lb); skb_reserve(new_lb, NS_SMBUFSIZE); @@ -2866,7 +2866,7 @@ { struct sk_buff *sb; - sb = alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); + sb = __dev_alloc_skb(NS_SMSKBSIZE, GFP_KERNEL); if (sb == NULL) return -ENOMEM; skb_queue_tail(&card->sbpool.queue, sb); @@ -2880,7 +2880,7 @@ { struct sk_buff *lb; - lb = alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); + lb = __dev_alloc_skb(NS_LGSKBSIZE, GFP_KERNEL); if (lb == NULL) return -ENOMEM; skb_queue_tail(&card->lbpool.queue, lb); @@ -2909,7 +2909,7 @@ { struct sk_buff *hb; - hb = alloc_skb(NS_HBUFSIZE, GFP_KERNEL); + hb = __dev_alloc_skb(NS_HBUFSIZE, GFP_KERNEL); if (hb == NULL) return -ENOMEM; ns_grab_int_lock(card, flags); diff -urN linux-2.4.26/drivers/block/Config.in linux-2.4.27/drivers/block/Config.in --- linux-2.4.26/drivers/block/Config.in 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/drivers/block/Config.in 2004-08-07 16:26:04.671347476 -0700 @@ -39,6 +39,7 @@ dep_mbool ' Enable monitor thread' CONFIG_CISS_MONITOR_THREAD $CONFIG_BLK_CPQ_CISS_DA dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI dep_tristate 'Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)' CONFIG_BLK_DEV_UMEM $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate 'Promise SATA SX8 support' CONFIG_BLK_DEV_SX8 $CONFIG_PCI tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET diff -urN linux-2.4.26/drivers/block/Makefile linux-2.4.27/drivers/block/Makefile --- linux-2.4.26/drivers/block/Makefile 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/drivers/block/Makefile 2004-08-07 16:26:04.671347476 -0700 @@ -31,6 +31,7 @@ obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o +obj-$(CONFIG_BLK_DEV_SX8) += sx8.o subdir-$(CONFIG_PARIDE) += paride diff -urN linux-2.4.26/drivers/block/acsi_slm.c linux-2.4.27/drivers/block/acsi_slm.c --- linux-2.4.26/drivers/block/acsi_slm.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/block/acsi_slm.c 2004-08-07 16:26:04.672347517 -0700 @@ -367,6 +367,7 @@ { struct inode *node = file->f_dentry->d_inode; + loff_t pos = *ppos; unsigned long page; int length; int end; @@ -381,18 +382,18 @@ count = length; goto out; } - if (file->f_pos >= length) { + if (pos != (unsigned) pos || pos >= length) { count = 0; goto out; } - if (count + file->f_pos > length) - count = length - file->f_pos; - end = count + file->f_pos; - if (copy_to_user(buf, (char *)page + file->f_pos, count)) { + if (count > length - pos) + count = length - pos; + end = count + pos; + if (copy_to_user(buf, (char *)page + pos, count)) { count = -EFAULT; goto out; } - file->f_pos = end; + *ppos = end; out: free_page( page ); return( count ); } diff -urN linux-2.4.26/drivers/block/cciss.c linux-2.4.27/drivers/block/cciss.c --- linux-2.4.26/drivers/block/cciss.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/block/cciss.c 2004-08-07 16:26:04.675347640 -0700 @@ -45,13 +45,13 @@ #include #define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) -#define DRIVER_NAME "HP CISS Driver (v 2.4.50)" -#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,50) +#define DRIVER_NAME "HP CISS Driver (v 2.4.52)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(2,4,52) /* Embedded module documentation macros - see modules.h */ MODULE_AUTHOR("Hewlett-Packard Company"); -MODULE_DESCRIPTION("Driver for HP SA5xxx SA6xxx Controllers version 2.4.50"); -MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400 6i"); +MODULE_DESCRIPTION("Driver for HP SA5xxx SA6xxx Controllers version 2.4.52"); +MODULE_SUPPORTED_DEVICE("HP SA5i SA5i+ SA532 SA5300 SA5312 SA641 SA642 SA6400 6i SA6422 V100"); MODULE_LICENSE("GPL"); #include "cciss_cmd.h" @@ -78,6 +78,10 @@ 0x0E11, 0x409D, 0, 0, 0}, { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, 0x0E11, 0x4091, 0, 0, 0}, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, + 0x0E11, 0x409E, 0, 0, 0}, + { PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_CISSC, + 0x103C, 0x3211, 0, 0, 0}, {0,} }; MODULE_DEVICE_TABLE(pci, cciss_pci_device_id); @@ -98,6 +102,8 @@ { 0x409C0E11, "Smart Array 6400", &SA5_access}, { 0x409D0E11, "Smart Array 6400 EM", &SA5_access}, { 0x40910E11, "Smart Array 6i", &SA5_access}, + { 0x409E0E11, "Smart Array 6422", &SA5_access}, + { 0x3211103C, "Smart Array V100", &SA5_access}, }; /* How long to wait (in millesconds) for board to go into simple mode */ @@ -487,6 +493,142 @@ return 0; } +#ifdef __x86_64__ +/* for AMD 64 bit kernel compatibility with 32-bit userland ioctls */ +extern int sys_ioctl(unsigned int fd, unsigned cmd, unsigned long arg); + +extern int +register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, + unsigned int, unsigned long, struct file *)); +extern int unregister_ioctl32_conversion(unsigned int cmd); + +static int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg, struct file *file); +static int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg, + struct file *file); + +typedef long (*handler type) (unsigned int, unsigned int, unsigned long, + struct file *); + +static struct ioctl32_map { + unsigned int cmd; + handler_type handler; + int registered; +} cciss_ioctl32_map[] = { + { CCISS_GETPCIINFO, (handler_type) sys_ioctl, 0 }, + { CCISS_GETINTINFO, (handler_type) sys_ioctl, 0 }, + { CCISS_SETINTINFO, (handler_type) sys_ioctl, 0 }, + { CCISS_GETNODENAME, (handler_type) sys_ioctl, 0 }, + { CCISS_SETNODENAME, (handler_type) sys_ioctl, 0 }, + { CCISS_GETHEARTBEAT, (handler_type) sys_ioctl, 0 }, + { CCISS_GETBUSTYPES, (handler_type) sys_ioctl, 0 }, + { CCISS_GETFIRMVER, (handler_type) sys_ioctl, 0 }, + { CCISS_GETDRIVVER, (handler_type) sys_ioctl, 0 }, + { CCISS_REVALIDVOLS, (handler_type) sys_ioctl, 0 }, + { CCISS_PASSTHRU32, cciss_ioctl32_passthru, 0 }, + { CCISS_DEREGDISK, (handler_type) sys_ioctl, 0 }, + { CCISS_REGNEWDISK, (handler_type) sys_ioctl, 0 }, + { CCISS_REGNEWD, (handler_type) sys_ioctl, 0 }, + { CCISS_RESCANDISK, (handler_type) sys_ioctl, 0 }, + { CCISS_GETLUNINFO, (handler_type) sys_ioctl, 0 }, + { CCISS_BIG_PASSTHRU32, cciss_ioctl32_big_passthru, 0 }, +}; +#define NCCISS_IOCTL32_ENTRIES (sizeof(cciss_ioctl32_map) / sizeof(cciss_ioctl32_map[0])) +static void register_cciss_ioctl32(void) +{ + int i, rc; + + for (i=0; i < NCCISS_IOCTL32_ENTRIES; i++) { + rc = register_ioctl32_conversion( + cciss_ioctl32_map[i].cmd, + cciss_ioctl32_map[i].handler); + if (rc != 0) { + printk(KERN_WARNING "cciss: failed to register " + "32 bit compatible ioctl 0x%08x\n", + cciss_ioctl32_map[i].cmd); + cciss_ioctl32_map[i].registered = 0; + } else + cciss_ioctl32_map[i].registered = 1; + } +} +static void unregister_cciss_ioctl32(void) +{ + int i, rc; + + for (i=0; i < NCCISS_IOCTL32_ENTRIES; i++) { + if (!cciss_ioctl32_map[i].registered) + continue; + rc = unregister_ioctl32_conversion( + cciss_ioctl32_map[i].cmd); + if (rc == 0) { + cciss_ioctl32_map[i].registered = 0; + continue; + } + printk(KERN_WARNING "cciss: failed to unregister " + "32 bit compatible ioctl 0x%08x\n", + cciss_ioctl32_map[i].cmd); + } +} +int cciss_ioctl32_passthru(unsigned int fd, unsigned cmd, unsigned long arg, + struct file *file) +{ + IOCTL32_Command_struct *arg32 = + (IOCTL32_Command_struct *) arg; + IOCTL_Command_struct arg64; + mm_segment_t old_fs; + int err; + + err = 0; + err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, sizeof(arg64.LUN_info)); + err |= copy_from_user(&arg64.Request, &arg32->Request, sizeof(arg64.Request)); + err |= copy_from_user(&arg64.error_info, &arg32->error_info, sizeof(arg64.error_info)); + err |= get_user(arg64.buf_size, &arg32->buf_size); + err |= get_user(arg64.buf, &arg32->buf); + if (err) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, CCISS_PASSTHRU, (unsigned long) &arg64); + set_fs(old_fs); + if (err) + return err; + err |= copy_to_user(&arg32->error_info, &arg64.error_info, sizeof(&arg32->error_info)); + if (err) + return -EFAULT; + return err; +} +int cciss_ioctl32_big_passthru(unsigned int fd, unsigned cmd, unsigned long arg, + struct file *file) +{ + BIG_IOCTL32_Command_struct *arg32 = + (BIG_IOCTL32_Command_struct *) arg; + BIG_IOCTL_Command_struct arg64; + mm_segment_t old_fs; + int err; + + err = 0; + err |= copy_from_user(&arg64.LUN_info, &arg32->LUN_info, sizeof(arg64.LUN_info)); + err |= copy_from_user(&arg64.Request, &arg32->Request, sizeof(arg64.Request)); + err |= copy_from_user(&arg64.error_info, &arg32->error_info, sizeof(arg64.error_info)); + err |= get_user(arg64.buf_size, &arg32->buf_size); + err |= get_user(arg64.malloc_size, &arg32->malloc_size); + err |= get_user(arg64.buf, &arg32->buf); + if (err) return -EFAULT; + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = sys_ioctl(fd, CCISS_BIG_PASSTHRU, (unsigned long) &arg64); + set_fs(old_fs); + if (err) + return err; + err |= copy_to_user(&arg32->error_info, &arg64.error_info, sizeof(&arg32->error_info)); + if (err) + return -EFAULT; + return err; +} +#else +static inline void register_cciss_ioctl32(void) {} +static inline void unregister_cciss_ioctl32(void) {} +#endif /* * ioctl */ @@ -3317,7 +3459,7 @@ EXPORT_NO_SYMBOLS; static int __init init_cciss_module(void) { - + register_cciss_ioctl32(); return cciss_init(); } @@ -3325,6 +3467,7 @@ { int i; + unregister_cciss_ioctl32(); pci_unregister_driver(&cciss_pci_driver); /* double check that all controller entrys have been removed */ for (i=0; i< MAX_CTLR; i++) { diff -urN linux-2.4.26/drivers/block/cciss_scsi.c linux-2.4.27/drivers/block/cciss_scsi.c --- linux-2.4.26/drivers/block/cciss_scsi.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/block/cciss_scsi.c 2004-08-07 16:26:04.676347681 -0700 @@ -1569,7 +1569,7 @@ cciss_proc_tape_report(int ctlr, unsigned char *buffer, off_t *pos, off_t *len) { int size; - unsigned int flags; + unsigned long flags; *pos = *pos -1; *len = *len - 1; // cut off the last trailing newline diff -urN linux-2.4.26/drivers/block/rd.c linux-2.4.27/drivers/block/rd.c --- linux-2.4.26/drivers/block/rd.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/block/rd.c 2004-08-07 16:26:04.677347723 -0700 @@ -320,14 +320,19 @@ static ssize_t initrd_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - int left; + loff_t n = *ppos; + unsigned pos = n; + unsigned left = initrd_end - initrd_start; - left = initrd_end - initrd_start - *ppos; + if (pos != n || pos >= left) + return 0; + + left -= pos; if (count > left) count = left; if (count == 0) return 0; - if (copy_to_user(buf, (char *)initrd_start + *ppos, count)) + if (copy_to_user(buf, (char *)initrd_start + pos, count)) return -EFAULT; - *ppos += count; + *ppos = pos + count; return count; } diff -urN linux-2.4.26/drivers/block/sx8.c linux-2.4.27/drivers/block/sx8.c --- linux-2.4.26/drivers/block/sx8.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/block/sx8.c 2004-08-07 16:26:04.680347846 -0700 @@ -0,0 +1,2083 @@ +/* + * carmel.c: Driver for Promise SATA SX8 looks-like-I2O hardware + * + * Copyright 2004 Red Hat, Inc. + * + * Author/maintainer: Jeff Garzik + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Promise SATA SX8 (carmel) block driver"); + +#if 0 +#define CARM_DEBUG +#define CARM_VERBOSE_DEBUG +#else +#undef CARM_DEBUG +#undef CARM_VERBOSE_DEBUG +#endif +#undef CARM_NDEBUG + +#define DRV_NAME "carmel" +#define DRV_VERSION "0.8-24.1" +#define PFX DRV_NAME ": " + +#define NEXT_RESP(idx) ((idx + 1) % RMSG_Q_LEN) + +/* 0xf is just arbitrary, non-zero noise; this is sorta like poisoning */ +#define TAG_ENCODE(tag) (((tag) << 16) | 0xf) +#define TAG_DECODE(tag) (((tag) >> 16) & 0x1f) +#define TAG_VALID(tag) ((((tag) & 0xf) == 0xf) && (TAG_DECODE(tag) < 32)) + +/* note: prints function name for you */ +#ifdef CARM_DEBUG +#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#ifdef CARM_VERBOSE_DEBUG +#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#else +#define VPRINTK(fmt, args...) +#endif /* CARM_VERBOSE_DEBUG */ +#else +#define DPRINTK(fmt, args...) +#define VPRINTK(fmt, args...) +#endif /* CARM_DEBUG */ + +#ifdef CARM_NDEBUG +#define assert(expr) +#else +#define assert(expr) \ + if(unlikely(!(expr))) { \ + printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ + #expr,__FILE__,__FUNCTION__,__LINE__); \ + } +#endif + +/* defines only for the constants which don't work well as enums */ +struct carm_host; + +enum { + /* adapter-wide limits */ + CARM_MAX_PORTS = 8, + CARM_SHM_SIZE = (4096 << 7), + CARM_PART_SHIFT = 5, + CARM_MINORS_PER_MAJOR = (1 << CARM_PART_SHIFT), + CARM_MAX_WAIT_Q = CARM_MAX_PORTS + 1, + + /* command message queue limits */ + CARM_MAX_REQ = 64, /* max command msgs per host */ + CARM_MAX_Q = 1, /* one command at a time */ + CARM_MSG_LOW_WATER = (CARM_MAX_REQ / 4), /* refill mark */ + + /* S/G limits, host-wide and per-request */ + CARM_MAX_REQ_SG = 32, /* max s/g entries per request */ + CARM_SG_BOUNDARY = 0xffffUL, /* s/g segment boundary */ + CARM_MAX_HOST_SG = 600, /* max s/g entries per host */ + CARM_SG_LOW_WATER = (CARM_MAX_HOST_SG / 4), /* re-fill mark */ + + /* hardware registers */ + CARM_IHQP = 0x1c, + CARM_INT_STAT = 0x10, /* interrupt status */ + CARM_INT_MASK = 0x14, /* interrupt mask */ + CARM_HMUC = 0x18, /* host message unit control */ + RBUF_ADDR_LO = 0x20, /* response msg DMA buf low 32 bits */ + RBUF_ADDR_HI = 0x24, /* response msg DMA buf high 32 bits */ + RBUF_BYTE_SZ = 0x28, + CARM_RESP_IDX = 0x2c, + CARM_CMS0 = 0x30, /* command message size reg 0 */ + CARM_LMUC = 0x48, + CARM_HMPHA = 0x6c, + CARM_INITC = 0xb5, + + /* bits in CARM_INT_{STAT,MASK} */ + INT_RESERVED = 0xfffffff0, + INT_WATCHDOG = (1 << 3), /* watchdog timer */ + INT_Q_OVERFLOW = (1 << 2), /* cmd msg q overflow */ + INT_Q_AVAILABLE = (1 << 1), /* cmd msg q has free space */ + INT_RESPONSE = (1 << 0), /* response msg available */ + INT_ACK_MASK = INT_WATCHDOG | INT_Q_OVERFLOW, + INT_DEF_MASK = INT_RESERVED | INT_Q_OVERFLOW | + INT_RESPONSE, + + /* command messages, and related register bits */ + CARM_HAVE_RESP = 0x01, + CARM_MSG_READ = 1, + CARM_MSG_WRITE = 2, + CARM_MSG_VERIFY = 3, + CARM_MSG_GET_CAPACITY = 4, + CARM_MSG_FLUSH = 5, + CARM_MSG_IOCTL = 6, + CARM_MSG_ARRAY = 8, + CARM_MSG_MISC = 9, + CARM_CME = (1 << 2), + CARM_RME = (1 << 1), + CARM_WZBC = (1 << 0), + CARM_RMI = (1 << 0), + CARM_Q_FULL = (1 << 3), + CARM_MSG_SIZE = 288, + CARM_Q_LEN = 48, + + /* CARM_MSG_IOCTL messages */ + CARM_IOC_SCAN_CHAN = 5, /* scan channels for devices */ + CARM_IOC_GET_TCQ = 13, /* get tcq/ncq depth */ + CARM_IOC_SET_TCQ = 14, /* set tcq/ncq depth */ + + IOC_SCAN_CHAN_NODEV = 0x1f, + IOC_SCAN_CHAN_OFFSET = 0x40, + + /* CARM_MSG_ARRAY messages */ + CARM_ARRAY_INFO = 0, + + ARRAY_NO_EXIST = (1 << 31), + + /* response messages */ + RMSG_SZ = 8, /* sizeof(struct carm_response) */ + RMSG_Q_LEN = 48, /* resp. msg list length */ + RMSG_OK = 1, /* bit indicating msg was successful */ + /* length of entire resp. msg buffer */ + RBUF_LEN = RMSG_SZ * RMSG_Q_LEN, + + PDC_SHM_SIZE = (4096 << 7), /* length of entire h/w buffer */ + + /* CARM_MSG_MISC messages */ + MISC_GET_FW_VER = 2, + MISC_ALLOC_MEM = 3, + MISC_SET_TIME = 5, + + /* MISC_GET_FW_VER feature bits */ + FW_VER_4PORT = (1 << 2), /* 1=4 ports, 0=8 ports */ + FW_VER_NON_RAID = (1 << 1), /* 1=non-RAID firmware, 0=RAID */ + FW_VER_ZCR = (1 << 0), /* zero channel RAID (whatever that is) */ + + /* carm_host flags */ + FL_NON_RAID = FW_VER_NON_RAID, + FL_4PORT = FW_VER_4PORT, + FL_FW_VER_MASK = (FW_VER_NON_RAID | FW_VER_4PORT), + FL_DAC = (1 << 16), + FL_DYN_MAJOR = (1 << 17), +}; + +enum carm_magic_numbers { + CARM_MAGIC_HOST = 0xdeadbeefUL, + CARM_MAGIC_PORT = 0xbedac0edUL, +}; + +enum scatter_gather_types { + SGT_32BIT = 0, + SGT_64BIT = 1, +}; + +enum host_states { + HST_INVALID, /* invalid state; never used */ + HST_ALLOC_BUF, /* setting up master SHM area */ + HST_ERROR, /* we never leave here */ + HST_PORT_SCAN, /* start dev scan */ + HST_DEV_SCAN_START, /* start per-device probe */ + HST_DEV_SCAN, /* continue per-device probe */ + HST_DEV_ACTIVATE, /* activate devices we found */ + HST_PROBE_FINISHED, /* probe is complete */ + HST_PROBE_START, /* initiate probe */ + HST_SYNC_TIME, /* tell firmware what time it is */ + HST_GET_FW_VER, /* get firmware version, adapter port cnt */ +}; + +#ifdef CARM_DEBUG +static const char *state_name[] = { + "HST_INVALID", + "HST_ALLOC_BUF", + "HST_ERROR", + "HST_PORT_SCAN", + "HST_DEV_SCAN_START", + "HST_DEV_SCAN", + "HST_DEV_ACTIVATE", + "HST_PROBE_FINISHED", + "HST_PROBE_START", + "HST_SYNC_TIME", + "HST_GET_FW_VER", +}; +#endif + +struct carm_port { + unsigned long magic; + unsigned int port_no; + unsigned int n_queued; + struct carm_host *host; + struct tasklet_struct tasklet; + request_queue_t q; + + /* attached device characteristics */ + u64 capacity; + char name[41]; + u16 dev_geom_head; + u16 dev_geom_sect; + u16 dev_geom_cyl; +}; + +struct carm_request { + unsigned int tag; + int n_elem; + unsigned int msg_type; + unsigned int msg_subtype; + unsigned int msg_bucket; + struct request *rq; + struct carm_port *port; + struct request special_rq; + struct scatterlist sg[CARM_MAX_REQ_SG]; +}; + +struct carm_host { + unsigned long magic; + unsigned long flags; + void *mmio; + void *shm; + dma_addr_t shm_dma; + + int major; + int id; + char name[32]; + + struct pci_dev *pdev; + unsigned int state; + u32 fw_ver; + + request_queue_t oob_q; + unsigned int n_oob; + struct tasklet_struct oob_tasklet; + + unsigned int hw_sg_used; + + unsigned int resp_idx; + + unsigned int wait_q_prod; + unsigned int wait_q_cons; + request_queue_t *wait_q[CARM_MAX_WAIT_Q]; + + unsigned int n_msgs; + u64 msg_alloc; + struct carm_request req[CARM_MAX_REQ]; + void *msg_base; + dma_addr_t msg_dma; + + int cur_scan_dev; + unsigned long dev_active; + unsigned long dev_present; + struct carm_port port[CARM_MAX_PORTS]; + + struct tq_struct fsm_task; + + struct semaphore probe_sem; + + struct gendisk gendisk; + struct hd_struct gendisk_hd[256]; + int blk_sizes[256]; + int blk_block_sizes[256]; + int blk_sect_sizes[256]; + + struct list_head host_list_node; +}; + +struct carm_response { + u32 ret_handle; + u32 status; +} __attribute__((packed)); + +struct carm_msg_sg { + u32 start; + u32 len; +} __attribute__((packed)); + +struct carm_msg_rw { + u8 type; + u8 id; + u8 sg_count; + u8 sg_type; + u32 handle; + u32 lba; + u16 lba_count; + u16 lba_high; + struct carm_msg_sg sg[32]; +} __attribute__((packed)); + +struct carm_msg_allocbuf { + u8 type; + u8 subtype; + u8 n_sg; + u8 sg_type; + u32 handle; + u32 addr; + u32 len; + u32 evt_pool; + u32 n_evt; + u32 rbuf_pool; + u32 n_rbuf; + u32 msg_pool; + u32 n_msg; + struct carm_msg_sg sg[8]; +} __attribute__((packed)); + +struct carm_msg_ioctl { + u8 type; + u8 subtype; + u8 array_id; + u8 reserved1; + u32 handle; + u32 data_addr; + u32 reserved2; +} __attribute__((packed)); + +struct carm_msg_sync_time { + u8 type; + u8 subtype; + u16 reserved1; + u32 handle; + u32 reserved2; + u32 timestamp; +} __attribute__((packed)); + +struct carm_msg_get_fw_ver { + u8 type; + u8 subtype; + u16 reserved1; + u32 handle; + u32 data_addr; + u32 reserved2; +} __attribute__((packed)); + +struct carm_fw_ver { + u32 version; + u8 features; + u8 reserved1; + u16 reserved2; +} __attribute__((packed)); + +struct carm_array_info { + u32 size; + + u16 size_hi; + u16 stripe_size; + + u32 mode; + + u16 stripe_blk_sz; + u16 reserved1; + + u16 cyl; + u16 head; + + u16 sect; + u8 array_id; + u8 reserved2; + + char name[40]; + + u32 array_status; + + /* device list continues beyond this point? */ +} __attribute__((packed)); + +static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static void carm_remove_one (struct pci_dev *pdev); +static int carm_bdev_ioctl(struct inode *ino, struct file *fil, + unsigned int cmd, unsigned long arg); +static request_queue_t *carm_find_queue(kdev_t device); +static int carm_revalidate_disk(kdev_t dev); + +static struct pci_device_id carm_pci_tbl[] = { + { PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + { PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, + { } /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci, carm_pci_tbl); + +static struct pci_driver carm_driver = { + .name = DRV_NAME, + .id_table = carm_pci_tbl, + .probe = carm_init_one, + .remove = carm_remove_one, +}; + +static struct block_device_operations carm_bd_ops = { + .owner = THIS_MODULE, + .ioctl = carm_bdev_ioctl, +}; + +static unsigned int carm_host_id; +static unsigned long carm_major_alloc; + + +static struct carm_host *carm_from_dev(kdev_t dev, struct carm_port **port_out) +{ + struct carm_host *host; + struct carm_port *port; + request_queue_t *q; + + q = carm_find_queue(dev); + if (!q || !q->queuedata) { + printk(KERN_ERR PFX "queue not found for major %d minor %d\n", + MAJOR(dev), MINOR(dev)); + return NULL; + } + + port = q->queuedata; + if (unlikely(port->magic != CARM_MAGIC_PORT)) { + printk(KERN_ERR PFX "bad port magic number for major %d minor %d\n", + MAJOR(dev), MINOR(dev)); + return NULL; + } + + host = port->host; + if (unlikely(host->magic != CARM_MAGIC_HOST)) { + printk(KERN_ERR PFX "bad host magic number for major %d minor %d\n", + MAJOR(dev), MINOR(dev)); + return NULL; + } + + if (port_out) + *port_out = port; + return host; +} + +static int carm_bdev_ioctl(struct inode *ino, struct file *fil, + unsigned int cmd, unsigned long arg) +{ + void *usermem = (void *) arg; + struct carm_port *port = NULL; + struct carm_host *host; + + host = carm_from_dev(ino->i_rdev, &port); + if (!host) + return -EINVAL; + + switch (cmd) { + case HDIO_GETGEO: { + struct hd_geometry geom; + + if (!usermem) + return -EINVAL; + + if (port->dev_geom_cyl) { + geom.heads = port->dev_geom_head; + geom.sectors = port->dev_geom_sect; + geom.cylinders = port->dev_geom_cyl; + } else { + u32 tmp = ((u32)port->capacity) / (0xff * 0x3f); + geom.heads = 0xff; + geom.sectors = 0x3f; + if (tmp > 65536) + geom.cylinders = 0xffff; + else + geom.cylinders = tmp; + } + geom.start = host->gendisk_hd[MINOR(ino->i_rdev)].start_sect; + + if (copy_to_user(usermem, &geom, sizeof(geom))) + return -EFAULT; + return 0; + } + + case HDIO_GETGEO_BIG: { + struct hd_big_geometry geom; + + if (!usermem) + return -EINVAL; + + if (port->dev_geom_cyl) { + geom.heads = port->dev_geom_head; + geom.sectors = port->dev_geom_sect; + geom.cylinders = port->dev_geom_cyl; + } else { + geom.heads = 0xff; + geom.sectors = 0x3f; + geom.cylinders = ((u32)port->capacity) / (0xff * 0x3f); + } + geom.start = host->gendisk_hd[MINOR(ino->i_rdev)].start_sect; + + if (copy_to_user(usermem, &geom, sizeof(geom))) + return -EFAULT; + return 0; + } + + case BLKRRPART: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return carm_revalidate_disk(ino->i_rdev); + + case BLKGETSIZE: + case BLKGETSIZE64: + case BLKFLSBUF: + case BLKBSZSET: + case BLKBSZGET: + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKPG: + case BLKELVGET: + case BLKELVSET: + return blk_ioctl(ino->i_rdev, cmd, arg); + + default: + break; + } + + return -EOPNOTSUPP; +} + +static inline unsigned long msecs_to_jiffies(unsigned long msecs) +{ + return ((HZ * msecs + 999) / 1000); +} + +static void msleep(unsigned long msecs) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(msecs) + 1); +} + +static const u32 msg_sizes[] = { 32, 64, 128, CARM_MSG_SIZE }; + +static inline int carm_lookup_bucket(u32 msg_size) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msg_sizes); i++) + if (msg_size <= msg_sizes[i]) + return i; + + return -ENOENT; +} + +static void carm_init_buckets(void *mmio) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(msg_sizes); i++) + writel(msg_sizes[i], mmio + CARM_CMS0 + (4 * i)); +} + +static inline void *carm_ref_msg(struct carm_host *host, + unsigned int msg_idx) +{ + return host->msg_base + (msg_idx * CARM_MSG_SIZE); +} + +static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host, + unsigned int msg_idx) +{ + return host->msg_dma + (msg_idx * CARM_MSG_SIZE); +} + +static int carm_send_msg(struct carm_host *host, + struct carm_request *crq) +{ + void *mmio = host->mmio; + u32 msg = (u32) carm_ref_msg_dma(host, crq->tag); + u32 cm_bucket = crq->msg_bucket; + u32 tmp; + int rc = 0; + + VPRINTK("ENTER\n"); + + tmp = readl(mmio + CARM_HMUC); + if (tmp & CARM_Q_FULL) { +#if 0 + tmp = readl(mmio + CARM_INT_MASK); + tmp |= INT_Q_AVAILABLE; + writel(tmp, mmio + CARM_INT_MASK); + readl(mmio + CARM_INT_MASK); /* flush */ +#endif + DPRINTK("host msg queue full\n"); + rc = -EBUSY; + } else { + writel(msg | (cm_bucket << 1), mmio + CARM_IHQP); + readl(mmio + CARM_IHQP); /* flush */ + } + + return rc; +} + +static struct carm_request *carm_get_request(struct carm_host *host) +{ + unsigned int i; + + /* obey global hardware limit on S/G entries */ + if (host->hw_sg_used >= (CARM_MAX_HOST_SG - CARM_MAX_REQ_SG)) + return NULL; + + for (i = 0; i < CARM_MAX_Q; i++) + if ((host->msg_alloc & (1ULL << i)) == 0) { + struct carm_request *crq = &host->req[i]; + crq->port = NULL; + crq->n_elem = 0; + + host->msg_alloc |= (1ULL << i); + host->n_msgs++; + + assert(host->n_msgs <= CARM_MAX_REQ); + return crq; + } + + DPRINTK("no request available, returning NULL\n"); + return NULL; +} + +static int carm_put_request(struct carm_host *host, struct carm_request *crq) +{ + assert(crq->tag < CARM_MAX_Q); + + if (unlikely((host->msg_alloc & (1ULL << crq->tag)) == 0)) + return -EINVAL; /* tried to clear a tag that was not active */ + + assert(host->hw_sg_used >= crq->n_elem); + + host->msg_alloc &= ~(1ULL << crq->tag); + host->hw_sg_used -= crq->n_elem; + host->n_msgs--; + + return 0; +} + +static void carm_insert_special(request_queue_t *q, struct request *rq, + void *data, int at_head) +{ + unsigned long flags; + + rq->cmd = SPECIAL; + rq->special = data; + rq->q = NULL; + rq->nr_segments = 0; + rq->elevator_sequence = 0; + + spin_lock_irqsave(&io_request_lock, flags); + if (at_head) + list_add(&rq->queue, &q->queue_head); + else + list_add_tail(&rq->queue, &q->queue_head); + q->request_fn(q); + spin_unlock_irqrestore(&io_request_lock, flags); +} + +static struct carm_request *carm_get_special(struct carm_host *host) +{ + unsigned long flags; + struct carm_request *crq = NULL; + int tries = 5000; + + while (tries-- > 0) { + spin_lock_irqsave(&io_request_lock, flags); + crq = carm_get_request(host); + spin_unlock_irqrestore(&io_request_lock, flags); + + if (crq) + break; + msleep(10); + } + + if (!crq) + return NULL; + + crq->rq = &crq->special_rq; + return crq; +} + +static int carm_array_info (struct carm_host *host, unsigned int array_idx) +{ + struct carm_msg_ioctl *ioc; + unsigned int idx; + u32 msg_data; + dma_addr_t msg_dma; + struct carm_request *crq; + int rc; + unsigned long flags; + + crq = carm_get_special(host); + if (!crq) { + rc = -ENOMEM; + goto err_out; + } + + idx = crq->tag; + + ioc = carm_ref_msg(host, idx); + msg_dma = carm_ref_msg_dma(host, idx); + msg_data = (u32) (msg_dma + sizeof(struct carm_array_info)); + + crq->msg_type = CARM_MSG_ARRAY; + crq->msg_subtype = CARM_ARRAY_INFO; + rc = carm_lookup_bucket(sizeof(struct carm_msg_ioctl) + + sizeof(struct carm_array_info)); + BUG_ON(rc < 0); + crq->msg_bucket = (u32) rc; + + memset(ioc, 0, sizeof(*ioc)); + ioc->type = CARM_MSG_ARRAY; + ioc->subtype = CARM_ARRAY_INFO; + ioc->array_id = (u8) array_idx; + ioc->handle = cpu_to_le32(TAG_ENCODE(idx)); + ioc->data_addr = cpu_to_le32(msg_data); + + assert(host->state == HST_DEV_SCAN_START || + host->state == HST_DEV_SCAN); + + DPRINTK("blk_insert_request, tag == %u\n", idx); + carm_insert_special(&host->oob_q, crq->rq, crq, 1); + + return 0; + +err_out: + spin_lock_irqsave(&io_request_lock, flags); + host->state = HST_ERROR; + spin_unlock_irqrestore(&io_request_lock, flags); + return rc; +} + +typedef unsigned int (*carm_sspc_t)(struct carm_host *, unsigned int, void *); + +static int carm_send_special (struct carm_host *host, carm_sspc_t func) +{ + struct carm_request *crq; + struct carm_msg_ioctl *ioc; + void *mem; + unsigned int idx, msg_size; + int rc; + + crq = carm_get_special(host); + if (!crq) + return -ENOMEM; + + idx = crq->tag; + + mem = carm_ref_msg(host, idx); + + msg_size = func(host, idx, mem); + + ioc = mem; + crq->msg_type = ioc->type; + crq->msg_subtype = ioc->subtype; + rc = carm_lookup_bucket(msg_size); + BUG_ON(rc < 0); + crq->msg_bucket = (u32) rc; + + DPRINTK("blk_insert_request, tag == %u\n", idx); + carm_insert_special(&host->oob_q, crq->rq, crq, 1); + + return 0; +} + +static unsigned int carm_fill_sync_time(struct carm_host *host, + unsigned int idx, void *mem) +{ + struct timeval tv; + struct carm_msg_sync_time *st = mem; + + do_gettimeofday(&tv); + + memset(st, 0, sizeof(*st)); + st->type = CARM_MSG_MISC; + st->subtype = MISC_SET_TIME; + st->handle = cpu_to_le32(TAG_ENCODE(idx)); + st->timestamp = cpu_to_le32(tv.tv_sec); + + return sizeof(struct carm_msg_sync_time); +} + +static unsigned int carm_fill_alloc_buf(struct carm_host *host, + unsigned int idx, void *mem) +{ + struct carm_msg_allocbuf *ab = mem; + + memset(ab, 0, sizeof(*ab)); + ab->type = CARM_MSG_MISC; + ab->subtype = MISC_ALLOC_MEM; + ab->handle = cpu_to_le32(TAG_ENCODE(idx)); + ab->n_sg = 1; + ab->sg_type = SGT_32BIT; + ab->addr = cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1)); + ab->len = cpu_to_le32(PDC_SHM_SIZE >> 1); + ab->evt_pool = cpu_to_le32(host->shm_dma + (16 * 1024)); + ab->n_evt = cpu_to_le32(1024); + ab->rbuf_pool = cpu_to_le32(host->shm_dma); + ab->n_rbuf = cpu_to_le32(RMSG_Q_LEN); + ab->msg_pool = cpu_to_le32(host->shm_dma + RBUF_LEN); + ab->n_msg = cpu_to_le32(CARM_Q_LEN); + ab->sg[0].start = cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1)); + ab->sg[0].len = cpu_to_le32(65536); + + return sizeof(struct carm_msg_allocbuf); +} + +static unsigned int carm_fill_scan_channels(struct carm_host *host, + unsigned int idx, void *mem) +{ + struct carm_msg_ioctl *ioc = mem; + u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + + IOC_SCAN_CHAN_OFFSET); + + memset(ioc, 0, sizeof(*ioc)); + ioc->type = CARM_MSG_IOCTL; + ioc->subtype = CARM_IOC_SCAN_CHAN; + ioc->handle = cpu_to_le32(TAG_ENCODE(idx)); + ioc->data_addr = cpu_to_le32(msg_data); + + /* fill output data area with "no device" default values */ + mem += IOC_SCAN_CHAN_OFFSET; + memset(mem, IOC_SCAN_CHAN_NODEV, CARM_MAX_PORTS); + + return IOC_SCAN_CHAN_OFFSET + CARM_MAX_PORTS; +} + +static unsigned int carm_fill_get_fw_ver(struct carm_host *host, + unsigned int idx, void *mem) +{ + struct carm_msg_get_fw_ver *ioc = mem; + u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + sizeof(*ioc)); + + memset(ioc, 0, sizeof(*ioc)); + ioc->type = CARM_MSG_MISC; + ioc->subtype = MISC_GET_FW_VER; + ioc->handle = cpu_to_le32(TAG_ENCODE(idx)); + ioc->data_addr = cpu_to_le32(msg_data); + + return sizeof(struct carm_msg_get_fw_ver) + + sizeof(struct carm_fw_ver); +} + +static void carm_activate_disk(struct carm_host *host, + struct carm_port *port) +{ + int minor_start = port->port_no << CARM_PART_SHIFT; + int start, end, i; + + host->gendisk_hd[minor_start].nr_sects = port->capacity; + host->blk_sizes[minor_start] = port->capacity; + + start = minor_start; + end = minor_start + CARM_MINORS_PER_MAJOR; + for (i = start; i < end; i++) { + invalidate_device(MKDEV(host->major, i), 1); + host->gendisk.part[i].start_sect = 0; + host->gendisk.part[i].nr_sects = 0; + host->blk_block_sizes[i] = 512; + host->blk_sect_sizes[i] = 512; + } + + grok_partitions(&host->gendisk, port->port_no, + CARM_MINORS_PER_MAJOR, + port->capacity); +} + +static int carm_revalidate_disk(kdev_t dev) +{ + struct carm_host *host; + struct carm_port *port = NULL; + + host = carm_from_dev(dev, &port); + if (!host) + return -EINVAL; + + carm_activate_disk(host, port); + + return 0; +} + +static inline void complete_buffers(struct buffer_head *bh, int status) +{ + struct buffer_head *xbh; + + while (bh) { + xbh = bh->b_reqnext; + bh->b_reqnext = NULL; + blk_finished_io(bh->b_size >> 9); + bh->b_end_io(bh, status); + bh = xbh; + } +} + +static inline void carm_end_request_queued(struct carm_host *host, + struct carm_request *crq, + int uptodate) +{ + struct request *req = crq->rq; + int rc; + + complete_buffers(req->bh, uptodate); + end_that_request_last(req); + + rc = carm_put_request(host, crq); + assert(rc == 0); +} + +static inline void carm_push_q (struct carm_host *host, request_queue_t *q) +{ + unsigned int idx = host->wait_q_prod % CARM_MAX_WAIT_Q; + + VPRINTK("STOPPED QUEUE %p\n", q); + + host->wait_q[idx] = q; + host->wait_q_prod++; + BUG_ON(host->wait_q_prod == host->wait_q_cons); /* overrun */ +} + +static inline request_queue_t *carm_pop_q(struct carm_host *host) +{ + unsigned int idx; + + if (host->wait_q_prod == host->wait_q_cons) + return NULL; + + idx = host->wait_q_cons % CARM_MAX_WAIT_Q; + host->wait_q_cons++; + + return host->wait_q[idx]; +} + +static inline void carm_round_robin(struct carm_host *host) +{ + request_queue_t *q = carm_pop_q(host); + if (q) { + struct tasklet_struct *tasklet; + if (q == &host->oob_q) + tasklet = &host->oob_tasklet; + else { + struct carm_port *port = q->queuedata; + tasklet = &port->tasklet; + } + tasklet_schedule(tasklet); + VPRINTK("STARTED QUEUE %p\n", q); + } +} + +static inline void carm_end_rq(struct carm_host *host, struct carm_request *crq, + int is_ok) +{ + carm_end_request_queued(host, crq, is_ok); + if (CARM_MAX_Q == 1) + carm_round_robin(host); + else if ((host->n_msgs <= CARM_MSG_LOW_WATER) && + (host->hw_sg_used <= CARM_SG_LOW_WATER)) { + carm_round_robin(host); + } +} + +static inline int carm_new_segment(request_queue_t *q, struct request *rq) +{ + if (rq->nr_segments < CARM_MAX_REQ_SG) { + rq->nr_segments++; + return 1; + } + return 0; +} + +static int carm_back_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segments) +{ + if (blk_seg_merge_ok(rq->bhtail, bh)) + return 1; + return carm_new_segment(q, rq); +} + +static int carm_front_merge_fn(request_queue_t *q, struct request *rq, + struct buffer_head *bh, int max_segments) +{ + if (blk_seg_merge_ok(bh, rq->bh)) + return 1; + return carm_new_segment(q, rq); +} + +static int carm_merge_requests_fn(request_queue_t *q, struct request *rq, + struct request *nxt, int max_segments) +{ + int total_segments = rq->nr_segments + nxt->nr_segments; + + if (blk_seg_merge_ok(rq->bhtail, nxt->bh)) + total_segments--; + + if (total_segments > CARM_MAX_REQ_SG) + return 0; + + rq->nr_segments = total_segments; + return 1; +} + +static void carm_oob_rq_fn(request_queue_t *q) +{ + struct carm_host *host = q->queuedata; + + tasklet_schedule(&host->oob_tasklet); +} + +static void carm_rq_fn(request_queue_t *q) +{ + struct carm_port *port = q->queuedata; + + tasklet_schedule(&port->tasklet); +} + +static void carm_oob_tasklet(unsigned long _data) +{ + struct carm_host *host = (void *) _data; + request_queue_t *q = &host->oob_q; + struct carm_request *crq; + struct request *rq; + int rc, have_work = 1; + struct list_head *queue_head = &q->queue_head; + unsigned long flags; + + spin_lock_irqsave(&io_request_lock, flags); + if (q->plugged || list_empty(queue_head)) + have_work = 0; + + if (!have_work) + goto out; + + while (1) { + DPRINTK("get req\n"); + if (list_empty(queue_head)) + break; + + rq = blkdev_entry_next_request(queue_head); + + crq = rq->special; + assert(crq != NULL); + assert(crq->rq == rq); + + crq->n_elem = 0; + + DPRINTK("send req\n"); + rc = carm_send_msg(host, crq); + if (rc) { + carm_push_q(host, q); + break; /* call us again later, eventually */ + } else + blkdev_dequeue_request(rq); + } + +out: + spin_unlock_irqrestore(&io_request_lock, flags); +} + +static int blk_rq_map_sg(request_queue_t *q, struct request *rq, + struct scatterlist *sg) +{ + int n_elem = 0; + struct buffer_head *bh = rq->bh; + u64 last_phys = ~0ULL; + + while (bh) { + if (bh_phys(bh) == last_phys) { + sg[n_elem - 1].length += bh->b_size; + last_phys += bh->b_size; + } else { + if (unlikely(n_elem == CARM_MAX_REQ_SG)) + BUG(); + sg[n_elem].page = bh->b_page; + sg[n_elem].length = bh->b_size; + sg[n_elem].offset = bh_offset(bh); + last_phys = bh_phys(bh) + bh->b_size; + n_elem++; + } + + bh = bh->b_reqnext; + } + + return n_elem; +} + +static void carm_rw_tasklet(unsigned long _data) +{ + struct carm_port *port = (void *) _data; + struct carm_host *host = port->host; + request_queue_t *q = &port->q; + struct carm_msg_rw *msg; + struct carm_request *crq; + struct request *rq; + struct scatterlist *sg; + int writing = 0, pci_dir, i, n_elem, rc, have_work = 1; + u32 tmp; + unsigned int msg_size; + unsigned long flags; + struct list_head *queue_head = &q->queue_head; + unsigned long start_sector; + + spin_lock_irqsave(&io_request_lock, flags); + if (q->plugged || list_empty(queue_head)) + have_work = 0; + + if (!have_work) + goto out; + +queue_one_request: + VPRINTK("get req\n"); + if (list_empty(queue_head)) + goto out; + + rq = blkdev_entry_next_request(queue_head); + + crq = carm_get_request(host); + if (!crq) { + carm_push_q(host, q); + goto out; /* call us again later, eventually */ + } + crq->rq = rq; + + if (rq_data_dir(rq) == WRITE) { + writing = 1; + pci_dir = PCI_DMA_TODEVICE; + } else { + pci_dir = PCI_DMA_FROMDEVICE; + } + + /* get scatterlist from block layer */ + sg = &crq->sg[0]; + n_elem = blk_rq_map_sg(q, rq, sg); + if (n_elem <= 0) { + carm_end_rq(host, crq, 0); + goto out; /* request with no s/g entries? */ + } + + /* map scatterlist to PCI bus addresses */ + n_elem = pci_map_sg(host->pdev, sg, n_elem, pci_dir); + if (n_elem <= 0) { + carm_end_rq(host, crq, 0); + goto out; /* request with no s/g entries? */ + } + crq->n_elem = n_elem; + crq->port = port; + host->hw_sg_used += n_elem; + + /* + * build read/write message + */ + + VPRINTK("build msg\n"); + msg = (struct carm_msg_rw *) carm_ref_msg(host, crq->tag); + + if (writing) { + msg->type = CARM_MSG_WRITE; + crq->msg_type = CARM_MSG_WRITE; + } else { + msg->type = CARM_MSG_READ; + crq->msg_type = CARM_MSG_READ; + } + + start_sector = rq->sector; + start_sector += host->gendisk_hd[MINOR(rq->rq_dev)].start_sect; + + msg->id = port->port_no; + msg->sg_count = n_elem; + msg->sg_type = SGT_32BIT; + msg->handle = cpu_to_le32(TAG_ENCODE(crq->tag)); + msg->lba = cpu_to_le32(start_sector & 0xffffffff); + tmp = (start_sector >> 16) >> 16; + msg->lba_high = cpu_to_le16( (u16) tmp ); + msg->lba_count = cpu_to_le16(rq->nr_sectors); + + msg_size = sizeof(struct carm_msg_rw) - sizeof(msg->sg); + for (i = 0; i < n_elem; i++) { + struct carm_msg_sg *carm_sg = &msg->sg[i]; + carm_sg->start = cpu_to_le32(sg_dma_address(&crq->sg[i])); + carm_sg->len = cpu_to_le32(sg_dma_len(&crq->sg[i])); + msg_size += sizeof(struct carm_msg_sg); + } + + rc = carm_lookup_bucket(msg_size); + BUG_ON(rc < 0); + crq->msg_bucket = (u32) rc; + + /* + * queue read/write message to hardware + */ + + VPRINTK("send msg, tag == %u\n", crq->tag); + rc = carm_send_msg(host, crq); + if (rc) { + carm_put_request(host, crq); + carm_push_q(host, q); + goto out; /* call us again later, eventually */ + } else + blkdev_dequeue_request(rq); + + goto queue_one_request; + +out: + spin_unlock_irqrestore(&io_request_lock, flags); +} + +static void carm_handle_array_info(struct carm_host *host, + struct carm_request *crq, u8 *mem, + int is_ok) +{ + struct carm_port *port; + u8 *msg_data = mem + sizeof(struct carm_array_info); + struct carm_array_info *desc = (struct carm_array_info *) msg_data; + u64 lo, hi; + int cur_port; + size_t slen; + + DPRINTK("ENTER\n"); + + carm_end_rq(host, crq, is_ok); + + if (!is_ok) + goto out; + if (le32_to_cpu(desc->array_status) & ARRAY_NO_EXIST) + goto out; + + cur_port = host->cur_scan_dev; + + /* should never occur */ + if ((cur_port < 0) || (cur_port >= CARM_MAX_PORTS)) { + printk(KERN_ERR PFX "BUG: cur_scan_dev==%d, array_id==%d\n", + cur_port, (int) desc->array_id); + goto out; + } + + port = &host->port[cur_port]; + + lo = (u64) le32_to_cpu(desc->size); + hi = (u64) le32_to_cpu(desc->size_hi); + + port->capacity = lo | (hi << 32); + port->dev_geom_head = le16_to_cpu(desc->head); + port->dev_geom_sect = le16_to_cpu(desc->sect); + port->dev_geom_cyl = le16_to_cpu(desc->cyl); + + host->dev_active |= (1 << cur_port); + + strncpy(port->name, desc->name, sizeof(port->name)); + port->name[sizeof(port->name) - 1] = 0; + slen = strlen(port->name); + while (slen && (port->name[slen - 1] == ' ')) { + port->name[slen - 1] = 0; + slen--; + } + + printk(KERN_INFO DRV_NAME "(%s): port %u device %Lu sectors\n", + pci_name(host->pdev), port->port_no, port->capacity); + printk(KERN_INFO DRV_NAME "(%s): port %u device \"%s\"\n", + pci_name(host->pdev), port->port_no, port->name); + +out: + assert(host->state == HST_DEV_SCAN); + schedule_task(&host->fsm_task); +} + +static void carm_handle_scan_chan(struct carm_host *host, + struct carm_request *crq, u8 *mem, + int is_ok) +{ + u8 *msg_data = mem + IOC_SCAN_CHAN_OFFSET; + unsigned int i, dev_count = 0; + int new_state = HST_DEV_SCAN_START; + + DPRINTK("ENTER\n"); + + carm_end_rq(host, crq, is_ok); + + if (!is_ok) { + new_state = HST_ERROR; + goto out; + } + + /* TODO: scan and support non-disk devices */ + for (i = 0; i < 8; i++) + if (msg_data[i] == 0) { /* direct-access device (disk) */ + host->dev_present |= (1 << i); + dev_count++; + } + + printk(KERN_INFO DRV_NAME "(%s): found %u interesting devices\n", + pci_name(host->pdev), dev_count); + +out: + assert(host->state == HST_PORT_SCAN); + host->state = new_state; + schedule_task(&host->fsm_task); +} + +static void carm_handle_generic(struct carm_host *host, + struct carm_request *crq, int is_ok, + int cur_state, int next_state) +{ + DPRINTK("ENTER\n"); + + carm_end_rq(host, crq, is_ok); + + assert(host->state == cur_state); + if (is_ok) + host->state = next_state; + else + host->state = HST_ERROR; + schedule_task(&host->fsm_task); +} + +static inline void carm_handle_rw(struct carm_host *host, + struct carm_request *crq, int is_ok) +{ + int pci_dir; + + VPRINTK("ENTER\n"); + + if (rq_data_dir(crq->rq) == WRITE) + pci_dir = PCI_DMA_TODEVICE; + else + pci_dir = PCI_DMA_FROMDEVICE; + + pci_unmap_sg(host->pdev, &crq->sg[0], crq->n_elem, pci_dir); + + carm_end_rq(host, crq, is_ok); +} + +static inline void carm_handle_resp(struct carm_host *host, + u32 ret_handle_le, u32 status) +{ + u32 handle = le32_to_cpu(ret_handle_le); + unsigned int msg_idx; + struct carm_request *crq; + int is_ok = (status == RMSG_OK); + u8 *mem; + + VPRINTK("ENTER, handle == 0x%x\n", handle); + + if (unlikely(!TAG_VALID(handle))) { + printk(KERN_ERR DRV_NAME "(%s): BUG: invalid tag 0x%x\n", + pci_name(host->pdev), handle); + return; + } + + msg_idx = TAG_DECODE(handle); + VPRINTK("tag == %u\n", msg_idx); + + crq = &host->req[msg_idx]; + + /* fast path */ + if (likely(crq->msg_type == CARM_MSG_READ || + crq->msg_type == CARM_MSG_WRITE)) { + carm_handle_rw(host, crq, is_ok); + return; + } + + mem = carm_ref_msg(host, msg_idx); + + switch (crq->msg_type) { + case CARM_MSG_IOCTL: { + switch (crq->msg_subtype) { + case CARM_IOC_SCAN_CHAN: + carm_handle_scan_chan(host, crq, mem, is_ok); + break; + default: + /* unknown / invalid response */ + goto err_out; + } + break; + } + + case CARM_MSG_MISC: { + switch (crq->msg_subtype) { + case MISC_ALLOC_MEM: + carm_handle_generic(host, crq, is_ok, + HST_ALLOC_BUF, HST_SYNC_TIME); + break; + case MISC_SET_TIME: + carm_handle_generic(host, crq, is_ok, + HST_SYNC_TIME, HST_GET_FW_VER); + break; + case MISC_GET_FW_VER: { + struct carm_fw_ver *ver = (struct carm_fw_ver *) + mem + sizeof(struct carm_msg_get_fw_ver); + if (is_ok) { + host->fw_ver = le32_to_cpu(ver->version); + host->flags |= (ver->features & FL_FW_VER_MASK); + } + carm_handle_generic(host, crq, is_ok, + HST_GET_FW_VER, HST_PORT_SCAN); + break; + } + default: + /* unknown / invalid response */ + goto err_out; + } + break; + } + + case CARM_MSG_ARRAY: { + switch (crq->msg_subtype) { + case CARM_ARRAY_INFO: + carm_handle_array_info(host, crq, mem, is_ok); + break; + default: + /* unknown / invalid response */ + goto err_out; + } + break; + } + + default: + /* unknown / invalid response */ + goto err_out; + } + + return; + +err_out: + printk(KERN_WARNING DRV_NAME "(%s): BUG: unhandled message type %d/%d\n", + pci_name(host->pdev), crq->msg_type, crq->msg_subtype); + carm_end_rq(host, crq, 0); +} + +static inline void carm_handle_responses(struct carm_host *host) +{ + void *mmio = host->mmio; + struct carm_response *resp = (struct carm_response *) host->shm; + unsigned int work = 0; + unsigned int idx = host->resp_idx % RMSG_Q_LEN; + + while (1) { + u32 status = le32_to_cpu(resp[idx].status); + + if (status == 0xffffffff) { + VPRINTK("ending response on index %u\n", idx); + writel(idx << 3, mmio + CARM_RESP_IDX); + break; + } + + /* response to a message we sent */ + else if ((status & (1 << 31)) == 0) { + VPRINTK("handling msg response on index %u\n", idx); + carm_handle_resp(host, resp[idx].ret_handle, status); + resp[idx].status = 0xffffffff; + } + + /* asynchronous events the hardware throws our way */ + else if ((status & 0xff000000) == (1 << 31)) { + u8 *evt_type_ptr = (u8 *) &resp[idx]; + u8 evt_type = *evt_type_ptr; + printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n", + pci_name(host->pdev), (int) evt_type); + resp[idx].status = 0xffffffff; + } + + idx = NEXT_RESP(idx); + work++; + } + + VPRINTK("EXIT, work==%u\n", work); + host->resp_idx += work; +} + +static irqreturn_t carm_interrupt(int irq, void *__host, struct pt_regs *regs) +{ + struct carm_host *host = __host; + void *mmio; + u32 mask; + int handled = 0; + unsigned long flags; + + if (!host) { + VPRINTK("no host\n"); + return IRQ_NONE; + } + + spin_lock_irqsave(&io_request_lock, flags); + + mmio = host->mmio; + + /* reading should also clear interrupts */ + mask = readl(mmio + CARM_INT_STAT); + + if (mask == 0 || mask == 0xffffffff) { + VPRINTK("no work, mask == 0x%x\n", mask); + goto out; + } + + if (mask & INT_ACK_MASK) + writel(mask, mmio + CARM_INT_STAT); + + if (unlikely(host->state == HST_INVALID)) { + VPRINTK("not initialized yet, mask = 0x%x\n", mask); + goto out; + } + + if (mask & CARM_HAVE_RESP) { + handled = 1; + carm_handle_responses(host); + } + +out: + spin_unlock_irqrestore(&io_request_lock, flags); + VPRINTK("EXIT\n"); + return IRQ_RETVAL(handled); +} + +static void carm_fsm_task (void *_data) +{ + struct carm_host *host = _data; + unsigned long flags; + unsigned int state; + int rc, i, next_dev; + int reschedule = 0; + int new_state = HST_INVALID; + + spin_lock_irqsave(&io_request_lock, flags); + state = host->state; + spin_unlock_irqrestore(&io_request_lock, flags); + + DPRINTK("ENTER, state == %s\n", state_name[state]); + + switch (state) { + case HST_PROBE_START: + new_state = HST_ALLOC_BUF; + reschedule = 1; + break; + + case HST_ALLOC_BUF: + rc = carm_send_special(host, carm_fill_alloc_buf); + if (rc) { + new_state = HST_ERROR; + reschedule = 1; + } + break; + + case HST_SYNC_TIME: + rc = carm_send_special(host, carm_fill_sync_time); + if (rc) { + new_state = HST_ERROR; + reschedule = 1; + } + break; + + case HST_GET_FW_VER: + rc = carm_send_special(host, carm_fill_get_fw_ver); + if (rc) { + new_state = HST_ERROR; + reschedule = 1; + } + break; + + case HST_PORT_SCAN: + rc = carm_send_special(host, carm_fill_scan_channels); + if (rc) { + new_state = HST_ERROR; + reschedule = 1; + } + break; + + case HST_DEV_SCAN_START: + host->cur_scan_dev = -1; + new_state = HST_DEV_SCAN; + reschedule = 1; + break; + + case HST_DEV_SCAN: + next_dev = -1; + for (i = host->cur_scan_dev + 1; i < CARM_MAX_PORTS; i++) + if (host->dev_present & (1 << i)) { + next_dev = i; + break; + } + + if (next_dev >= 0) { + host->cur_scan_dev = next_dev; + rc = carm_array_info(host, next_dev); + if (rc) { + new_state = HST_ERROR; + reschedule = 1; + } + } else { + new_state = HST_DEV_ACTIVATE; + reschedule = 1; + } + break; + + case HST_DEV_ACTIVATE: { + int activated = 0; + for (i = 0; i < CARM_MAX_PORTS; i++) + if (host->dev_active & (1 << i)) { + carm_activate_disk(host, &host->port[i]); + activated++; + } + + printk(KERN_INFO DRV_NAME "(%s): %d ports activated\n", + pci_name(host->pdev), activated); + + new_state = HST_PROBE_FINISHED; + reschedule = 1; + break; + } + + case HST_PROBE_FINISHED: + up(&host->probe_sem); + break; + + case HST_ERROR: + /* FIXME: TODO */ + break; + + default: + /* should never occur */ + printk(KERN_ERR PFX "BUG: unknown state %d\n", state); + assert(0); + break; + } + + if (new_state != HST_INVALID) { + spin_lock_irqsave(&io_request_lock, flags); + host->state = new_state; + spin_unlock_irqrestore(&io_request_lock, flags); + } + if (reschedule) + schedule_task(&host->fsm_task); +} + +static int carm_init_wait(void *mmio, u32 bits, unsigned int test_bit) +{ + unsigned int i; + + for (i = 0; i < 50000; i++) { + u32 tmp = readl(mmio + CARM_LMUC); + udelay(100); + + if (test_bit) { + if ((tmp & bits) == bits) + return 0; + } else { + if ((tmp & bits) == 0) + return 0; + } + + cond_resched(); + } + + printk(KERN_ERR PFX "carm_init_wait timeout, bits == 0x%x, test_bit == %s\n", + bits, test_bit ? "yes" : "no"); + return -EBUSY; +} + +static void carm_init_responses(struct carm_host *host) +{ + void *mmio = host->mmio; + unsigned int i; + struct carm_response *resp = (struct carm_response *) host->shm; + + for (i = 0; i < RMSG_Q_LEN; i++) + resp[i].status = 0xffffffff; + + writel(0, mmio + CARM_RESP_IDX); +} + +static int carm_init_host(struct carm_host *host) +{ + void *mmio = host->mmio; + u32 tmp; + u8 tmp8; + int rc; + unsigned long flags; + + DPRINTK("ENTER\n"); + + writel(0, mmio + CARM_INT_MASK); + + tmp8 = readb(mmio + CARM_INITC); + if (tmp8 & 0x01) { + tmp8 &= ~0x01; + writeb(tmp8, CARM_INITC); + readb(mmio + CARM_INITC); /* flush */ + + DPRINTK("snooze...\n"); + msleep(5000); + } + + tmp = readl(mmio + CARM_HMUC); + if (tmp & CARM_CME) { + DPRINTK("CME bit present, waiting\n"); + rc = carm_init_wait(mmio, CARM_CME, 1); + if (rc) { + DPRINTK("EXIT, carm_init_wait 1 failed\n"); + return rc; + } + } + if (tmp & CARM_RME) { + DPRINTK("RME bit present, waiting\n"); + rc = carm_init_wait(mmio, CARM_RME, 1); + if (rc) { + DPRINTK("EXIT, carm_init_wait 2 failed\n"); + return rc; + } + } + + tmp &= ~(CARM_RME | CARM_CME); + writel(tmp, mmio + CARM_HMUC); + readl(mmio + CARM_HMUC); /* flush */ + + rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 0); + if (rc) { + DPRINTK("EXIT, carm_init_wait 3 failed\n"); + return rc; + } + + carm_init_buckets(mmio); + + writel(host->shm_dma & 0xffffffff, mmio + RBUF_ADDR_LO); + writel((host->shm_dma >> 16) >> 16, mmio + RBUF_ADDR_HI); + writel(RBUF_LEN, mmio + RBUF_BYTE_SZ); + + tmp = readl(mmio + CARM_HMUC); + tmp |= (CARM_RME | CARM_CME | CARM_WZBC); + writel(tmp, mmio + CARM_HMUC); + readl(mmio + CARM_HMUC); /* flush */ + + rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 1); + if (rc) { + DPRINTK("EXIT, carm_init_wait 4 failed\n"); + return rc; + } + + writel(0, mmio + CARM_HMPHA); + writel(INT_DEF_MASK, mmio + CARM_INT_MASK); + + carm_init_responses(host); + + /* start initialization, probing state machine */ + spin_lock_irqsave(&io_request_lock, flags); + assert(host->state == HST_INVALID); + host->state = HST_PROBE_START; + spin_unlock_irqrestore(&io_request_lock, flags); + + schedule_task(&host->fsm_task); + + DPRINTK("EXIT\n"); + return 0; +} + +static void carm_init_ports (struct carm_host *host) +{ + struct carm_port *port; + request_queue_t *q; + unsigned int i; + + for (i = 0; i < CARM_MAX_PORTS; i++) { + port = &host->port[i]; + port->magic = CARM_MAGIC_PORT; + port->host = host; + port->port_no = i; + tasklet_init(&port->tasklet, carm_rw_tasklet, + (unsigned long) port); + + q = &port->q; + + blk_init_queue(q, carm_rq_fn); + q->queuedata = port; + blk_queue_bounce_limit(q, host->pdev->dma_mask); + blk_queue_headactive(q, 0); + + q->back_merge_fn = carm_back_merge_fn; + q->front_merge_fn = carm_front_merge_fn; + q->merge_requests_fn = carm_merge_requests_fn; + } +} + +static request_queue_t *carm_find_queue(kdev_t device) +{ + struct carm_host *host; + + host = blk_dev[MAJOR(device)].data; + if (!host) + return NULL; + if (host->magic != CARM_MAGIC_HOST) + return NULL; + + DPRINTK("match: major %d, minor %d\n", + MAJOR(device), MINOR(device)); + return &host->port[MINOR(device) >> CARM_PART_SHIFT].q; +} + +static int carm_init_disks(struct carm_host *host) +{ + host->gendisk.major = host->major; + host->gendisk.major_name = host->name; + host->gendisk.minor_shift = CARM_PART_SHIFT; + host->gendisk.max_p = CARM_MINORS_PER_MAJOR; + host->gendisk.part = host->gendisk_hd; + host->gendisk.sizes = host->blk_sizes; + host->gendisk.nr_real = CARM_MAX_PORTS; + host->gendisk.fops = &carm_bd_ops; + + blk_dev[host->major].queue = carm_find_queue; + blk_dev[host->major].data = host; + blk_size[host->major] = host->blk_sizes; + blksize_size[host->major] = host->blk_block_sizes; + hardsect_size[host->major] = host->blk_sect_sizes; + + add_gendisk(&host->gendisk); + + return 0; +} + +static void carm_free_disks(struct carm_host *host) +{ + unsigned int i; + + del_gendisk(&host->gendisk); + + for (i = 0; i < CARM_MAX_PORTS; i++) { + struct carm_port *port = &host->port[i]; + + blk_cleanup_queue(&port->q); + } + + blk_dev[host->major].queue = NULL; + blk_dev[host->major].data = NULL; + blk_size[host->major] = NULL; + blksize_size[host->major] = NULL; + hardsect_size[host->major] = NULL; +} + +static void carm_stop_tasklets(struct carm_host *host) +{ + unsigned int i; + + tasklet_kill(&host->oob_tasklet); + + for (i = 0; i < CARM_MAX_PORTS; i++) { + struct carm_port *port = &host->port[i]; + tasklet_kill(&port->tasklet); + } +} + +static int carm_init_shm(struct carm_host *host) +{ + host->shm = pci_alloc_consistent(host->pdev, CARM_SHM_SIZE, + &host->shm_dma); + if (!host->shm) + return -ENOMEM; + + host->msg_base = host->shm + RBUF_LEN; + host->msg_dma = host->shm_dma + RBUF_LEN; + + memset(host->shm, 0xff, RBUF_LEN); + memset(host->msg_base, 0, PDC_SHM_SIZE - RBUF_LEN); + + return 0; +} + +static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static unsigned int printed_version; + struct carm_host *host; + unsigned int pci_dac; + int rc; + unsigned int i; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + +#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ + rc = pci_set_dma_mask(pdev, 0xffffffffffffffffULL); + if (!rc) { + rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): consistent DMA mask failure\n", + pci_name(pdev)); + goto err_out_regions; + } + pci_dac = 1; + } else { +#endif + rc = pci_set_dma_mask(pdev, 0xffffffffULL); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n", + pci_name(pdev)); + goto err_out_regions; + } + pci_dac = 0; +#if IF_64BIT_DMA_IS_POSSIBLE /* grrrr... */ + } +#endif + + host = kmalloc(sizeof(*host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR DRV_NAME "(%s): memory alloc failure\n", + pci_name(pdev)); + rc = -ENOMEM; + goto err_out_regions; + } + + memset(host, 0, sizeof(*host)); + host->magic = CARM_MAGIC_HOST; + host->pdev = pdev; + host->flags = pci_dac ? FL_DAC : 0; + INIT_TQUEUE(&host->fsm_task, carm_fsm_task, host); + INIT_LIST_HEAD(&host->host_list_node); + init_MUTEX_LOCKED(&host->probe_sem); + tasklet_init(&host->oob_tasklet, carm_oob_tasklet, + (unsigned long) host); + carm_init_ports(host); + + for (i = 0; i < ARRAY_SIZE(host->req); i++) + host->req[i].tag = i; + + host->mmio = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (!host->mmio) { + printk(KERN_ERR DRV_NAME "(%s): MMIO alloc failure\n", + pci_name(pdev)); + rc = -ENOMEM; + goto err_out_kfree; + } + + rc = carm_init_shm(host); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): DMA SHM alloc failure\n", + pci_name(pdev)); + goto err_out_iounmap; + } + + blk_init_queue(&host->oob_q, carm_oob_rq_fn); + host->oob_q.queuedata = host; + blk_queue_bounce_limit(&host->oob_q, pdev->dma_mask); + blk_queue_headactive(&host->oob_q, 0); + + /* + * Figure out which major to use: 160, 161, or dynamic + */ + if (!test_and_set_bit(0, &carm_major_alloc)) + host->major = 160; + else if (!test_and_set_bit(1, &carm_major_alloc)) + host->major = 161; + else + host->flags |= FL_DYN_MAJOR; + + host->id = carm_host_id; + sprintf(host->name, DRV_NAME "%d", carm_host_id); + + rc = register_blkdev(host->major, host->name, &carm_bd_ops); + if (rc < 0) + goto err_out_free_majors; + if (host->flags & FL_DYN_MAJOR) + host->major = rc; + + rc = carm_init_disks(host); + if (rc) + goto err_out_blkdev_disks; + + pci_set_master(pdev); + + rc = request_irq(pdev->irq, carm_interrupt, SA_SHIRQ, DRV_NAME, host); + if (rc) { + printk(KERN_ERR DRV_NAME "(%s): irq alloc failure\n", + pci_name(pdev)); + goto err_out_blkdev_disks; + } + + rc = carm_init_host(host); + if (rc) + goto err_out_free_irq; + + DPRINTK("waiting for probe_sem\n"); + down(&host->probe_sem); + + printk(KERN_INFO "%s: pci %s, ports %d, io %lx, irq %u, major %d\n", + host->name, pci_name(pdev), (int) CARM_MAX_PORTS, + pci_resource_start(pdev, 0), pdev->irq, host->major); + + carm_host_id++; + pci_set_drvdata(pdev, host); + return 0; + +err_out_free_irq: + free_irq(pdev->irq, host); +err_out_blkdev_disks: + carm_free_disks(host); + unregister_blkdev(host->major, host->name); +err_out_free_majors: + if (host->major == 160) + clear_bit(0, &carm_major_alloc); + else if (host->major == 161) + clear_bit(1, &carm_major_alloc); + blk_cleanup_queue(&host->oob_q); + pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma); +err_out_iounmap: + iounmap(host->mmio); +err_out_kfree: + kfree(host); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + +static void carm_remove_one (struct pci_dev *pdev) +{ + struct carm_host *host = pci_get_drvdata(pdev); + + if (!host) { + printk(KERN_ERR PFX "BUG: no host data for PCI(%s)\n", + pci_name(pdev)); + return; + } + + free_irq(pdev->irq, host); + carm_stop_tasklets(host); + carm_free_disks(host); + unregister_blkdev(host->major, host->name); + if (host->major == 160) + clear_bit(0, &carm_major_alloc); + else if (host->major == 161) + clear_bit(1, &carm_major_alloc); + blk_cleanup_queue(&host->oob_q); + pci_free_consistent(pdev, CARM_SHM_SIZE, host->shm, host->shm_dma); + iounmap(host->mmio); + kfree(host); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +static int __init carm_init(void) +{ + return pci_module_init(&carm_driver); +} + +static void __exit carm_exit(void) +{ + pci_unregister_driver(&carm_driver); +} + +module_init(carm_init); +module_exit(carm_exit); + + diff -urN linux-2.4.26/drivers/bluetooth/Makefile.lib linux-2.4.27/drivers/bluetooth/Makefile.lib --- linux-2.4.26/drivers/bluetooth/Makefile.lib 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/drivers/bluetooth/Makefile.lib 2004-08-07 16:26:04.680347846 -0700 @@ -1 +1,2 @@ -obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o +obj-$(CONFIG_BLUEZ_HCIBFUSB) += firmware_class.o +obj-$(CONFIG_BLUEZ_HCIBT3C) += firmware_class.o diff -urN linux-2.4.26/drivers/bluetooth/bfusb.c linux-2.4.27/drivers/bluetooth/bfusb.c --- linux-2.4.26/drivers/bluetooth/bfusb.c 2003-11-28 10:26:19.000000000 -0800 +++ linux-2.4.27/drivers/bluetooth/bfusb.c 2004-08-07 16:26:04.681347887 -0700 @@ -359,11 +359,11 @@ BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len); - if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) - return; - read_lock(&bfusb->lock); + if (!test_bit(HCI_RUNNING, &bfusb->hdev.flags)) + goto unlock; + if (urb->status || !count) goto resubmit; @@ -414,6 +414,7 @@ bfusb->hdev.name, urb, err); } +unlock: read_unlock(&bfusb->lock); } diff -urN linux-2.4.26/drivers/bluetooth/bluecard_cs.c linux-2.4.27/drivers/bluetooth/bluecard_cs.c --- linux-2.4.26/drivers/bluetooth/bluecard_cs.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/bluetooth/bluecard_cs.c 2004-08-07 16:26:04.682347928 -0700 @@ -803,6 +803,9 @@ unsigned int iobase = info->link.io.BasePort1; struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + bluecard_hci_close(hdev); clear_bit(CARD_READY, &(info->hw_state)); diff -urN linux-2.4.26/drivers/bluetooth/bt3c_cs.c linux-2.4.27/drivers/bluetooth/bt3c_cs.c --- linux-2.4.26/drivers/bluetooth/bt3c_cs.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/bluetooth/bt3c_cs.c 2004-08-07 16:26:04.683347969 -0700 @@ -24,8 +24,6 @@ #include #include -#define __KERNEL_SYSCALLS__ - #include #include #include @@ -48,6 +46,8 @@ #include #include +#include + #include #include #include @@ -485,78 +485,101 @@ -/* ======================== User mode firmware loader ======================== */ +/* ======================== Card services HCI interaction ======================== */ -#define FW_LOADER "/sbin/bluefw" -static int errno; +static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count) +{ + char *ptr = (char *) firmware; + char b[9]; + unsigned int iobase, size, addr, fcs, tmp; + int i, err = 0; + iobase = info->link.io.BasePort1; -static int bt3c_fw_loader_exec(void *dev) -{ - char *argv[] = { FW_LOADER, "pccard", dev, NULL }; - char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - int err; + /* Reset */ - err = exec_usermodehelper(FW_LOADER, argv, envp); - if (err) - printk(KERN_WARNING "bt3c_cs: Failed to exec \"%s pccard %s\".\n", FW_LOADER, (char *)dev); + bt3c_io_write(iobase, 0x8040, 0x0404); + bt3c_io_write(iobase, 0x8040, 0x0400); - return err; -} + udelay(1); + bt3c_io_write(iobase, 0x8040, 0x0404); -static int bt3c_firmware_load(bt3c_info_t *info) -{ - sigset_t tmpsig; - char dev[16]; - pid_t pid; - int result; + udelay(17); - /* Check if root fs is mounted */ - if (!current->fs->root) { - printk(KERN_WARNING "bt3c_cs: Root filesystem is not mounted.\n"); - return -EPERM; - } + /* Load */ - sprintf(dev, "%04x", info->link.io.BasePort1); + while (count) { + if (ptr[0] != 'S') { + printk(KERN_WARNING "bt3c_cs: Bad address in firmware.\n"); + err = -EFAULT; + goto error; + } - pid = kernel_thread(bt3c_fw_loader_exec, (void *)dev, 0); - if (pid < 0) { - printk(KERN_WARNING "bt3c_cs: Forking of kernel thread failed (errno=%d).\n", -pid); - return pid; - } + memset(b, 0, sizeof(b)); + memcpy(b, ptr + 2, 2); + size = simple_strtol(b, NULL, 16); + + memset(b, 0, sizeof(b)); + memcpy(b, ptr + 4, 8); + addr = simple_strtol(b, NULL, 16); + + memset(b, 0, sizeof(b)); + memcpy(b, ptr + (size * 2) + 2, 2); + fcs = simple_strtol(b, NULL, 16); + + memset(b, 0, sizeof(b)); + for (tmp = 0, i = 0; i < size; i++) { + memcpy(b, ptr + (i * 2) + 2, 2); + tmp += simple_strtol(b, NULL, 16); + } - /* Block signals, everything but SIGKILL/SIGSTOP */ - spin_lock_irq(¤t->sigmask_lock); - tmpsig = current->blocked; - siginitsetinv(¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP)); - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); + if (((tmp + fcs) & 0xff) != 0xff) { + printk(KERN_WARNING "bt3c_cs: Checksum error in firmware.\n"); + err = -EILSEQ; + goto error; + } - result = waitpid(pid, NULL, __WCLONE); + if (ptr[1] == '3') { + bt3c_address(iobase, addr); - /* Allow signals again */ - spin_lock_irq(¤t->sigmask_lock); - current->blocked = tmpsig; - recalc_sigpending(current); - spin_unlock_irq(¤t->sigmask_lock); + memset(b, 0, sizeof(b)); + for (i = 0; i < (size - 4) / 2; i++) { + memcpy(b, ptr + (i * 4) + 12, 4); + tmp = simple_strtol(b, NULL, 16); + bt3c_put(iobase, tmp); + } + } - if (result != pid) { - printk(KERN_WARNING "bt3c_cs: Waiting for pid %d failed (errno=%d).\n", pid, -result); - return -result; + ptr += (size * 2) + 6; + count -= (size * 2) + 6; } - return 0; -} + udelay(17); + /* Boot */ + bt3c_address(iobase, 0x3000); + outb(inb(iobase + CONTROL) | 0x40, iobase + CONTROL); -/* ======================== Card services HCI interaction ======================== */ +error: + udelay(17); + + /* Clear */ + + bt3c_io_write(iobase, 0x7006, 0x0000); + bt3c_io_write(iobase, 0x7005, 0x0000); + bt3c_io_write(iobase, 0x7001, 0x0000); + + return err; +} int bt3c_open(bt3c_info_t *info) { + const struct firmware *firmware; + char device[16]; struct hci_dev *hdev; int err; @@ -570,8 +593,22 @@ /* Load firmware */ - if ((err = bt3c_firmware_load(info)) < 0) + snprintf(device, sizeof(device), "bt3c%4.4x", info->link.io.BasePort1); + + err = request_firmware(&firmware, "BT3CPCC.bin", device); + if (err < 0) { + printk(KERN_WARNING "bt3c_cs: Firmware request failed.\n"); return err; + } + + err = bt3c_load_firmware(info, firmware->data, firmware->size); + + release_firmware(firmware); + + if (err < 0) { + printk(KERN_WARNING "bt3c_cs: Firmware loading failed.\n"); + return err; + } /* Timeout before it is safe to send the first HCI packet */ @@ -606,6 +643,9 @@ { struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + bt3c_hci_close(hdev); if (hci_unregister_dev(hdev) < 0) diff -urN linux-2.4.26/drivers/bluetooth/btuart_cs.c linux-2.4.27/drivers/bluetooth/btuart_cs.c --- linux-2.4.26/drivers/bluetooth/btuart_cs.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/drivers/bluetooth/btuart_cs.c 2004-08-07 16:26:04.684348010 -0700 @@ -556,6 +556,9 @@ unsigned int iobase = info->link.io.BasePort1; struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + btuart_hci_close(hdev); spin_lock_irqsave(&(info->lock), flags); diff -urN linux-2.4.26/drivers/bluetooth/dtl1_cs.c linux-2.4.27/drivers/bluetooth/dtl1_cs.c --- linux-2.4.26/drivers/bluetooth/dtl1_cs.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/bluetooth/dtl1_cs.c 2004-08-07 16:26:04.685348051 -0700 @@ -535,6 +535,9 @@ unsigned int iobase = info->link.io.BasePort1; struct hci_dev *hdev = &(info->hdev); + if (info->link.state & DEV_CONFIG_PENDING) + return -ENODEV; + dtl1_hci_close(hdev); spin_lock_irqsave(&(info->lock), flags); diff -urN linux-2.4.26/drivers/bluetooth/hci_bcsp.c linux-2.4.27/drivers/bluetooth/hci_bcsp.c --- linux-2.4.26/drivers/bluetooth/hci_bcsp.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/drivers/bluetooth/hci_bcsp.c 2004-08-07 16:26:04.685348051 -0700 @@ -34,7 +34,6 @@ #include #include -#include #include #include #include @@ -635,7 +634,8 @@ struct sk_buff *skb; unsigned long flags; - BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen); + BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen); + spin_lock_irqsave(&bcsp->unack.lock, flags); while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) { diff -urN linux-2.4.26/drivers/bluetooth/hci_ldisc.c linux-2.4.27/drivers/bluetooth/hci_ldisc.c --- linux-2.4.26/drivers/bluetooth/hci_ldisc.c 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/drivers/bluetooth/hci_ldisc.c 2004-08-07 16:26:04.686348092 -0700 @@ -33,7 +33,6 @@ #include #include -#include #include #include #include diff -urN linux-2.4.26/drivers/bluetooth/hci_uart.h linux-2.4.27/drivers/bluetooth/hci_uart.h --- linux-2.4.26/drivers/bluetooth/hci_uart.h 2003-06-13 07:51:32.000000000 -0700 +++ linux-2.4.27/drivers/bluetooth/hci_uart.h 2004-08-07 16:26:04.686348092 -0700 @@ -35,11 +35,12 @@ #define HCIUARTGETPROTO _IOR('U', 201, int) /* UART protocols */ -#define HCI_UART_MAX_PROTO 3 +#define HCI_UART_MAX_PROTO 4 #define HCI_UART_H4 0 #define HCI_UART_BCSP 1 -#define HCI_UART_NCSP 2 +#define HCI_UART_3WIRE 2 +#define HCI_UART_H4DS 3 #ifdef __KERNEL__ struct hci_uart; diff -urN linux-2.4.26/drivers/bluetooth/hci_usb.c linux-2.4.27/drivers/bluetooth/hci_usb.c --- linux-2.4.26/drivers/bluetooth/hci_usb.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/bluetooth/hci_usb.c 2004-08-07 16:26:04.687348133 -0700 @@ -30,7 +30,7 @@ * * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $ */ -#define VERSION "2.5" +#define VERSION "2.7" #include #include @@ -76,14 +76,15 @@ /* AVM BlueFRITZ! USB v2.0 */ { USB_DEVICE(0x057c, 0x3800) }, - /* Ericsson with non-standard id */ - { USB_DEVICE(0x0bdb, 0x1002) }, + /* Bluetooth Ultraport Module from IBM */ + { USB_DEVICE(0x04bf, 0x030a) }, - /* ALPS Module with non-standard id */ + /* ALPS Modules with non-standard id */ + { USB_DEVICE(0x044e, 0x3001) }, { USB_DEVICE(0x044e, 0x3002) }, - /* Bluetooth Ultraport Module from IBM */ - { USB_DEVICE(0x04bf, 0x030a) }, + /* Ericsson with non-standard id */ + { USB_DEVICE(0x0bdb, 0x1002) }, { } /* Terminating entry */ }; @@ -97,9 +98,15 @@ /* Broadcom BCM2035 */ { USB_DEVICE(0x0a5c, 0x200a), driver_info: HCI_RESET }, + /* ISSC Bluetooth Adapter v3.1 */ + { USB_DEVICE(0x1131, 0x1001), driver_info: HCI_RESET }, + /* Digianswer device */ { USB_DEVICE(0x08fd, 0x0001), driver_info: HCI_DIGIANSWER }, + /* RTX Telecom based adapter with buggy SCO support */ + { USB_DEVICE(0x0400, 0x0807), driver_info: HCI_BROKEN_ISOC }, + { } /* Terminating entry */ }; @@ -699,11 +706,11 @@ BT_DBG("%s urb %p type %d status %d count %d flags %x", hdev->name, urb, _urb->type, urb->status, count, urb->transfer_flags); - if (!test_bit(HCI_RUNNING, &hdev->flags)) - return; - read_lock(&husb->completion_lock); + if (!test_bit(HCI_RUNNING, &hdev->flags)) + goto unlock; + if (urb->status || !count) goto resubmit; @@ -740,6 +747,8 @@ BT_DBG("%s urb %p type %d resubmit status %d", hdev->name, urb, _urb->type, err); } + +unlock: read_unlock(&husb->completion_lock); } @@ -876,7 +885,7 @@ } #ifdef CONFIG_BLUEZ_HCIUSB_SCO - if (!isoc_in_ep[1] || !isoc_out_ep[1]) { + if (id->driver_info & HCI_BROKEN_ISOC || !isoc_in_ep[1] || !isoc_out_ep[1]) { BT_DBG("Isoc endpoints not found"); isoc_iface = NULL; } diff -urN linux-2.4.26/drivers/bluetooth/hci_usb.h linux-2.4.27/drivers/bluetooth/hci_usb.h --- linux-2.4.26/drivers/bluetooth/hci_usb.h 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/bluetooth/hci_usb.h 2004-08-07 16:26:04.688348174 -0700 @@ -40,6 +40,7 @@ #define HCI_IGNORE 0x01 #define HCI_RESET 0x02 #define HCI_DIGIANSWER 0x04 +#define HCI_BROKEN_ISOC 0x08 #define HCI_MAX_IFACE_NUM 3 diff -urN linux-2.4.26/drivers/char/ChangeLog linux-2.4.27/drivers/char/ChangeLog --- linux-2.4.26/drivers/char/ChangeLog 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/char/ChangeLog 2004-08-07 16:26:04.689348216 -0700 @@ -1,3 +1,8 @@ +2002-09-21 Marek Michalkiewicz + + * parport_serial.c: Move from ../parport/ here, must be initialised + after serial.c for register_serial to work. + 2001-08-11 Tim Waugh * serial.c (get_pci_port): Deal with awkward Titan cards. diff -urN linux-2.4.26/drivers/char/Config.in linux-2.4.27/drivers/char/Config.in --- linux-2.4.26/drivers/char/Config.in 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/char/Config.in 2004-08-07 16:26:04.689348216 -0700 @@ -273,7 +273,6 @@ if [ "$CONFIG_SGI_IP22" = "y" ]; then dep_tristate ' Indy/I2 Hardware Watchdog' CONFIG_INDYDOG $CONFIG_SGI_IP22 fi - dep_tristate ' AMD 766/768 TCO Timer/Watchdog' CONFIG_AMD7XX_TCO $CONFIG_EXPERIMENTAL if [ "$CONFIG_8xx" = "y" ]; then tristate ' MPC8xx Watchdog Timer' CONFIG_8xx_WDT fi diff -urN linux-2.4.26/drivers/char/Makefile linux-2.4.27/drivers/char/Makefile --- linux-2.4.26/drivers/char/Makefile 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/char/Makefile 2004-08-07 16:26:04.690348257 -0700 @@ -171,6 +171,7 @@ obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o obj-$(CONFIG_SERIAL) += $(SERIAL) +obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o obj-$(CONFIG_SERIAL_HCDP) += hcdp_serial.o obj-$(CONFIG_SERIAL_21285) += serial_21285.o obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o @@ -320,7 +321,6 @@ obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o -obj-$(CONFIG_AMD7XX_TCO) += amd7xx_tco.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o diff -urN linux-2.4.26/drivers/char/agp/agp.h linux-2.4.27/drivers/char/agp/agp.h --- linux-2.4.26/drivers/char/agp/agp.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/char/agp/agp.h 2004-08-07 16:26:04.691348298 -0700 @@ -316,6 +316,9 @@ #ifndef PCI_DEVICE_ID_ATI_RS200 #define PCI_DEVICE_ID_ATI_RS200 0xcab2 #endif +#ifndef PCI_DEVICE_ID_ATI_RS200_REV2 +#define PCI_DEVICE_ID_ATI_RS200_REV2 0xcbb2 +#endif #ifndef PCI_DEVICE_ID_ATI_RS250 #define PCI_DEVICE_ID_ATI_RS250 0xcab3 #endif diff -urN linux-2.4.26/drivers/char/agp/agpgart_be.c linux-2.4.27/drivers/char/agp/agpgart_be.c --- linux-2.4.26/drivers/char/agp/agpgart_be.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/char/agp/agpgart_be.c 2004-08-07 16:26:04.695348462 -0700 @@ -5738,6 +5738,7 @@ if ((agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS100) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200) || + (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_REV2) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_B) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS250)) { pci_read_config_dword(agp_bridge.dev, ATI_RS100_APSIZE, &temp); @@ -5792,6 +5793,7 @@ if ((agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS100) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200) || + (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_REV2) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_B) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS250)) { pci_read_config_dword(agp_bridge.dev, ATI_RS100_APSIZE, &temp); @@ -5825,6 +5827,7 @@ if ((agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS100) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200) || + (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_REV2) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_B) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS250)) { pci_write_config_dword(agp_bridge.dev, ATI_RS100_IG_AGPMODE, 0x20000); @@ -5863,6 +5866,7 @@ /* Write back the previous size and disable gart translation */ if ((agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS100) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200) || + (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_REV2) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS200_B) || (agp_bridge.dev->device == PCI_DEVICE_ID_ATI_RS250)) { pci_read_config_dword(agp_bridge.dev, ATI_RS100_APSIZE, &temp); @@ -6426,6 +6430,12 @@ "ATI", "IGP330/340/345/350/M", ati_generic_setup }, + { PCI_DEVICE_ID_ATI_RS200_REV2, + PCI_VENDOR_ID_ATI, + ATI_RS200, + "ATI", + "IGP345M (rev 2)", + ati_generic_setup }, { PCI_DEVICE_ID_ATI_RS200_B, PCI_VENDOR_ID_ATI, ATI_RS200, diff -urN linux-2.4.26/drivers/char/amd7xx_tco.c linux-2.4.27/drivers/char/amd7xx_tco.c --- linux-2.4.26/drivers/char/amd7xx_tco.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/char/amd7xx_tco.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,387 +0,0 @@ -/* - * AMD 766/768 TCO Timer Driver - * (c) Copyright 2002 Zwane Mwaikambo - * All Rights Reserved. - * - * Parts from; - * Hardware driver for the AMD 768 Random Number Generator (RNG) - * (c) Copyright 2001 Red Hat Inc - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * The author(s) of this software shall not be held liable for damages - * of any nature resulting due to the use of this software. This - * software is provided AS-IS with no warranties. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AMDTCO_MODULE_VER "build 20021116" -#define AMDTCO_MODULE_NAME "amd7xx_tco" -#define PFX AMDTCO_MODULE_NAME ": " - -#define MAX_TIMEOUT 38 /* max of 38 seconds, although the system will only - * reset itself after the second timeout */ - -/* pmbase registers */ -#define TCO_RELOAD_REG 0x40 /* bits 0-5 are current count, 6-7 are reserved */ -#define TCO_INITVAL_REG 0x41 /* bits 0-5 are value to load, 6-7 are reserved */ -#define TCO_TIMEOUT_MASK 0x3f -#define TCO_STATUS1_REG 0x44 -#define TCO_STATUS2_REG 0x46 -#define NDTO_STS2 (1 << 1) /* we're interested in the second timeout */ -#define BOOT_STS (1 << 2) /* will be set if NDTO_STS2 was set before reboot */ -#define TCO_CTRL1_REG 0x48 -#define TCO_HALT (1 << 11) -#define NO_REBOOT (1 << 10) /* in DevB:3x48 */ - -static char banner[] __initdata = KERN_INFO PFX AMDTCO_MODULE_VER "\n"; -static int timeout = 38; -static u32 pmbase; /* PMxx I/O base */ -static struct pci_dev *dev; -static struct semaphore open_sem; -static spinlock_t amdtco_lock; /* only for device access */ -static int expect_close = 0; - -MODULE_PARM(timeout, "i"); -MODULE_PARM_DESC(timeout, "range is 0-38 seconds, default is 38"); - -static inline u8 seconds_to_ticks(int seconds) -{ - /* the internal timer is stored as ticks which decrement - * every 0.6 seconds */ - return (seconds * 10) / 6; -} - -static inline int ticks_to_seconds(u8 ticks) -{ - return (ticks * 6) / 10; -} - -static inline int amdtco_status(void) -{ - u16 reg; - int status = 0; - - reg = inb(pmbase+TCO_CTRL1_REG); - if ((reg & TCO_HALT) == 0) - status |= WDIOF_KEEPALIVEPING; - - reg = inb(pmbase+TCO_STATUS2_REG); - if (reg & BOOT_STS) - status |= WDIOF_CARDRESET; - - return status; -} - -static inline void amdtco_ping(void) -{ - outb(1, pmbase+TCO_RELOAD_REG); -} - -static inline int amdtco_gettimeout(void) -{ - u8 reg = inb(pmbase+TCO_RELOAD_REG) & TCO_TIMEOUT_MASK; - return ticks_to_seconds(reg); -} - -static inline void amdtco_settimeout(unsigned int timeout) -{ - u8 reg = seconds_to_ticks(timeout) & TCO_TIMEOUT_MASK; - outb(reg, pmbase+TCO_INITVAL_REG); -} - -static inline void amdtco_global_enable(void) -{ - u16 reg; - - spin_lock(&amdtco_lock); - - /* clear NO_REBOOT on DevB:3x48 p97 */ - pci_read_config_word(dev, 0x48, ®); - reg &= ~NO_REBOOT; - pci_write_config_word(dev, 0x48, reg); - - spin_unlock(&amdtco_lock); -} - -static inline void amdtco_enable(void) -{ - u16 reg; - - spin_lock(&amdtco_lock); - reg = inw(pmbase+TCO_CTRL1_REG); - reg &= ~TCO_HALT; - outw(reg, pmbase+TCO_CTRL1_REG); - spin_unlock(&amdtco_lock); -} - -static inline void amdtco_disable(void) -{ - u16 reg; - - spin_lock(&amdtco_lock); - reg = inw(pmbase+TCO_CTRL1_REG); - reg |= TCO_HALT; - outw(reg, pmbase+TCO_CTRL1_REG); - spin_unlock(&amdtco_lock); -} - -static int amdtco_fop_open(struct inode *inode, struct file *file) -{ - if (down_trylock(&open_sem)) - return -EBUSY; - -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif - - if (timeout > MAX_TIMEOUT) - timeout = MAX_TIMEOUT; - - amdtco_disable(); - amdtco_settimeout(timeout); - amdtco_global_enable(); - amdtco_enable(); - amdtco_ping(); - printk(KERN_INFO PFX "Watchdog enabled, timeout = %ds of %ds\n", - amdtco_gettimeout(), timeout); - - return 0; -} - - -static int amdtco_fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) -{ - int new_timeout; - int tmp; - - static struct watchdog_info ident = { - options: WDIOF_SETTIMEOUT | WDIOF_CARDRESET, - identity: "AMD 766/768" - }; - - switch (cmd) { - default: - return -ENOTTY; - - case WDIOC_GETSUPPORT: - if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof ident)) - return -EFAULT; - return 0; - - case WDIOC_GETSTATUS: - return put_user(amdtco_status(), (int *)arg); - - case WDIOC_KEEPALIVE: - amdtco_ping(); - return 0; - - case WDIOC_SETTIMEOUT: - if (get_user(new_timeout, (int *)arg)) - return -EFAULT; - - if (new_timeout < 0) - return -EINVAL; - - if (new_timeout > MAX_TIMEOUT) - new_timeout = MAX_TIMEOUT; - - timeout = new_timeout; - amdtco_settimeout(timeout); - /* fall through and return the new timeout */ - - case WDIOC_GETTIMEOUT: - return put_user(amdtco_gettimeout(), (int *)arg); - - case WDIOC_SETOPTIONS: - if (copy_from_user(&tmp, (int *)arg, sizeof tmp)) - return -EFAULT; - - if (tmp & WDIOS_DISABLECARD) - amdtco_disable(); - - if (tmp & WDIOS_ENABLECARD) - amdtco_enable(); - - return 0; - } -} - - -static int amdtco_fop_release(struct inode *inode, struct file *file) -{ - if (expect_close) { - amdtco_disable(); - printk(KERN_INFO PFX "Watchdog disabled\n"); - } else { - amdtco_ping(); - printk(KERN_CRIT PFX "Unexpected close!, timeout in %d seconds\n", timeout); - } - - up(&open_sem); - return 0; -} - - -static ssize_t amdtco_fop_write(struct file *file, const char *data, size_t len, loff_t *ppos) -{ - if (ppos != &file->f_pos) - return -ESPIPE; - - if (len) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT - size_t i; - char c; - expect_close = 0; - - for (i = 0; i != len; i++) { - if (get_user(c, data + i)) - return -EFAULT; - - if (c == 'V') - expect_close = 1; - } -#endif - amdtco_ping(); - } - - return len; -} - - -static int amdtco_notify_sys(struct notifier_block *this, unsigned long code, void *unused) -{ - if (code == SYS_DOWN || code == SYS_HALT) - amdtco_disable(); - - return NOTIFY_DONE; -} - - -static struct notifier_block amdtco_notifier = -{ - notifier_call: amdtco_notify_sys -}; - -static struct file_operations amdtco_fops = -{ - owner: THIS_MODULE, - write: amdtco_fop_write, - ioctl: amdtco_fop_ioctl, - open: amdtco_fop_open, - release: amdtco_fop_release -}; - -static struct miscdevice amdtco_miscdev = -{ - minor: WATCHDOG_MINOR, - name: "watchdog", - fops: &amdtco_fops -}; - -static struct pci_device_id amdtco_pci_tbl[] __initdata = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, }, - { 0, } -}; - -MODULE_DEVICE_TABLE (pci, amdtco_pci_tbl); - -static int __init amdtco_init(void) -{ - int ret; - - sema_init(&open_sem, 1); - spin_lock_init(&amdtco_lock); - - pci_for_each_dev(dev) { - if (pci_match_device (amdtco_pci_tbl, dev) != NULL) - goto found_one; - } - - return -ENODEV; - -found_one: - - if ((ret = register_reboot_notifier(&amdtco_notifier))) { - printk(KERN_ERR PFX "Unable to register reboot notifier err = %d\n", ret); - goto out_clean; - } - - if ((ret = misc_register(&amdtco_miscdev))) { - printk(KERN_ERR PFX "Unable to register miscdev on minor %d\n", WATCHDOG_MINOR); - goto out_unreg_reboot; - } - - pci_read_config_dword(dev, 0x58, &pmbase); - pmbase &= 0x0000FF00; - - if (pmbase == 0) { - printk (KERN_ERR PFX "power management base not set\n"); - ret = -EIO; - goto out_unreg_misc; - } - - /* ret = 0; */ - printk(banner); - goto out_clean; - -out_unreg_misc: - misc_deregister(&amdtco_miscdev); -out_unreg_reboot: - unregister_reboot_notifier(&amdtco_notifier); -out_clean: - return ret; -} - -static void __exit amdtco_exit(void) -{ - misc_deregister(&amdtco_miscdev); - unregister_reboot_notifier(&amdtco_notifier); -} - - -#ifndef MODULE -static int __init amdtco_setup(char *str) -{ - int ints[4]; - - str = get_options (str, ARRAY_SIZE(ints), ints); - if (ints[0] > 0) - timeout = ints[1]; - - if (!timeout || timeout > 38) - timeout = MAX_TIMEOUT; - - return 1; -} - -__setup("amd7xx_tco=", amdtco_setup); -#endif - -module_init(amdtco_init); -module_exit(amdtco_exit); - -MODULE_AUTHOR("Zwane Mwaikambo "); -MODULE_DESCRIPTION("AMD 766/768 TCO Timer Driver"); -MODULE_LICENSE("GPL"); -EXPORT_NO_SYMBOLS; - diff -urN linux-2.4.26/drivers/char/drm/drmP.h linux-2.4.27/drivers/char/drm/drmP.h --- linux-2.4.26/drivers/char/drm/drmP.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/char/drm/drmP.h 2004-08-07 16:26:04.700348668 -0700 @@ -52,6 +52,7 @@ #include #include #include /* For (un)lock_kernel */ +#include /* for cmpxchg() */ #include #include #if defined(__alpha__) || defined(__powerpc__) @@ -174,38 +175,7 @@ (unsigned long)_n_, sizeof(*(ptr))); \ }) -#elif __i386__ -static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, - unsigned long new, int size) -{ - unsigned long prev; - switch (size) { - case 1: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - case 2: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - case 4: - __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2" - : "=a"(prev) - : "q"(new), "m"(*__xg(ptr)), "0"(old) - : "memory"); - return prev; - } - return old; -} - -#define cmpxchg(ptr,o,n) \ - ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o), \ - (unsigned long)(n),sizeof(*(ptr)))) -#endif /* i386 & alpha */ +#endif /* alpha */ #endif #define __REALLY_HAVE_SG (__HAVE_SG) diff -urN linux-2.4.26/drivers/char/drm/sis_mm.c linux-2.4.27/drivers/char/drm/sis_mm.c --- linux-2.4.26/drivers/char/drm/sis_mm.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/char/drm/sis_mm.c 2004-08-07 16:26:04.701348709 -0700 @@ -120,7 +120,7 @@ return -1; } - sis_free(fb.free); + sis_free((u32)fb.free); if(!del_alloc_set(fb.context, VIDEO_TYPE, fb.free)) retval = -1; diff -urN linux-2.4.26/drivers/char/i8k.c linux-2.4.27/drivers/char/i8k.c --- linux-2.4.26/drivers/char/i8k.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/char/i8k.c 2004-08-07 16:26:04.701348709 -0700 @@ -493,6 +493,7 @@ static ssize_t i8k_read(struct file *f, char *buffer, size_t len, loff_t *fpos) { + loff_t pos = *fpos; int n; char info[128]; @@ -501,19 +502,19 @@ return n; } - if (*fpos >= n) { + if (pos != (unsigned)pos || pos >= n) { return 0; } - if ((*fpos + len) >= n) { - len = n - *fpos; + if (len >= n - pos) { + len = n - pos; } if (copy_to_user(buffer, info, len) != 0) { return -EFAULT; } - *fpos += len; + *fpos = pos + len; return len; } diff -urN linux-2.4.26/drivers/char/istallion.c linux-2.4.27/drivers/char/istallion.c --- linux-2.4.26/drivers/char/istallion.c 2002-08-02 17:39:43.000000000 -0700 +++ linux-2.4.27/drivers/char/istallion.c 2004-08-07 16:26:04.705348873 -0700 @@ -4854,6 +4854,7 @@ void *memptr; stlibrd_t *brdp; int brdnr, size, n; + loff_t pos = *offp; #if DEBUG printk(KERN_DEBUG "stli_memread(fp=%x,buf=%x,count=%x,offp=%x)\n", @@ -4868,25 +4869,26 @@ return(-ENODEV); if (brdp->state == 0) return(-ENODEV); - if (fp->f_pos >= brdp->memsize) + if (pos != (unsigned)pos || pos >= brdp->memsize) return(0); - size = MIN(count, (brdp->memsize - fp->f_pos)); + size = MIN(count, (brdp->memsize - pos)); save_flags(flags); cli(); EBRDENABLE(brdp); while (size > 0) { - memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos); - n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize))); + memptr = (void *) EBRDGETMEMPTR(brdp, pos); + n = MIN(size, (brdp->pagesize - (((unsigned long) pos) % brdp->pagesize))); if (copy_to_user(buf, memptr, n)) { count = -EFAULT; goto out; } - fp->f_pos += n; + pos += n; buf += n; size -= n; } + *offp = pos; out: EBRDDISABLE(brdp); restore_flags(flags); @@ -4909,6 +4911,7 @@ stlibrd_t *brdp; char *chbuf; int brdnr, size, n; + loff_t pos = *offp; #if DEBUG printk(KERN_DEBUG "stli_memwrite(fp=%x,buf=%x,count=%x,offp=%x)\n", @@ -4923,26 +4926,27 @@ return(-ENODEV); if (brdp->state == 0) return(-ENODEV); - if (fp->f_pos >= brdp->memsize) + if (pos != (unsigned)pos || pos >= brdp->memsize) return(0); chbuf = (char *) buf; - size = MIN(count, (brdp->memsize - fp->f_pos)); + size = MIN(count, (brdp->memsize - pos)); save_flags(flags); cli(); EBRDENABLE(brdp); while (size > 0) { - memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos); - n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize))); + memptr = (void *) EBRDGETMEMPTR(brdp, pos); + n = MIN(size, (brdp->pagesize - (((unsigned long) pos) % brdp->pagesize))); if (copy_from_user(memptr, chbuf, n)) { count = -EFAULT; goto out; } - fp->f_pos += n; + pos += n; chbuf += n; size -= n; } + *offp = pos; out: EBRDDISABLE(brdp); restore_flags(flags); diff -urN linux-2.4.26/drivers/char/mem.c linux-2.4.27/drivers/char/mem.c --- linux-2.4.26/drivers/char/mem.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/char/mem.c 2004-08-07 16:26:04.715349284 -0700 @@ -64,7 +64,7 @@ if (copy_from_user(p, buf, count)) return -EFAULT; written += count; - *ppos += written; + *ppos = realp + written; return written; } @@ -105,7 +105,7 @@ if (copy_to_user(buf, __va(p), count)) return -EFAULT; read += count; - *ppos += read; + *ppos = p + read; return read; } diff -urN linux-2.4.26/drivers/char/nvram.c linux-2.4.27/drivers/char/nvram.c --- linux-2.4.26/drivers/char/nvram.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/char/nvram.c 2004-08-07 16:26:04.716349325 -0700 @@ -252,9 +252,13 @@ nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos) { unsigned char contents[NVRAM_BYTES]; - unsigned i = *ppos; + loff_t n = *ppos; + unsigned i = n; unsigned char *tmp; + if (i != n || i >= NVRAM_BYTES) + return 0; + spin_lock_irq(&rtc_lock); if (!__nvram_check_checksum()) @@ -281,10 +285,14 @@ nvram_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { unsigned char contents[NVRAM_BYTES]; - unsigned i = *ppos; + loff_t n = *ppos; + unsigned i = n; unsigned char *tmp; int len; + if (i != n || i >= NVRAM_BYTES) + return 0; + len = (NVRAM_BYTES - i) < count ? (NVRAM_BYTES - i) : count; if (copy_from_user(contents, buf, len)) return -EFAULT; diff -urN linux-2.4.26/drivers/char/nwflash.c linux-2.4.27/drivers/char/nwflash.c --- linux-2.4.26/drivers/char/nwflash.c 2001-10-12 13:48:42.000000000 -0700 +++ linux-2.4.27/drivers/char/nwflash.c 2004-08-07 16:26:04.717349366 -0700 @@ -133,7 +133,8 @@ static ssize_t flash_read(struct file *file, char *buf, size_t size, loff_t * ppos) { - unsigned long p = *ppos; + loff_t n = *ppos; + unsigned long p = n; unsigned int count = size; int ret = 0; @@ -144,7 +145,7 @@ if (count) ret = -ENXIO; - if (p < gbFlashSize) { + if (n == p && p < gbFlashSize) { if (count > gbFlashSize - p) count = gbFlashSize - p; @@ -157,7 +158,7 @@ ret = copy_to_user(buf, (void *)(FLASH_BASE + p), count); if (ret == 0) { ret = count; - *ppos += count; + *ppos = p + count; } up(&nwflash_sem); } @@ -166,7 +167,8 @@ static ssize_t flash_write(struct file *file, const char *buf, size_t size, loff_t * ppos) { - unsigned long p = *ppos; + loff_t n = *ppos; + unsigned long p = n; unsigned int count = size; int written; int nBlock, temp, rc; @@ -185,7 +187,7 @@ /* * check for out of range pos or count */ - if (p >= gbFlashSize) + if (p != n || p >= gbFlashSize) return count ? -ENXIO : 0; if (count > gbFlashSize - p) @@ -274,7 +276,7 @@ p += rc; buf += rc; written += rc; - *ppos += rc; + *ppos = p; if (flashdebug) printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written); diff -urN linux-2.4.26/drivers/char/parport_serial.c linux-2.4.27/drivers/char/parport_serial.c --- linux-2.4.26/drivers/char/parport_serial.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/char/parport_serial.c 2004-08-07 16:26:04.718349407 -0700 @@ -0,0 +1,436 @@ +/* + * Support for common PCI multi-I/O cards (which is most of them) + * + * Copyright (C) 2001 Tim Waugh + * + * 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. + * + * + * Multi-function PCI cards are supposed to present separate logical + * devices on the bus. A common thing to do seems to be to just use + * one logical device with lots of base address registers for both + * parallel ports and serial ports. This driver is for dealing with + * that. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum parport_pc_pci_cards { + titan_110l = 0, + titan_210l, + netmos_9735, + netmos_9835, + avlab_1s1p, + avlab_1s1p_650, + avlab_1s1p_850, + avlab_1s2p, + avlab_1s2p_650, + avlab_1s2p_850, + avlab_2s1p, + avlab_2s1p_650, + avlab_2s1p_850, + siig_1s1p_10x, + siig_2s1p_10x, + siig_2p1s_20x, + siig_1s1p_20x, + siig_2s1p_20x, +}; + + +/* each element directly indexed from enum list, above */ +static struct parport_pc_pci { + int numports; + struct { /* BAR (base address registers) numbers in the config + space header */ + int lo; + int hi; /* -1 if not there, >6 for offset-method (max + BAR is 6) */ + } addr[4]; + + /* If set, this is called immediately after pci_enable_device. + * If it returns non-zero, no probing will take place and the + * ports will not be used. */ + int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma); + + /* If set, this is called after probing for ports. If 'failed' + * is non-zero we couldn't use any of the ports. */ + void (*postinit_hook) (struct pci_dev *pdev, int failed); +} cards[] __devinitdata = { + /* titan_110l */ { 1, { { 3, -1 }, } }, + /* titan_210l */ { 1, { { 3, -1 }, } }, + /* netmos_9735 (not tested) */ { 1, { { 2, -1 }, } }, + /* netmos_9835 */ { 1, { { 2, -1 }, } }, + /* avlab_1s1p */ { 1, { { 1, 2}, } }, + /* avlab_1s1p_650 */ { 1, { { 1, 2}, } }, + /* avlab_1s1p_850 */ { 1, { { 1, 2}, } }, + /* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} }, + /* avlab_1s2p_650 */ { 2, { { 1, 2}, { 3, 4 },} }, + /* avlab_1s2p_850 */ { 2, { { 1, 2}, { 3, 4 },} }, + /* avlab_2s1p */ { 1, { { 2, 3}, } }, + /* avlab_2s1p_650 */ { 1, { { 2, 3}, } }, + /* avlab_2s1p_850 */ { 1, { { 2, 3}, } }, + /* siig_1s1p_10x */ { 1, { { 3, 4 }, } }, + /* siig_2s1p_10x */ { 1, { { 4, 5 }, } }, + /* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } }, + /* siig_1s1p_20x */ { 1, { { 1, 2 }, } }, + /* siig_2s1p_20x */ { 1, { { 2, 3 }, } }, +}; + +static struct pci_device_id parport_serial_pci_tbl[] __devinitdata = { + /* PCI cards */ + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l }, + { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9735, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9735 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9835 }, + /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ + { 0x14db, 0x2110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p}, + { 0x14db, 0x2111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p_650}, + { 0x14db, 0x2112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p_850}, + { 0x14db, 0x2140, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p}, + { 0x14db, 0x2141, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p_650}, + { 0x14db, 0x2142, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p_850}, + { 0x14db, 0x2160, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p}, + { 0x14db, 0x2161, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p_650}, + { 0x14db, 0x2162, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p_850}, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, + + { 0, } /* terminate list */ +}; +MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); + +struct pci_board_no_ids { + int flags; + int num_ports; + int base_baud; + int uart_offset; + int reg_shift; + int (*init_fn)(struct pci_dev *dev, struct pci_board_no_ids *board, + int enable); + int first_uart_offset; +}; + +static int __devinit siig10x_init_fn(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) +{ + return pci_siig10x_fn(dev, NULL, enable); +} + +static int __devinit siig20x_init_fn(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) +{ + return pci_siig20x_fn(dev, NULL, enable); +} + +static struct pci_board_no_ids pci_boards[] __devinitdata = { + /* + * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, + * Offset to get to next UART's registers, + * Register shift to use for memory-mapped I/O, + * Initialization function, first UART offset + */ + +// Cards not tested are marked n/t +// If you have one of these cards and it works for you, please tell me.. + +/* titan_110l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 1, 921600 }, +/* titan_210l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, +/* netmos_9735 (n/t)*/ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, +/* netmos_9835 */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, +/* avlab_1s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, +/* avlab_1s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, +/* avlab_1s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, +/* avlab_1s2p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, +/* avlab_1s2p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, +/* avlab_1s2p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, +/* avlab_2s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, +/* avlab_2s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, +/* avlab_2s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, +/* siig_1s1p_10x */ { SPCI_FL_BASE2, 1, 460800, 0, 0, siig10x_init_fn }, +/* siig_2s1p_10x */ { SPCI_FL_BASE2, 1, 921600, 0, 0, siig10x_init_fn }, +/* siig_2p1s_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, +/* siig_1s1p_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, +/* siig_2s1p_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, +}; + +struct parport_serial_private { + int num_ser; + int line[20]; + struct pci_board_no_ids ser; + int num_par; + struct parport *port[PARPORT_MAX]; +}; + +static int __devinit get_pci_port (struct pci_dev *dev, + struct pci_board_no_ids *board, + struct serial_struct *req, + int idx) +{ + unsigned long port; + int base_idx; + int max_port; + int offset; + + base_idx = SPCI_FL_GET_BASE(board->flags); + if (board->flags & SPCI_FL_BASE_TABLE) + base_idx += idx; + + if (board->flags & SPCI_FL_REGION_SZ_CAP) { + max_port = pci_resource_len(dev, base_idx) / 8; + if (idx >= max_port) + return 1; + } + + offset = board->first_uart_offset; + + /* Timedia/SUNIX uses a mixture of BARs and offsets */ + /* Ugh, this is ugly as all hell --- TYT */ + if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ + switch(idx) { + case 0: base_idx=0; + break; + case 1: base_idx=0; offset=8; + break; + case 2: base_idx=1; + break; + case 3: base_idx=1; offset=8; + break; + case 4: /* BAR 2*/ + case 5: /* BAR 3 */ + case 6: /* BAR 4*/ + case 7: base_idx=idx-2; /* BAR 5*/ + } + + port = pci_resource_start(dev, base_idx) + offset; + + if ((board->flags & SPCI_FL_BASE_TABLE) == 0) + port += idx * (board->uart_offset ? board->uart_offset : 8); + + if (pci_resource_flags (dev, base_idx) & IORESOURCE_IO) { + int high_bits_offset = ((sizeof(long)-sizeof(int))*8); + req->port = port; + if (high_bits_offset) + req->port_high = port >> high_bits_offset; + else + req->port_high = 0; + return 0; + } + req->io_type = SERIAL_IO_MEM; + req->iomem_base = ioremap(port, board->uart_offset); + req->iomem_reg_shift = board->reg_shift; + req->port = 0; + return req->iomem_base ? 0 : 1; +} + +/* Register the serial port(s) of a PCI card. */ +static int __devinit serial_register (struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct pci_board_no_ids *board = &pci_boards[id->driver_data]; + struct parport_serial_private *priv = pci_get_drvdata (dev); + struct serial_struct serial_req; + int base_baud; + int k; + int success = 0; + + priv->ser = *board; + if (board->init_fn && ((board->init_fn) (dev, board, 1) != 0)) + return 1; + + base_baud = board->base_baud; + if (!base_baud) + base_baud = BASE_BAUD; + memset (&serial_req, 0, sizeof (serial_req)); + + for (k = 0; k < board->num_ports; k++) { + int line; + serial_req.irq = dev->irq; + if (get_pci_port (dev, board, &serial_req, k)) + break; + serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; + serial_req.baud_base = base_baud; + line = register_serial (&serial_req); + if (line < 0) { + printk (KERN_DEBUG + "parport_serial: register_serial failed\n"); + continue; + } + priv->line[priv->num_ser++] = line; + success = 1; + } + + return success ? 0 : 1; +} + +/* Register the parallel port(s) of a PCI card. */ +static int __devinit parport_register (struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct parport_serial_private *priv = pci_get_drvdata (dev); + int i = id->driver_data, n; + int success = 0; + + if (cards[i].preinit_hook && + cards[i].preinit_hook (dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) + return -ENODEV; + + for (n = 0; n < cards[i].numports; n++) { + struct parport *port; + int lo = cards[i].addr[n].lo; + int hi = cards[i].addr[n].hi; + unsigned long io_lo, io_hi; + io_lo = pci_resource_start (dev, lo); + io_hi = 0; + if ((hi >= 0) && (hi <= 6)) + io_hi = pci_resource_start (dev, hi); + else if (hi > 6) + io_lo += hi; /* Reinterpret the meaning of + "hi" as an offset (see SYBA + def.) */ + /* TODO: test if sharing interrupts works */ + printk (KERN_DEBUG "PCI parallel port detected: %04x:%04x, " + "I/O at %#lx(%#lx)\n", + parport_serial_pci_tbl[i].vendor, + parport_serial_pci_tbl[i].device, io_lo, io_hi); + port = parport_pc_probe_port (io_lo, io_hi, PARPORT_IRQ_NONE, + PARPORT_DMA_NONE, dev); + if (port) { + priv->port[priv->num_par++] = port; + success = 1; + } + } + + if (cards[i].postinit_hook) + cards[i].postinit_hook (dev, !success); + + return success ? 0 : 1; +} + +static int __devinit parport_serial_pci_probe (struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct parport_serial_private *priv; + int err; + + priv = kmalloc (sizeof *priv, GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->num_ser = priv->num_par = 0; + pci_set_drvdata (dev, priv); + + err = pci_enable_device (dev); + if (err) { + pci_set_drvdata (dev, NULL); + kfree (priv); + return err; + } + + if (parport_register (dev, id)) { + pci_set_drvdata (dev, NULL); + kfree (priv); + return -ENODEV; + } + + if (serial_register (dev, id)) { + int i; + for (i = 0; i < priv->num_par; i++) + parport_pc_unregister_port (priv->port[i]); + pci_set_drvdata (dev, NULL); + kfree (priv); + return -ENODEV; + } + + return 0; +} + +static void __devexit parport_serial_pci_remove (struct pci_dev *dev) +{ + struct parport_serial_private *priv = pci_get_drvdata (dev); + int i; + + // Serial ports + for (i = 0; i < priv->num_ser; i++) { + unregister_serial (priv->line[i]); + + if (priv->ser.init_fn) + (priv->ser.init_fn) (dev, &priv->ser, 0); + } + pci_set_drvdata (dev, NULL); + + // Parallel ports + for (i = 0; i < priv->num_par; i++) + parport_pc_unregister_port (priv->port[i]); + + kfree (priv); + return; +} + +static struct pci_driver parport_serial_pci_driver = { + name: "parport_serial", + id_table: parport_serial_pci_tbl, + probe: parport_serial_pci_probe, + remove: __devexit_p(parport_serial_pci_remove), +}; + + +static int __init parport_serial_init (void) +{ + return pci_module_init (&parport_serial_pci_driver); +} + +static void __exit parport_serial_exit (void) +{ + pci_unregister_driver (&parport_serial_pci_driver); + return; +} + +MODULE_AUTHOR("Tim Waugh "); +MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards"); +MODULE_LICENSE("GPL"); + +module_init(parport_serial_init); +module_exit(parport_serial_exit); diff -urN linux-2.4.26/drivers/char/raw.c linux-2.4.27/drivers/char/raw.c --- linux-2.4.26/drivers/char/raw.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/char/raw.c 2004-08-07 16:26:04.719349448 -0700 @@ -301,6 +301,7 @@ int minor; kdev_t dev; unsigned long limit; + loff_t off = *offp; int sector_size, sector_bits, sector_mask; int max_sectors; @@ -338,12 +339,12 @@ MAJOR(dev), MINOR(dev), limit); err = -EINVAL; - if ((*offp & sector_mask) || (size & sector_mask)) + if ((off & sector_mask) || (size & sector_mask)) goto out_free; err = 0; if (size) err = -ENXIO; - if ((*offp >> sector_bits) >= limit) + if ((off >> sector_bits) >= limit) goto out_free; /* @@ -353,7 +354,7 @@ */ transferred = 0; - blocknr = *offp >> sector_bits; + blocknr = off >> sector_bits; while (size > 0) { blocks = size >> sector_bits; if (blocks > max_sectors) @@ -390,7 +391,7 @@ } if (transferred) { - *offp += transferred; + *offp = off + transferred; err = transferred; } diff -urN linux-2.4.26/drivers/char/sysrq.c linux-2.4.27/drivers/char/sysrq.c --- linux-2.4.26/drivers/char/sysrq.c 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.27/drivers/char/sysrq.c 2004-08-07 16:26:04.720349489 -0700 @@ -365,7 +365,7 @@ /* v */ NULL, /* w */ NULL, /* x */ NULL, -/* w */ NULL, +/* y */ NULL, /* z */ NULL }; diff -urN linux-2.4.26/drivers/char/tipar.c linux-2.4.27/drivers/char/tipar.c --- linux-2.4.26/drivers/char/tipar.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/char/tipar.c 2004-08-07 16:26:04.720349489 -0700 @@ -66,7 +66,7 @@ /* * Version Information */ -#define DRIVER_VERSION "1.18" +#define DRIVER_VERSION "1.19" #define DRIVER_AUTHOR "Romain Lievin " #define DRIVER_DESC "Device driver for TI/PC parallel link cables" #define DRIVER_LICENSE "GPL" @@ -124,7 +124,7 @@ /* ----- global defines ----------------------------------------------- */ -#define START(x) { x=jiffies+HZ/(timeout/10); } +#define START(x) { x = jiffies + (HZ * timeout) / 10; } #define WAIT(x) { \ if (time_before((x), jiffies)) return -1; \ if (need_resched()) schedule(); } @@ -366,11 +366,14 @@ switch (cmd) { case IOCTL_TIPAR_DELAY: - delay = (int)arg; //get_user(delay, &arg); - break; + delay = (int)arg; //get_user(delay, &arg); + break; case IOCTL_TIPAR_TIMEOUT: - timeout = (int)arg; //get_user(timeout, &arg); - break; + if (arg != 0) + timeout = (int)arg; + else + retval = -EINVAL; + break; default: retval = -ENOTTY; break; @@ -404,7 +407,10 @@ str = get_options(str, ARRAY_SIZE(ints), ints); if (ints[0] > 0) { - timeout = ints[1]; + if (ints[1] != 0) + timeout = ints[1]; + else + printk("tipar: wrong timeout value (0), using default value instead."); if (ints[0] > 1) { delay = ints[2]; } diff -urN linux-2.4.26/drivers/char/tpqic02.c linux-2.4.27/drivers/char/tpqic02.c --- linux-2.4.26/drivers/char/tpqic02.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/char/tpqic02.c 2004-08-07 16:26:04.722349572 -0700 @@ -1818,6 +1818,7 @@ kdev_t dev = filp->f_dentry->d_inode->i_rdev; unsigned short flags = filp->f_flags; unsigned long bytes_todo, bytes_done, total_bytes_done = 0; + loff_t pos = *ppos; int stat; if (status_zombie == YES) { @@ -1898,6 +1899,7 @@ /*****************************/ if (bytes_todo == 0) { + *ppos = pos; return total_bytes_done; } @@ -1966,7 +1968,7 @@ if (bytes_done > 0) { status_bytes_rd = YES; buf += bytes_done; - *ppos += bytes_done; + pos += bytes_done; total_bytes_done += bytes_done; count -= bytes_done; } diff -urN linux-2.4.26/drivers/char/vc_screen.c linux-2.4.27/drivers/char/vc_screen.c --- linux-2.4.26/drivers/char/vc_screen.c 2001-09-16 21:22:40.000000000 -0700 +++ linux-2.4.27/drivers/char/vc_screen.c 2004-08-07 16:26:04.723349613 -0700 @@ -64,12 +64,25 @@ return size; } +/* We share this temporary buffer with the console write code + * so that we can easily avoid touching user space while holding the + * console spinlock. + */ +extern char con_buf[PAGE_SIZE]; +#define CON_BUF_SIZE PAGE_SIZE +extern struct semaphore con_buf_sem; + static loff_t vcs_lseek(struct file *file, loff_t offset, int orig) { - int size = vcs_size(file->f_dentry->d_inode); + int size; + + down(&con_buf_sem); + + size = vcs_size(file->f_dentry->d_inode); switch (orig) { default: + up(&con_buf_sem); return -EINVAL; case 2: offset += size; @@ -79,26 +92,22 @@ case 0: break; } - if (offset < 0 || offset > size) + if (offset < 0 || offset > size) { + up(&con_buf_sem); return -EINVAL; + } file->f_pos = offset; + up (&con_buf_sem); return file->f_pos; } -/* We share this temporary buffer with the console write code - * so that we can easily avoid touching user space while holding the - * console spinlock. - */ -extern char con_buf[PAGE_SIZE]; -#define CON_BUF_SIZE PAGE_SIZE -extern struct semaphore con_buf_sem; - static ssize_t vcs_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct inode *inode = file->f_dentry->d_inode; unsigned int currcons = MINOR(inode->i_rdev); - long pos = *ppos; + loff_t n; + unsigned pos; long viewed, attr, read; int col, maxcol; unsigned short *org = NULL; @@ -106,6 +115,9 @@ down(&con_buf_sem); + n = *ppos; + pos = n; + /* Select the proper current console and verify * sanity of the situation under the console lock. */ @@ -124,11 +136,10 @@ if (!vc_cons_allocated(currcons)) goto unlock_out; - ret = -EINVAL; - if (pos < 0) - goto unlock_out; read = 0; ret = 0; + if (pos != n) + goto unlock_out; while (count) { char *con_buf0, *con_buf_start; long this_round, size; @@ -244,16 +255,15 @@ acquire_console_sem(); if (ret) { - read += (orig_count - ret); ret = -EFAULT; - break; + goto unlock_out; } buf += orig_count; pos += orig_count; read += orig_count; count -= orig_count; } - *ppos += read; + *ppos = pos; if (read) ret = read; unlock_out: @@ -267,7 +277,8 @@ { struct inode *inode = file->f_dentry->d_inode; unsigned int currcons = MINOR(inode->i_rdev); - long pos = *ppos; + loff_t n; + unsigned pos; long viewed, attr, size, written; char *con_buf0; int col, maxcol; @@ -276,6 +287,9 @@ down(&con_buf_sem); + n = *ppos; + pos = n; + /* Select the proper current console and verify * sanity of the situation under the console lock. */ @@ -297,7 +311,7 @@ size = vcs_size(inode); ret = -EINVAL; - if (pos < 0 || pos > size) + if (pos != n || pos > size) goto unlock_out; if (count > size - pos) count = size - pos; @@ -435,7 +449,7 @@ if (org0) update_region(currcons, (unsigned long)(org0), org-org0); } - *ppos += written; + *ppos = pos; ret = written; unlock_out: diff -urN linux-2.4.26/drivers/gsc/eisa_eeprom.c linux-2.4.27/drivers/gsc/eisa_eeprom.c --- linux-2.4.26/drivers/gsc/eisa_eeprom.c 2002-11-28 15:53:12.000000000 -0800 +++ linux-2.4.27/drivers/gsc/eisa_eeprom.c 2004-08-07 16:26:04.733350024 -0700 @@ -34,24 +34,30 @@ unsigned char *tmp; ssize_t ret; int i; + loff_t n = *ppos; + unsigned pos = n; - if (*ppos >= HPEE_MAX_LENGTH) + if (n != pos || pos >= HPEE_MAX_LENGTH) return 0; - count = *ppos + count < HPEE_MAX_LENGTH ? count : HPEE_MAX_LENGTH - *ppos; + if (count > HPEE_MAX_LENGTH - pos) + count = HPEE_MAX_LENGTH - pos; + tmp = kmalloc(count, GFP_KERNEL); if (tmp) { for (i = 0; i < count; i++) - tmp[i] = gsc_readb(eeprom_addr+(*ppos)++); + tmp[i] = gsc_readb(eeprom_addr+(pos)++); if (copy_to_user (buf, tmp, count)) ret = -EFAULT; - else + else { ret = count; + *ppos = pos; + } kfree (tmp); } else ret = -ENOMEM; - + return ret; } diff -urN linux-2.4.26/drivers/hotplug/Config.in linux-2.4.27/drivers/hotplug/Config.in --- linux-2.4.26/drivers/hotplug/Config.in 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/Config.in 2004-08-07 16:26:04.733350024 -0700 @@ -14,4 +14,11 @@ if [ "$CONFIG_ACPI_INTERPRETER" = "y" ]; then dep_tristate ' ACPI PCI Hotplug driver' CONFIG_HOTPLUG_PCI_ACPI $CONFIG_HOTPLUG_PCI fi +dep_tristate ' SHPC PCI Hotplug driver' CONFIG_HOTPLUG_PCI_SHPC $CONFIG_HOTPLUG_PCI +dep_mbool ' Use polling mechanism for hot-plug events (for testing purpose)' CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE $CONFIG_HOTPLUG_PCI_SHPC +if [ "$CONFIG_ACPI" = "n" ]; then +dep_mbool ' For AMD SHPC only: Use HRT for resource/configuration' CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY $CONFIG_HOTPLUG_PCI_SHPC +fi +dep_tristate ' PCI Express Hotplug driver' CONFIG_HOTPLUG_PCI_PCIE $CONFIG_HOTPLUG_PCI +dep_mbool ' Use polling mechanism for hot-plug events (for testing purpose)' CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE $CONFIG_HOTPLUG_PCI_PCIE endmenu diff -urN linux-2.4.26/drivers/hotplug/Makefile linux-2.4.27/drivers/hotplug/Makefile --- linux-2.4.26/drivers/hotplug/Makefile 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.27/drivers/hotplug/Makefile 2004-08-07 16:26:04.734350065 -0700 @@ -4,7 +4,7 @@ O_TARGET := vmlinux-obj.o -list-multi := cpqphp.o pci_hotplug.o ibmphp.o acpiphp.o +list-multi := cpqphp.o pci_hotplug.o ibmphp.o acpiphp.o shpchp.o pciehp.o export-objs := pci_hotplug_core.o pci_hotplug_util.o @@ -12,6 +12,8 @@ obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o +obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o +obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o pci_hotplug-objs := pci_hotplug_core.o \ pci_hotplug_util.o @@ -32,11 +34,36 @@ acpiphp_pci.o \ acpiphp_res.o +pciehp-objs := pciehp_core.o \ + pciehp_ctrl.o \ + pciehp_hpc.o \ + pciehp_pci.o +ifdef CONFIG_ACPI_INTERPRETER + pciehp-objs += pciehprm_acpi.o +else + pciehp-objs += pciehprm_nonacpi.o + EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi +endif + +shpchp-objs := shpchp_core.o \ + shpchp_ctrl.o \ + shpchp_hpc.o \ + shpchp_pci.o +ifdef CONFIG_ACPI_INTERPRETER + shpchp-objs += shpchprm_acpi.o + EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi +else + ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY + shpchp-objs += shpchprm_legacy.o + else + shpchp-objs += shpchprm_nonacpi.o + endif +endif + ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y) cpqphp-objs += cpqphp_nvram.o endif - include $(TOPDIR)/Rules.make pci_hotplug.o: $(pci_hotplug-objs) @@ -50,3 +77,10 @@ acpiphp.o: $(acpiphp_objs) $(LD) -r -o $@ $(acpiphp_objs) + +pciehp.o: $(pciehp-objs) + $(LD) -r -o $@ $(pciehp-objs) + +shpchp.o: $(shpchp-objs) + $(LD) -r -o $@ $(shpchp-objs) + diff -urN linux-2.4.26/drivers/hotplug/acpiphp.h linux-2.4.27/drivers/hotplug/acpiphp.h --- linux-2.4.26/drivers/hotplug/acpiphp.h 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/acpiphp.h 2004-08-07 16:26:04.734350065 -0700 @@ -36,63 +36,7 @@ #include #include "pci_hotplug.h" -#if ACPI_CA_VERSION < 0x20020201 -/* until we get a new version of the ACPI driver for both ia32 and ia64 ... */ -#define acpi_util_eval_error(h,p,s) - -static acpi_status -acpi_evaluate_integer ( - acpi_handle handle, - acpi_string pathname, - acpi_object_list *arguments, - unsigned long *data) -{ - acpi_status status = AE_OK; - acpi_object element; - acpi_buffer buffer = {sizeof(acpi_object), &element}; - - if (!data) - return AE_BAD_PARAMETER; - - status = acpi_evaluate_object(handle, pathname, arguments, &buffer); - if (ACPI_FAILURE(status)) { - acpi_util_eval_error(handle, pathname, status); - return status; - } - - if (element.type != ACPI_TYPE_INTEGER) { - acpi_util_eval_error(handle, pathname, AE_BAD_DATA); - return AE_BAD_DATA; - } - - *data = element.integer.value; - - return AE_OK; -} -#else /* ACPI_CA_VERSION < 0x20020201 */ #include -#endif - -/* compatibility stuff for ACPI CA */ -#ifndef ACPI_MEMORY_RANGE -#define ACPI_MEMORY_RANGE MEMORY_RANGE -#endif - -#ifndef ACPI_IO_RANGE -#define ACPI_IO_RANGE IO_RANGE -#endif - -#ifndef ACPI_BUS_NUMBER_RANGE -#define ACPI_BUS_NUMBER_RANGE BUS_NUMBER_RANGE -#endif - -#ifndef ACPI_PREFETCHABLE_MEMORY -#define ACPI_PREFETCHABLE_MEMORY PREFETCHABLE_MEMORY -#endif - -#ifndef ACPI_PRODUCER -#define ACPI_PRODUCER PRODUCER -#endif #define dbg(format, arg...) \ do { \ @@ -258,7 +202,7 @@ #define SLOT_POWEREDON (0x00000001) #define SLOT_ENABLED (0x00000002) -#define SLOT_MULTIFUNCTION (x000000004) +#define SLOT_MULTIFUNCTION (0x00000004) /* function flags */ @@ -269,8 +213,6 @@ #define FUNC_HAS_PS2 (0x00000040) #define FUNC_HAS_PS3 (0x00000080) -#define FUNC_EXISTS (0x10000000) /* to make sure we call _EJ0 only for existing funcs */ - /* function prototypes */ /* acpiphp_glue.c */ @@ -288,6 +230,7 @@ extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot); extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot); +extern u32 acpiphp_get_address (struct acpiphp_slot *slot); /* acpiphp_pci.c */ extern struct pci_dev *acpiphp_allocate_pcidev (struct pci_bus *pbus, int dev, int fn); diff -urN linux-2.4.26/drivers/hotplug/acpiphp_core.c linux-2.4.27/drivers/hotplug/acpiphp_core.c --- linux-2.4.26/drivers/hotplug/acpiphp_core.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/acpiphp_core.c 2004-08-07 16:26:04.735350106 -0700 @@ -30,14 +30,14 @@ * */ -#include -#include +#include #include + +#include #include #include #include #include -#include #include "pci_hotplug.h" #include "acpiphp.h" @@ -73,6 +73,7 @@ static int get_attention_status (struct hotplug_slot *slot, u8 *value); static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); +static int get_address (struct hotplug_slot *slot, u32 *value); static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value); @@ -86,6 +87,7 @@ .get_attention_status = get_attention_status, .get_latch_status = get_latch_status, .get_adapter_status = get_adapter_status, + .get_address = get_address, .get_max_bus_speed = get_max_bus_speed, .get_cur_bus_speed = get_cur_bus_speed, }; @@ -322,6 +324,28 @@ } +/** + * get_address - get pci address of a slot + * @hotplug_slot: slot to get status + * @busdev: pointer to struct pci_busdev (seg, bus, dev) + * + */ +static int get_address (struct hotplug_slot *hotplug_slot, u32 *value) +{ + struct slot *slot = get_slot(hotplug_slot, __FUNCTION__); + int retval = 0; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + *value = acpiphp_get_address(slot->acpi_slot); + + return retval; +} + + /* return dummy value because ACPI doesn't provide any method... */ static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) { diff -urN linux-2.4.26/drivers/hotplug/acpiphp_glue.c linux-2.4.27/drivers/hotplug/acpiphp_glue.c --- linux-2.4.26/drivers/hotplug/acpiphp_glue.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/acpiphp_glue.c 2004-08-07 16:26:04.737350188 -0700 @@ -26,12 +26,12 @@ * */ -#include -#include +#include #include + +#include #include #include -#include #include #include "pci_hotplug.h" @@ -389,12 +389,8 @@ static void decode_hpp(struct acpiphp_bridge *bridge) { acpi_status status; -#if ACPI_CA_VERSION < 0x20020201 - acpi_buffer buffer; -#else struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER, .pointer = NULL}; -#endif union acpi_object *package; int i; @@ -404,21 +400,7 @@ bridge->hpp.enable_SERR = 0; bridge->hpp.enable_PERR = 0; -#if ACPI_CA_VERSION < 0x20020201 - buffer.length = 0; - buffer.pointer = NULL; - - status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); - - if (status == AE_BUFFER_OVERFLOW) { - buffer.pointer = kmalloc(buffer.length, GFP_KERNEL); - if (!buffer.pointer) - return; - status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); - } -#else status = acpi_evaluate_object(bridge->handle, "_HPP", NULL, &buffer); -#endif if (ACPI_FAILURE(status)) { dbg("_HPP evaluation failed\n"); @@ -494,12 +476,8 @@ static void add_host_bridge (acpi_handle *handle, int seg, int bus) { acpi_status status; -#if ACPI_CA_VERSION < 0x20020201 - acpi_buffer buffer; -#else struct acpi_buffer buffer = { .length = ACPI_ALLOCATE_BUFFER, .pointer = NULL}; -#endif struct acpiphp_bridge *bridge; bridge = kmalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); @@ -522,22 +500,8 @@ /* decode resources */ -#if ACPI_CA_VERSION < 0x20020201 - buffer.length = 0; - buffer.pointer = NULL; - status = acpi_get_current_resources(handle, &buffer); - if (status == AE_BUFFER_OVERFLOW) { - buffer.pointer = kmalloc(buffer.length, GFP_KERNEL); - if (!buffer.pointer) - return; - status = acpi_get_current_resources(handle, &buffer); - } -#else - status = acpi_get_current_resources(handle, &buffer); -#endif - if (ACPI_FAILURE(status)) { err("failed to decode bridge resources\n"); kfree(bridge); @@ -819,7 +783,7 @@ struct list_head *l; int retval = 0; - /* is this already enabled? */ + /* if already enabled, just skip */ if (slot->flags & SLOT_POWEREDON) goto err_exit; @@ -827,14 +791,14 @@ func = list_entry(l, struct acpiphp_func, sibling); if (func->flags & FUNC_HAS_PS0) { - dbg("%s: executing _PS0 on %s\n", __FUNCTION__, - func->pci_dev->slot_name); + dbg("%s: executing _PS0\n", __FUNCTION__); status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL); if (ACPI_FAILURE(status)) { warn("%s: _PS0 failed\n", __FUNCTION__); retval = -1; goto err_exit; - } + } else + break; } } @@ -857,20 +821,21 @@ int retval = 0; - /* is this already enabled? */ + /* if already disabled, just skip */ if ((slot->flags & SLOT_POWEREDON) == 0) goto err_exit; list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); - if (func->flags & (FUNC_HAS_PS3 | FUNC_EXISTS)) { + if (func->pci_dev && (func->flags & FUNC_HAS_PS3)) { status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL); if (ACPI_FAILURE(status)) { warn("%s: _PS3 failed\n", __FUNCTION__); retval = -1; goto err_exit; - } + } else + break; } } @@ -878,20 +843,19 @@ func = list_entry(l, struct acpiphp_func, sibling); /* We don't want to call _EJ0 on non-existing functions. */ - if (func->flags & (FUNC_HAS_EJ0 | FUNC_EXISTS)) { + if (func->pci_dev && (func->flags & FUNC_HAS_EJ0)) { /* _EJ0 method take one argument */ arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; arg.integer.value = 1; - status = acpi_evaluate_object(func->handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) { warn("%s: _EJ0 failed\n", __FUNCTION__); retval = -1; goto err_exit; - } - func->flags &= (~FUNC_EXISTS); + } else + break; } } @@ -973,8 +937,6 @@ retval = acpiphp_configure_function(func); if (retval) goto err_exit; - - func->flags |= FUNC_EXISTS; } slot->flags |= SLOT_ENABLED; @@ -1003,15 +965,12 @@ list_for_each (l, &slot->funcs) { func = list_entry(l, struct acpiphp_func, sibling); - if (func->pci_dev) { - if (acpiphp_unconfigure_function(func) == 0) { - func->pci_dev = NULL; - } else { + if (func->pci_dev) + if (acpiphp_unconfigure_function(func)) { err("failed to unconfigure device\n"); retval = -1; goto err_exit; } - } } slot->flags &= (~SLOT_ENABLED); @@ -1391,7 +1350,7 @@ up(&slot->crit_sect); goto err_exit; } - enabled++; + disabled++; } } else { /* if disabled but present, enable */ @@ -1402,7 +1361,7 @@ up(&slot->crit_sect); goto err_exit; } - disabled++; + enabled++; } } } @@ -1468,3 +1427,18 @@ return (sta == 0) ? 0 : 1; } + + +/* + * pci address (seg/bus/dev) + */ +u32 acpiphp_get_address (struct acpiphp_slot *slot) +{ + u32 address; + + address = ((slot->bridge->seg) << 16) | + ((slot->bridge->bus) << 8) | + slot->device; + + return address; +} diff -urN linux-2.4.26/drivers/hotplug/acpiphp_pci.c linux-2.4.27/drivers/hotplug/acpiphp_pci.c --- linux-2.4.26/drivers/hotplug/acpiphp_pci.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/acpiphp_pci.c 2004-08-07 16:26:04.737350188 -0700 @@ -29,11 +29,11 @@ * */ -#include -#include +#include #include + +#include #include -#include #include "pci_hotplug.h" #include "acpiphp.h" @@ -78,8 +78,8 @@ if (bar & PCI_BASE_ADDRESS_SPACE_IO) { /* This is IO */ - len = bar & 0xFFFFFFFC; - len = ~len + 1; + len = bar & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); + len = len & ~(len - 1); dbg("len in IO %x, BAR %d\n", len, count); @@ -340,8 +340,8 @@ if (len & PCI_BASE_ADDRESS_SPACE_IO) { /* This is IO */ base = bar & 0xFFFFFFFC; - len &= 0xFFFFFFFC; - len = ~len + 1; + len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); + len = len & ~(len - 1); dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); @@ -465,8 +465,8 @@ if (len & PCI_BASE_ADDRESS_SPACE_IO) { /* This is IO */ base = bar & 0xFFFFFFFC; - len &= 0xFFFFFFFC; - len = ~len + 1; + len = len & (PCI_BASE_ADDRESS_IO_MASK & 0xFFFF); + len = len & ~(len - 1); dbg("BAR[%d] %08x - %08x (IO)\n", count, (u32)base, (u32)base + len - 1); diff -urN linux-2.4.26/drivers/hotplug/acpiphp_res.c linux-2.4.27/drivers/hotplug/acpiphp_res.c --- linux-2.4.26/drivers/hotplug/acpiphp_res.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/acpiphp_res.c 2004-08-07 16:26:04.738350229 -0700 @@ -29,7 +29,7 @@ * */ -#include +#include #include #include @@ -39,7 +39,6 @@ #include #include #include -#include #include #include @@ -225,7 +224,7 @@ } /* End of too big on top end */ /* For IO make sure it's not in the ISA aliasing space */ - if (node->base & 0x300L) + if ((node->base & 0x300L) && !(node->base & 0xfffff000)) continue; /* If we got here, then it is the right size diff -urN linux-2.4.26/drivers/hotplug/pci_hotplug.h linux-2.4.27/drivers/hotplug/pci_hotplug.h --- linux-2.4.26/drivers/hotplug/pci_hotplug.h 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pci_hotplug.h 2004-08-07 16:26:04.739350270 -0700 @@ -36,15 +36,36 @@ PCI_SPEED_66MHz_PCIX = 0x02, PCI_SPEED_100MHz_PCIX = 0x03, PCI_SPEED_133MHz_PCIX = 0x04, + PCI_SPEED_66MHz_PCIX_ECC = 0x05, + PCI_SPEED_100MHz_PCIX_ECC = 0x06, + PCI_SPEED_133MHz_PCIX_ECC = 0x07, PCI_SPEED_66MHz_PCIX_266 = 0x09, PCI_SPEED_100MHz_PCIX_266 = 0x0a, PCI_SPEED_133MHz_PCIX_266 = 0x0b, PCI_SPEED_66MHz_PCIX_533 = 0x11, - PCI_SPEED_100MHz_PCIX_533 = 0X12, + PCI_SPEED_100MHz_PCIX_533 = 0x12, PCI_SPEED_133MHz_PCIX_533 = 0x13, PCI_SPEED_UNKNOWN = 0xff, }; +/* These values come from the PCI Express Spec */ +enum pcie_link_width { + PCIE_LNK_WIDTH_RESRV = 0x00, + PCIE_LNK_X1 = 0x01, + PCIE_LNK_X2 = 0x02, + PCIE_LNK_X4 = 0x04, + PCIE_LNK_X8 = 0x08, + PCIE_LNK_X12 = 0x0C, + PCIE_LNK_X16 = 0x10, + PCIE_LNK_X32 = 0x20, + PCIE_LNK_WIDTH_UNKNOWN = 0xFF, +}; + +enum pcie_link_speed { + PCIE_2PT5GB = 0x14, + PCIE_LNK_SPEED_UNKNOWN = 0xFF, +}; + struct hotplug_slot; struct hotplug_slot_core; @@ -69,6 +90,9 @@ * @get_adapter_status: Called to get see if an adapter is present in the slot or not. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. + * @get_address: Called to get pci address of a slot. + * If this field is NULL, the value passed in the struct hotplug_slot_info + * will be used when this value is requested by a user. * @get_max_bus_speed: Called to get the max bus speed for a slot. * If this field is NULL, the value passed in the struct hotplug_slot_info * will be used when this value is requested by a user. @@ -91,6 +115,7 @@ int (*get_attention_status) (struct hotplug_slot *slot, u8 *value); int (*get_latch_status) (struct hotplug_slot *slot, u8 *value); int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value); + int (*get_address) (struct hotplug_slot *slot, u32 *value); int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value); }; @@ -101,6 +126,7 @@ * @attention_status: if the attention light is enabled or not (1/0) * @latch_status: if the latch (if any) is open or closed (1/0) * @adapter_present: if there is a pci board present in the slot or not (1/0) + * @address: (domain << 16 | bus << 8 | dev) * * Used to notify the hotplug pci core of the status of a specific slot. */ @@ -109,6 +135,7 @@ u8 attention_status; u8 latch_status; u8 adapter_status; + u32 address; enum pci_bus_speed max_bus_speed; enum pci_bus_speed cur_bus_speed; }; diff -urN linux-2.4.26/drivers/hotplug/pci_hotplug_core.c linux-2.4.27/drivers/hotplug/pci_hotplug_core.c --- linux-2.4.26/drivers/hotplug/pci_hotplug_core.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pci_hotplug_core.c 2004-08-07 16:26:04.740350311 -0700 @@ -74,6 +74,7 @@ struct dentry *attention_dentry; struct dentry *latch_dentry; struct dentry *adapter_dentry; + struct dentry *address_dentry; struct dentry *test_dentry; struct dentry *max_bus_speed_dentry; struct dentry *cur_bus_speed_dentry; @@ -111,6 +112,7 @@ "66 MHz PCIX 533", /* 0x11 */ "100 MHz PCIX 533", /* 0x12 */ "133 MHz PCIX 533", /* 0x13 */ + "25 GBps PCI-E", /* 0x14 */ }; static int pcihpfs_statfs (struct super_block *sb, struct statfs *buf) @@ -312,6 +314,15 @@ llseek: default_file_lseek, }; +/* file ops for the "address" files */ +static ssize_t address_read_file (struct file *file, char *buf, size_t count, loff_t *offset); +static struct file_operations address_file_operations = { + read: address_read_file, + write: default_write_file, + open: default_open, + llseek: default_file_lseek, +}; + /* file ops for the "max bus speed" files */ static ssize_t max_bus_speed_read_file (struct file *file, char *buf, size_t count, loff_t *offset); static struct file_operations max_bus_speed_file_operations = { @@ -566,6 +577,7 @@ GET_STATUS(attention_status, u8) GET_STATUS(latch_status, u8) GET_STATUS(adapter_status, u8) +GET_STATUS(address, u32) GET_STATUS(max_bus_speed, enum pci_bus_speed) GET_STATUS(cur_bus_speed, enum pci_bus_speed) @@ -604,7 +616,7 @@ retval = -EFAULT; goto exit; } - *offset += len; + *offset = len; retval = len; exit: @@ -715,7 +727,7 @@ retval = -EFAULT; goto exit; } - *offset += len; + *offset = len; retval = len; exit: @@ -780,14 +792,15 @@ int retval; int len; u8 value; + loff_t off = *offset; - dbg("count = %d, offset = %lld\n", count, *offset); + dbg("count = %d, offset = %lld\n", count, off); - if (*offset < 0) + if (off < 0) return -EINVAL; if (count <= 0) return 0; - if (*offset != 0) + if (off != 0) return 0; if (slot == NULL) { @@ -808,7 +821,7 @@ retval = -EFAULT; goto exit; } - *offset += len; + *offset = off + len; retval = len; exit: @@ -823,14 +836,15 @@ int retval; int len; u8 value; + loff_t off = *offset; dbg("count = %d, offset = %lld\n", count, *offset); - if (*offset < 0) + if (off < 0) return -EINVAL; if (count <= 0) return 0; - if (*offset != 0) + if (off != 0) return 0; if (slot == NULL) { @@ -851,7 +865,54 @@ retval = -EFAULT; goto exit; } - *offset += len; + *offset = off + len; + retval = len; + +exit: + free_page((unsigned long)page); + return retval; +} + +static ssize_t address_read_file (struct file *file, char *buf, size_t count, loff_t *offset) +{ + struct hotplug_slot *slot = file->private_data; + unsigned char *page; + int retval; + int len; + u32 address; + loff_t off = *offset; + + dbg("count = %d, offset = %lld\n", count, off); + + if (off < 0) + return -EINVAL; + if (count <= 0) + return 0; + if (off != 0) + return 0; + + if (slot == NULL) { + dbg("slot == NULL???\n"); + return -ENODEV; + } + + page = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + retval = get_address (slot, &address); + if (retval) + goto exit; + len = sprintf (page, "%04x:%02x:%02x\n", + (address >> 16) & 0xffff, + (address >> 8) & 0xff, + address & 0xff); + + if (copy_to_user (buf, page, len)) { + retval = -EFAULT; + goto exit; + } + *offset = off + len; retval = len; exit: @@ -869,14 +930,15 @@ int retval; int len = 0; enum pci_bus_speed value; + loff_t off = *offset; - dbg ("count = %d, offset = %lld\n", count, *offset); + dbg ("count = %d, offset = %lld\n", count, off); - if (*offset < 0) + if (off < 0) return -EINVAL; if (count <= 0) return 0; - if (*offset != 0) + if (off != 0) return 0; if (slot == NULL) { @@ -903,7 +965,7 @@ retval = -EFAULT; goto exit; } - *offset += len; + *offset = off + len; retval = len; exit: @@ -919,14 +981,15 @@ int retval; int len = 0; enum pci_bus_speed value; + loff_t off = *offset; - dbg ("count = %d, offset = %lld\n", count, *offset); + dbg ("count = %d, offset = %lld\n", count, off); - if (*offset < 0) + if (off < 0) return -EINVAL; if (count <= 0) return 0; - if (*offset != 0) + if (off != 0) return 0; if (slot == NULL) { @@ -953,7 +1016,7 @@ retval = -EFAULT; goto exit; } - *offset += len; + *offset = off + len; retval = len; exit: @@ -1055,6 +1118,13 @@ core->dir_dentry, slot, &presence_file_operations); + if (slot->ops->get_address) + core->address_dentry = + fs_create_file ("address", + S_IFREG | S_IRUGO, + core->dir_dentry, slot, + &address_file_operations); + if (slot->ops->get_max_bus_speed) core->max_bus_speed_dentry = fs_create_file ("max_bus_speed", @@ -1092,6 +1162,8 @@ fs_remove_file (core->latch_dentry); if (core->adapter_dentry) fs_remove_file (core->adapter_dentry); + if (core->address_dentry) + fs_remove_file (core->address_dentry); if (core->max_bus_speed_dentry) fs_remove_file (core->max_bus_speed_dentry); if (core->cur_bus_speed_dentry) @@ -1243,6 +1315,9 @@ if ((core->adapter_dentry) && (temp->info->adapter_status != info->adapter_status)) update_dentry_inode_time (core->adapter_dentry); + if ((core->address_dentry) && + (temp->info->address != info->address)) + update_dentry_inode_time (core->address_dentry); if ((core->cur_bus_speed_dentry) && (temp->info->cur_bus_speed != info->cur_bus_speed)) update_dentry_inode_time (core->cur_bus_speed_dentry); diff -urN linux-2.4.26/drivers/hotplug/pciehp.h linux-2.4.27/drivers/hotplug/pciehp.h --- linux-2.4.26/drivers/hotplug/pciehp.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehp.h 2004-08-07 16:26:04.742350393 -0700 @@ -0,0 +1,383 @@ +/* + * PCI Express Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ +#ifndef _PCIEHP_H +#define _PCIEHP_H + +#include +#include +#include +#include +#include "pci_hotplug.h" + +#if !defined(CONFIG_HOTPLUG_PCI_PCIE_MODULE) + #define MY_NAME "pciehp.o" +#else + #define MY_NAME THIS_MODULE->name +#endif + +extern int pciehp_poll_mode; +extern int pciehp_poll_time; +extern int pciehp_debug; + +extern int pciehp_msi_quirk; + +/*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/ +#define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) + +struct pci_func { + struct pci_func *next; + u8 bus; + u8 device; + u8 function; + u8 is_a_board; + u16 status; + u8 configured; + u8 switch_save; + u8 presence_save; + u32 base_length[0x06]; + u8 base_type[0x06]; + u16 reserved2; + u32 config_space[0x20]; + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct pci_dev* pci_dev; +}; + +#define SLOT_MAGIC 0x67267321 +struct slot { + u32 magic; + struct slot *next; + u8 bus; + u8 device; + u32 number; + u8 is_a_board; + u8 configured; + u8 state; + u8 switch_save; + u8 presence_save; + u32 capabilities; + u16 reserved2; + struct timer_list task_event; + u8 hp_slot; + struct controller *ctrl; + struct hpc_ops *hpc_ops; + struct hotplug_slot *hotplug_slot; + struct list_head slot_list; +}; + +struct pci_resource { + struct pci_resource * next; + u32 base; + u32 length; +}; + +struct event_info { + u32 event_type; + u8 hp_slot; +}; + +struct controller { + struct controller *next; + struct semaphore crit_sect; /* critical section semaphore */ + void * hpc_ctlr_handle; /* HPC controller handle */ + int num_slots; /* Number of slots on ctlr */ + int slot_num_inc; /* 1 or -1 */ + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct pci_dev *pci_dev; + struct pci_bus *pci_bus; + struct event_info event_queue[10]; + struct slot *slot; + struct hpc_ops *hpc_ops; + wait_queue_head_t queue; /* sleep & wake process */ + u8 next_event; + u8 seg; + u8 bus; + u8 device; + u8 function; + u8 rev; + u8 slot_device_offset; + u8 add_support; + enum pci_bus_speed speed; + u32 first_slot; /* First physical slot number; PCI-E only has 1 slot */ + u8 slot_bus; /* Bus where the slots handled by this controller sit */ + u8 push_flag; + u16 ctlrcap; + u16 vendor_id; +}; + +struct irq_mapping { + u8 barber_pole; + u8 valid_INT; + u8 interrupt[4]; +}; + +struct resource_lists { + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct irq_mapping *irqs; +}; + +#define INT_BUTTON_IGNORE 0 +#define INT_PRESENCE_ON 1 +#define INT_PRESENCE_OFF 2 +#define INT_SWITCH_CLOSE 3 +#define INT_SWITCH_OPEN 4 +#define INT_POWER_FAULT 5 +#define INT_POWER_FAULT_CLEAR 6 +#define INT_BUTTON_PRESS 7 +#define INT_BUTTON_RELEASE 8 +#define INT_BUTTON_CANCEL 9 + +#define STATIC_STATE 0 +#define BLINKINGON_STATE 1 +#define BLINKINGOFF_STATE 2 +#define POWERON_STATE 3 +#define POWEROFF_STATE 4 + +#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 + +/* Error messages */ +#define INTERLOCK_OPEN 0x00000002 +#define ADD_NOT_SUPPORTED 0x00000003 +#define CARD_FUNCTIONING 0x00000005 +#define ADAPTER_NOT_SAME 0x00000006 +#define NO_ADAPTER_PRESENT 0x00000009 +#define NOT_ENOUGH_RESOURCES 0x0000000B +#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C +#define WRONG_BUS_FREQUENCY 0x0000000D +#define POWER_FAILURE 0x0000000E + +#define REMOVE_NOT_SUPPORTED 0x00000003 + +#define DISABLE_CARD 1 + +/* + * error Messages + */ +#define msg_initialization_err "Initialization failure, error=%d\n" +#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" +#define msg_HPC_non_pcie "The PCI hot plug controller is not supported by this driver.\n" +#define msg_HPC_not_supported "This system is not supported by this version of pciephd mdoule. Upgrade to a newer version of pciehpd\n" +#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" +#define msg_button_on "PCI slot #%d - powering on due to button press.\n" +#define msg_button_off "PCI slot #%d - powering off due to button press.\n" +#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" +#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" + +/* controller functions */ +extern void pciehp_pushbutton_thread (unsigned long event_pointer); +extern int pciehprm_find_available_resources (struct controller *ctrl); +extern int pciehp_event_start_thread (void); +extern void pciehp_event_stop_thread (void); +extern struct pci_func *pciehp_slot_create (unsigned char busnumber); +extern struct pci_func *pciehp_slot_find (unsigned char bus, unsigned char device, unsigned char index); +extern int pciehp_enable_slot (struct slot *slot); +extern int pciehp_disable_slot (struct slot *slot); + +extern u8 pciehp_handle_attention_button (u8 hp_slot, void *inst_id); +extern u8 pciehp_handle_switch_change (u8 hp_slot, void *inst_id); +extern u8 pciehp_handle_presence_change (u8 hp_slot, void *inst_id); +extern u8 pciehp_handle_power_fault (u8 hp_slot, void *inst_id); +/* extern void long_delay (int delay); */ + +/* resource functions */ +extern int pciehp_resource_sort_and_combine (struct pci_resource **head); + +/* pci functions */ +extern int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); +extern int pciehp_save_config (struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num); +extern int pciehp_save_used_resources (struct controller *ctrl, struct pci_func * func, int flag); +extern int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot); +extern void pciehp_destroy_board_resources (struct pci_func * func); +extern int pciehp_return_board_resources (struct pci_func * func, struct resource_lists * resources); +extern void pciehp_destroy_resource_list (struct resource_lists * resources); +extern int pciehp_configure_device (struct controller* ctrl, struct pci_func* func); +extern int pciehp_unconfigure_device (struct pci_func* func); + + +/* Global variables */ +extern struct controller *pciehp_ctrl_list; +extern struct pci_func *pciehp_slot_list[256]; + +/* Inline functions */ + + +/* Inline functions to check the sanity of a pointer that is passed to us */ +static inline int slot_paranoia_check (struct slot *slot, const char *function) +{ + if (!slot) { + dbg("%s - slot == NULL", function); + return -1; + } + if (slot->magic != SLOT_MAGIC) { + dbg("%s - bad magic number for slot", function); + return -1; + } + if (!slot->hotplug_slot) { + dbg("%s - slot->hotplug_slot == NULL!", function); + return -1; + } + return 0; +} + +static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function) +{ + struct slot *slot; + + if (!hotplug_slot) { + dbg("%s - hotplug_slot == NULL\n", function); + return NULL; + } + + slot = (struct slot *)hotplug_slot->private; + if (slot_paranoia_check (slot, function)) + return NULL; + return slot; +} + +static inline struct slot *pciehp_find_slot (struct controller *ctrl, u8 device) +{ + struct slot *p_slot, *tmp_slot = NULL; + + if (!ctrl) + return NULL; + + p_slot = ctrl->slot; + + dbg("p_slot = %p\n", p_slot); + + while (p_slot && (p_slot->device != device)) { + tmp_slot = p_slot; + p_slot = p_slot->next; + dbg("In while loop, p_slot = %p\n", p_slot); + } + if (p_slot == NULL) { + err("ERROR: pciehp_find_slot device=0x%x\n", device); + p_slot = tmp_slot; + } + + return (p_slot); +} + +static inline int wait_for_ctrl_irq (struct controller *ctrl) +{ + int retval = 0; + + DECLARE_WAITQUEUE(wait, current); + + dbg("%s : start\n", __FUNCTION__); + add_wait_queue(&ctrl->queue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + if (!pciehp_poll_mode) { + /* Sleep for up to 1 second */ + schedule_timeout(1*HZ); + } else + schedule_timeout(2.5*HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctrl->queue, &wait); + if (signal_pending(current)) + retval = -EINTR; + + dbg("%s : end\n", __FUNCTION__); + + return retval; +} + + +/* Puts node back in the resource list pointed to by head */ +static inline void return_resource(struct pci_resource **head, struct pci_resource *node) +{ + if (!node || !head) + return; + node->next = *head; + *head = node; +} + +#define SLOT_NAME_SIZE 10 + +static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) +{ + snprintf(buffer, buffer_size, "%d", slot->number); +} + +enum php_ctlr_type { + PCI, + ISA, + ACPI +}; + +typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id); + +int pcie_init( struct controller *ctrl, struct pci_dev *pdev, + php_intr_callback_t attention_button_callback, + php_intr_callback_t switch_change_callback, + php_intr_callback_t presence_change_callback, + php_intr_callback_t power_fault_callback); + +/* This has no meaning for PCI Express, as there is only 1 slot per port */ +int pcie_get_ctlr_slot_config( struct controller *ctrl, + int *num_ctlr_slots, + int *first_device_num, + int *physical_slot_num, + int *updown, + int *flags); + +struct hpc_ops { + int (*power_on_slot ) (struct slot *slot); + int (*power_off_slot ) (struct slot *slot); + int (*get_power_status) (struct slot *slot, u8 *status); + int (*get_attention_status) (struct slot *slot, u8 *status); + int (*set_attention_status) (struct slot *slot, u8 status); + int (*get_latch_status) (struct slot *slot, u8 *status); + int (*get_adapter_status) (struct slot *slot, u8 *status); + + int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); + int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); + int (*get_max_lnk_width) (struct slot *slot, enum pcie_link_width *value); + int (*get_cur_lnk_width) (struct slot *slot, enum pcie_link_width *value); + + int (*query_power_fault) (struct slot *slot); + void (*green_led_on) (struct slot *slot); + void (*green_led_off) (struct slot *slot); + void (*green_led_blink) (struct slot *slot); + void (*release_ctlr) (struct controller *ctrl); + int (*check_lnk_status) (struct controller *ctrl); +}; + +#endif /* _PCIEHP_H */ diff -urN linux-2.4.26/drivers/hotplug/pciehp_core.c linux-2.4.27/drivers/hotplug/pciehp_core.c --- linux-2.4.26/drivers/hotplug/pciehp_core.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehp_core.c 2004-08-07 16:26:04.743350434 -0700 @@ -0,0 +1,703 @@ +/* + * PCI Express Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pciehp.h" +#include "pciehprm.h" + +/* Global variables */ +int pciehp_debug; +int pciehp_poll_mode; +int pciehp_poll_time; +struct controller *pciehp_ctrl_list; /* = NULL */ +struct pci_func *pciehp_slot_list[256]; + +#define DRIVER_VERSION "0.5" +#define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " +#define DRIVER_DESC "PCI Express Hot Plug Controller Driver" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +MODULE_PARM(pciehp_debug, "i"); +MODULE_PARM(pciehp_poll_mode, "i"); +MODULE_PARM(pciehp_poll_time, "i"); +MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not"); +MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); +MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds"); + +#define PCIE_MODULE_NAME "pciehp.o" + +static int pcie_start_thread (void); +static int set_attention_status (struct hotplug_slot *slot, u8 value); +static int enable_slot (struct hotplug_slot *slot); +static int disable_slot (struct hotplug_slot *slot); +static int hardware_test (struct hotplug_slot *slot, u32 value); +static int get_power_status (struct hotplug_slot *slot, u8 *value); +static int get_attention_status (struct hotplug_slot *slot, u8 *value); +static int get_latch_status (struct hotplug_slot *slot, u8 *value); +static int get_adapter_status (struct hotplug_slot *slot, u8 *value); +static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); +static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); + +static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { + .owner = THIS_MODULE, + .set_attention_status = set_attention_status, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .hardware_test = hardware_test, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, + .get_max_bus_speed = get_max_bus_speed, + .get_cur_bus_speed = get_cur_bus_speed, +}; + +static int init_slots(struct controller *ctrl) +{ + struct slot *new_slot; + u8 number_of_slots; + u8 slot_device; + u32 slot_number; + int result; + + dbg("%s\n",__FUNCTION__); + + number_of_slots = ctrl->num_slots; + slot_device = ctrl->slot_device_offset; + slot_number = ctrl->first_slot; + + while (number_of_slots) { + new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL); + if (!new_slot) + return -ENOMEM; + + memset(new_slot, 0, sizeof(struct slot)); + new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); + if (!new_slot->hotplug_slot) { + kfree (new_slot); + return -ENOMEM; + } + memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot)); + + new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!new_slot->hotplug_slot->info) { + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return -ENOMEM; + } + memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info)); + new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); + if (!new_slot->hotplug_slot->name) { + kfree (new_slot->hotplug_slot->info); + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return -ENOMEM; + } + + new_slot->magic = SLOT_MAGIC; + new_slot->ctrl = ctrl; + new_slot->bus = ctrl->slot_bus; + new_slot->device = slot_device; + new_slot->hpc_ops = ctrl->hpc_ops; + + new_slot->number = ctrl->first_slot; + new_slot->hp_slot = slot_device - ctrl->slot_device_offset; + + /* register this slot with the hotplug pci core */ + new_slot->hotplug_slot->private = new_slot; + make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot); + new_slot->hotplug_slot->ops = &pciehp_hotplug_slot_ops; + + new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status)); + new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status)); + new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status)); + new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status)); + + dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", + new_slot->bus, new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset); + result = pci_hp_register (new_slot->hotplug_slot); + if (result) { + err ("pci_hp_register failed with error %d\n", result); + kfree (new_slot->hotplug_slot->info); + kfree (new_slot->hotplug_slot->name); + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return result; + } + + new_slot->next = ctrl->slot; + ctrl->slot = new_slot; + + number_of_slots--; + slot_device++; + slot_number += ctrl->slot_num_inc; + } + + return(0); +} + + +static int cleanup_slots (struct controller * ctrl) +{ + struct slot *old_slot, *next_slot; + + old_slot = ctrl->slot; + ctrl->slot = NULL; + + while (old_slot) { + next_slot = old_slot->next; + pci_hp_deregister (old_slot->hotplug_slot); + kfree(old_slot->hotplug_slot->info); + kfree(old_slot->hotplug_slot->name); + kfree(old_slot->hotplug_slot); + kfree(old_slot); + old_slot = next_slot; + } + + + return(0); +} + +static int get_ctlr_slot_config(struct controller *ctrl) +{ + int num_ctlr_slots; /* Not needed; PCI Express has 1 slot per port */ + int first_device_num; /* Not needed */ + int physical_slot_num; + int updown; /* Not needed */ + int rc; + int flags; /* Not needed */ + + rc = pcie_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &updown, &flags); + if (rc) { + err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device); + return (-1); + } + + ctrl->num_slots = num_ctlr_slots; /* PCI Express has 1 slot per port */ + ctrl->slot_device_offset = first_device_num; + ctrl->first_slot = physical_slot_num; + ctrl->slot_num_inc = updown; /* Not needed */ /* either -1 or 1 */ + + dbg("%s: bus(0x%x) num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n", + __FUNCTION__, ctrl->slot_bus, num_ctlr_slots, first_device_num, physical_slot_num, updown, + ctrl->bus, ctrl->device); + + return (0); +} + + +/* + * set_attention_status - Turns the Amber LED for a slot on, off or blink + */ +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + hotplug_slot->info->attention_status = status; + slot->hpc_ops->set_attention_status(slot, status); + + + return 0; +} + + +static int enable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + return pciehp_enable_slot(slot); +} + + +static int disable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + return pciehp_disable_slot(slot); +} + + +static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) +{ + return 0; +} + + +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_power_status(slot, value); + if (retval < 0) + *value = hotplug_slot->info->power_status; + + return 0; +} + +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_attention_status(slot, value); + if (retval < 0) + *value = hotplug_slot->info->attention_status; + + return 0; +} + +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_latch_status(slot, value); + if (retval < 0) + *value = hotplug_slot->info->latch_status; + + return 0; +} + +static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_adapter_status(slot, value); + + if (retval < 0) + *value = hotplug_slot->info->adapter_status; + + return 0; +} + +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_max_bus_speed(slot, value); + if (retval < 0) + *value = PCI_SPEED_UNKNOWN; + + return 0; +} + +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_cur_bus_speed(slot, value); + if (retval < 0) + *value = PCI_SPEED_UNKNOWN; + + return 0; +} + +static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int rc; + struct controller *ctrl; + struct slot *t_slot; + int first_device_num = 0; /* first PCI device number supported by this PCIE */ + int num_ctlr_slots; /* number of slots supported by this HPC */ + u8 value; + + ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL); + if (!ctrl) { + err("%s : out of memory\n", __FUNCTION__); + goto err_out_none; + } + memset(ctrl, 0, sizeof(struct controller)); + + dbg("%s: DRV_thread pid = %d\n", __FUNCTION__, current->pid); + + rc = pcie_init(ctrl, pdev, + (php_intr_callback_t) pciehp_handle_attention_button, + (php_intr_callback_t) pciehp_handle_switch_change, + (php_intr_callback_t) pciehp_handle_presence_change, + (php_intr_callback_t) pciehp_handle_power_fault); + if (rc) { + dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME); + goto err_out_free_ctrl; + } + + ctrl->pci_dev = pdev; + + ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL); + if (!ctrl->pci_bus) { + err("%s: out of memory\n", __FUNCTION__); + rc = -ENOMEM; + goto err_out_unmap_mmio_region; + } + dbg("%s: ctrl->pci_bus %p\n", __FUNCTION__, ctrl->pci_bus); + memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus)); + + ctrl->bus = pdev->bus->number; /* ctrl bus */ + ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */ + + ctrl->device = PCI_SLOT(pdev->devfn); + ctrl->function = PCI_FUNC(pdev->devfn); + dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__, + ctrl->bus, ctrl->device, ctrl->function, pdev->irq); + + /* + * Save configuration headers for this and subordinate PCI buses + */ + + rc = get_ctlr_slot_config(ctrl); + if (rc) { + err(msg_initialization_err, rc); + goto err_out_free_ctrl_bus; + } + + first_device_num = ctrl->slot_device_offset; + num_ctlr_slots = ctrl->num_slots; + + + /* Store PCI Config Space for all devices on this bus */ + dbg("%s: Before calling pciehp_save_config, ctrl->bus %x,ctrl->slot_bus %x\n", + __FUNCTION__,ctrl->bus, ctrl->slot_bus); + rc = pciehp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num); + if (rc) { + err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); + goto err_out_free_ctrl_bus; + } + + /* Get IO, memory, and IRQ resources for new devices */ + rc = pciehprm_find_available_resources(ctrl); + + ctrl->add_support = !rc; + if (rc) { + dbg("pciehprm_find_available_resources = %#x\n", rc); + err("unable to locate PCI configuration resources for hot plug add.\n"); + goto err_out_free_ctrl_bus; + } + /* Setup the slot information structures */ + rc = init_slots(ctrl); + if (rc) { + err(msg_initialization_err, 6); + goto err_out_free_ctrl_slot; + } + + t_slot = pciehp_find_slot(ctrl, first_device_num); + dbg("%s: t_slot %p\n", __FUNCTION__, t_slot); + + /* Finish setting up the hot plug ctrl device */ + ctrl->next_event = 0; + + if (!pciehp_ctrl_list) { + pciehp_ctrl_list = ctrl; + ctrl->next = NULL; + } else { + ctrl->next = pciehp_ctrl_list; + pciehp_ctrl_list = ctrl; + } + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */ + dbg("%s: adapter value %x\n", __FUNCTION__, value); + if (!value) { + rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/ + if (rc) { + up(&ctrl->crit_sect); + goto err_out_free_ctrl_slot; + } else + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + } + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + return 0; + +err_out_free_ctrl_slot: + cleanup_slots(ctrl); +err_out_free_ctrl_bus: + kfree(ctrl->pci_bus); +err_out_unmap_mmio_region: + ctrl->hpc_ops->release_ctlr(ctrl); +err_out_free_ctrl: + kfree(ctrl); +err_out_none: + return -ENODEV; +} + + +static int pcie_start_thread(void) +{ + int loop; + int retval = 0; + + dbg("Initialize + Start the notification/polling mechanism \n"); + + retval = pciehp_event_start_thread(); + if (retval) { + dbg("pciehp_event_start_thread() failed\n"); + return retval; + } + + dbg("Initialize slot lists\n"); + /* One slot list for each bus in the system */ + for (loop = 0; loop < 256; loop++) { + pciehp_slot_list[loop] = NULL; + } + + return retval; +} + + +static void unload_pciehpd(void) +{ + struct pci_func *next; + struct pci_func *TempSlot; + int loop; + struct controller *ctrl; + struct controller *tctrl; + struct pci_resource *res; + struct pci_resource *tres; + + ctrl = pciehp_ctrl_list; + while (ctrl) { + cleanup_slots(ctrl); + + res = ctrl->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + kfree (ctrl->pci_bus); + + ctrl->hpc_ops->release_ctlr(ctrl); + + tctrl = ctrl; + ctrl = ctrl->next; + + kfree(tctrl); + } + + for (loop = 0; loop < 256; loop++) { + next = pciehp_slot_list[loop]; + while (next != NULL) { + res = next->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + TempSlot = next; + next = next->next; + kfree(TempSlot); + } + } + + /* Stop the notification mechanism */ + pciehp_event_stop_thread(); + +} + + +static struct pci_device_id pcied_pci_tbl[] __devinitdata = { + { + class: ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), + class_mask: ~0, + vendor: PCI_ANY_ID, + device: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + }, + { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, pcied_pci_tbl); + + + +static struct pci_driver pcie_driver = { + .name = PCIE_MODULE_NAME, + .id_table = pcied_pci_tbl, + .probe = pcie_probe, + /* remove: pcie_remove_one, */ +}; + + + +static int __init pcied_init(void) +{ + int retval = 0; + +#ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE + pciehp_poll_mode = 1; +#endif + retval = pcie_start_thread(); + if (retval) + goto error_hpc_init; + + retval = pciehprm_init(PCI); + if (!retval) { + retval = pci_module_init(&pcie_driver); + dbg("pci_module_init = %d\n", retval); + info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + } + +error_hpc_init: + if (retval) { + pciehprm_cleanup(); + pciehp_event_stop_thread(); + } + + return retval; +} + +static void __exit pcied_cleanup(void) +{ + dbg("unload_pciehpd()\n"); + unload_pciehpd(); + + pciehprm_cleanup(); + + dbg("pci_unregister_driver\n"); + pci_unregister_driver(&pcie_driver); + + info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); +} + + +module_init(pcied_init); +module_exit(pcied_cleanup); + + diff -urN linux-2.4.26/drivers/hotplug/pciehp_ctrl.c linux-2.4.27/drivers/hotplug/pciehp_ctrl.c --- linux-2.4.26/drivers/hotplug/pciehp_ctrl.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehp_ctrl.c 2004-08-07 16:26:04.747350599 -0700 @@ -0,0 +1,2629 @@ +/* + * PCI Express Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pciehp.h" +#include "pciehprm.h" + +static u32 configure_new_device(struct controller *ctrl, struct pci_func *func, + u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); +static int configure_new_function( struct controller *ctrl, struct pci_func *func, + u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); +static void interrupt_event_handler(struct controller *ctrl); + +static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ +static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ +static int event_finished; +static unsigned long pushbutton_pending; /* = 0 */ + +u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + u8 getstatus; + struct pci_func *func; + struct event_info *taskInfo; + + /* Attention Button Change */ + dbg("pciehp: Attention button interrupt received.\n"); + + func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread what to do */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + + /* + * Button pressed - See if need to TAKE ACTION!!! + */ + info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_BUTTON_PRESS; + + if ((p_slot->state == BLINKINGON_STATE) + || (p_slot->state == BLINKINGOFF_STATE)) { + /* Cancel if we are still blinking; this means that we press the + * attention again before the 5 sec. limit expires to cancel hot-add + * or hot-remove + */ + taskInfo->event_type = INT_BUTTON_CANCEL; + info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); + } else if ((p_slot->state == POWERON_STATE) + || (p_slot->state == POWEROFF_STATE)) { + /* Ignore if the slot is on power-on or power-off state; this + * means that the previous attention button action to hot-add or + * hot-remove is undergoing + */ + taskInfo->event_type = INT_BUTTON_IGNORE; + info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); + } + if (rc) + up(&event_semaphore); /* signal event thread that new event is posted */ + + return 0; + +} + +u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + u8 getstatus; + struct pci_func *func; + struct event_info *taskInfo; + + /* Switch Change */ + dbg("pciehp: Switch interrupt received.\n"); + + func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* this is the structure that tells the worker thread + * what to do + */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + + if (getstatus) { + /* + * Switch opened + */ + info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); + func->switch_save = 0; + taskInfo->event_type = INT_SWITCH_OPEN; + } else { + /* + * Switch closed + */ + info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); + func->switch_save = 0x10; + taskInfo->event_type = INT_SWITCH_CLOSE; + } + if (rc) + up(&event_semaphore); /* signal event thread that new event is posted */ + + return rc; +} + +u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + struct pci_func *func; + struct event_info *taskInfo; + + /* Presence Change */ + dbg("pciehp: Presence/Notify input change.\n"); + + func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread + * what to do + */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + /* Switch is open, assume a presence change + * Save the presence state + */ + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + if (func->presence_save) { + /* + * Card Present + */ + info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_PRESENCE_ON; + } else { + /* + * Not Present + */ + info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_PRESENCE_OFF; + } + if (rc) + up(&event_semaphore); /* signal event thread that new event is posted */ + + return rc; +} + +u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + struct pci_func *func; + struct event_info *taskInfo; + + /* power fault */ + dbg("pciehp: Power fault interrupt received.\n"); + + func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread + * what to do + */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { + /* + * Power fault cleared + */ + info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); + func->status = 0x00; + taskInfo->event_type = INT_POWER_FAULT_CLEAR; + } else { + /* + * Power fault + */ + info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_POWER_FAULT; + /* Set power fault status for this board */ + func->status = 0xFF; + info("power fault bit %x set\n", hp_slot); + } + if (rc) + up(&event_semaphore); /* Signal event thread that new event is posted */ + + return rc; +} + + +/* + * sort_by_size + * + * Sorts nodes on the list by their length. + * Smallest first. + * + */ +static int sort_by_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return(1); + + if (!((*head)->next)) + return(0); + + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->length > (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length > current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } /* End of out_of_order loop */ + + return(0); +} + + +/* + * sort_by_max_size + * + * Sorts nodes on the list by their length. + * Largest first. + * + */ +static int sort_by_max_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return(1); + + if (!((*head)->next)) + return(0); + + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->length < (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length < current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } /* End of out_of_order loop */ + + return(0); +} + + +/* + * do_pre_bridge_resource_split + * + * Returns zero or one node of resources that aren't in use + * + */ +static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment) +{ + struct pci_resource *prevnode = NULL; + struct pci_resource *node; + struct pci_resource *split_node; + u32 rc; + u32 temp_dword; + dbg("do_pre_bridge_resource_split\n"); + + if (!(*head) || !(*orig_head)) + return(NULL); + + rc = pciehp_resource_sort_and_combine(head); + + if (rc) + return(NULL); + + if ((*head)->base != (*orig_head)->base) + return(NULL); + + if ((*head)->length == (*orig_head)->length) + return(NULL); + + + /* If we got here, there the bridge requires some of the resource, but + * we may be able to split some off of the front + */ + node = *head; + + if (node->length & (alignment -1)) { + /* This one isn't an aligned length, so we'll make a new entry + * and split it up. + */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + temp_dword = (node->length | (alignment-1)) + 1 - alignment; + + split_node->base = node->base; + split_node->length = temp_dword; + + node->length -= temp_dword; + node->base += split_node->length; + + /* Put it in the list */ + *head = split_node; + split_node->next = node; + } + + if (node->length < alignment) { + return(NULL); + } + + /* Now unlink it */ + if (*head == node) { + *head = node->next; + node->next = NULL; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + node->next = NULL; + } + + return(node); +} + + +/* + * do_bridge_resource_split + * + * Returns zero or one node of resources that aren't in use + * + */ +static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment) +{ + struct pci_resource *prevnode = NULL; + struct pci_resource *node; + u32 rc; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + rc = pciehp_resource_sort_and_combine(head); + + if (rc) + return(NULL); + + node = *head; + + while (node->next) { + prevnode = node; + node = node->next; + kfree(prevnode); + } + + if (node->length < alignment) { + kfree(node); + return(NULL); + } + + if (node->base & (alignment - 1)) { + /* Short circuit if adjusted size is too small */ + temp_dword = (node->base | (alignment-1)) + 1; + if ((node->length - (temp_dword - node->base)) < alignment) { + kfree(node); + return(NULL); + } + + node->length -= (temp_dword - node->base); + node->base = temp_dword; + } + + if (node->length & (alignment - 1)) { + /* There's stuff in use after this node */ + kfree(node); + return(NULL); + } + + return(node); +} + + +/* + * get_io_resource + * + * this function sorts the resource list by size and then + * returns the first node of "size" length that is not in the + * ISA aliasing window. If it finds a node larger than "size" + * it will split it up. + * + * size must be a power of two. + */ +static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node = NULL; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if ( pciehp_resource_sort_and_combine(head) ) + return(NULL); + + if ( sort_by_size(head) ) + return(NULL); + + for (node = *head; node; node = node->next) { + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + /* This one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_dword = (node->base | (size-1)) + 1; + + /*/ Short circuit if adjusted size is too small */ + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base; + split_node->length = temp_dword - node->base; + node->base = temp_dword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of non-aligned base */ + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + /* This one is longer than we need + so we'll make a new entry and split it up */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base + size; + split_node->length = node->length - size; + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + /* For IO make sure it's not in the ISA aliasing space */ + if (node->base & 0x300L) + continue; + + /* If we got here, then it is the right size + Now take it out of the list */ + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + /* Stop looping */ + break; + } + + return(node); +} + + +/* + * get_max_resource + * + * Gets the largest node that is at least "size" big from the + * list pointed to by head. It aligns the node on top and bottom + * to "size" alignment before returning it. + * J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M + * This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot. + */ +static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *max; + struct pci_resource *temp; + struct pci_resource *split_node; + u32 temp_dword; + u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 }; + int i; + + if (!(*head)) + return(NULL); + + if (pciehp_resource_sort_and_combine(head)) + return(NULL); + + if (sort_by_max_size(head)) + return(NULL); + + for (max = *head;max; max = max->next) { + + /* If not big enough we could probably just bail, + instead we'll continue to the next. */ + if (max->length < size) + continue; + + if (max->base & (size - 1)) { + /* This one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_dword = (max->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((max->length - (temp_dword - max->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = max->base; + split_node->length = temp_dword - max->base; + max->base = temp_dword; + max->length -= split_node->length; + + /* Put it next in the list */ + split_node->next = max->next; + max->next = split_node; + } + + if ((max->base + max->length) & (size - 1)) { + /* This one isn't end aligned properly at the top + so we'll make a new entry and split it up */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + temp_dword = ((max->base + max->length) & ~(size - 1)); + split_node->base = temp_dword; + split_node->length = max->length + max->base + - split_node->base; + max->length -= split_node->length; + + /* Put it in the list */ + split_node->next = max->next; + max->next = split_node; + } + + /* Make sure it didn't shrink too much when we aligned it */ + if (max->length < size) + continue; + + for ( i = 0; max_size[i] > size; i++) { + if (max->length > max_size[i]) { + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + break; /* return (NULL); */ + split_node->base = max->base + max_size[i]; + split_node->length = max->length - max_size[i]; + max->length = max_size[i]; + /* Put it next in the list */ + split_node->next = max->next; + max->next = split_node; + break; + } + } + + /* Now take it out of the list */ + temp = (struct pci_resource*) *head; + if (temp == max) { + *head = max->next; + } else { + while (temp && temp->next != max) { + temp = temp->next; + } + + temp->next = max->next; + } + + max->next = NULL; + return(max); + } + + /* If we get here, we couldn't find one */ + return(NULL); +} + + +/* + * get_resource + * + * this function sorts the resource list by size and then + * returns the first node of "size" length. If it finds a node + * larger than "size" it will split it up. + * + * size must be a power of two. + */ +static struct pci_resource *get_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if ( pciehp_resource_sort_and_combine(head) ) + return(NULL); + + if ( sort_by_size(head) ) + return(NULL); + + for (node = *head; node; node = node->next) { + dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n", + __FUNCTION__, size, node, node->base, node->length); + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + dbg("%s: not aligned\n", __FUNCTION__); + /* This one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_dword = (node->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base; + split_node->length = temp_dword - node->base; + node->base = temp_dword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of non-aligned base */ + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + dbg("%s: too big\n", __FUNCTION__); + /* This one is longer than we need + so we'll make a new entry and split it up */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base + size; + split_node->length = node->length - size; + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + dbg("%s: got one!!!\n", __FUNCTION__); + /* If we got here, then it is the right size + Now take it out of the list */ + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + /* Stop looping */ + break; + } + return(node); +} + + +/* + * pciehp_resource_sort_and_combine + * + * Sorts all of the nodes in the list in ascending order by + * their base addresses. Also does garbage collection by + * combining adjacent nodes. + * + * returns 0 if success + */ +int pciehp_resource_sort_and_combine(struct pci_resource **head) +{ + struct pci_resource *node1; + struct pci_resource *node2; + int out_of_order = 1; + + dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); + + if (!(*head)) + return(1); + + dbg("*head->next = %p\n",(*head)->next); + + if (!(*head)->next) + return(0); /* Only one item on the list, already sorted! */ + + dbg("*head->base = 0x%x\n",(*head)->base); + dbg("*head->next->base = 0x%x\n",(*head)->next->base); + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->base > (*head)->next->base)) { + node1 = *head; + (*head) = (*head)->next; + node1->next = (*head)->next; + (*head)->next = node1; + out_of_order++; + } + + node1 = (*head); + + while (node1->next && node1->next->next) { + if (node1->next->base > node1->next->next->base) { + out_of_order++; + node2 = node1->next; + node1->next = node1->next->next; + node1 = node1->next; + node2->next = node1->next; + node1->next = node2; + } else + node1 = node1->next; + } + } /* End of out_of_order loop */ + + node1 = *head; + + while (node1 && node1->next) { + if ((node1->base + node1->length) == node1->next->base) { + /* Combine */ + dbg("8..\n"); + node1->length += node1->next->length; + node2 = node1->next; + node1->next = node1->next->next; + kfree(node2); + } else + node1 = node1->next; + } + + return(0); +} + + +/** + * pciehp_slot_create - Creates a node and adds it to the proper bus. + * @busnumber - bus where new node is to be located + * + * Returns pointer to the new node or NULL if unsuccessful + */ +struct pci_func *pciehp_slot_create(u8 busnumber) +{ + struct pci_func *new_slot; + struct pci_func *next; + dbg("%s: busnumber %x\n", __FUNCTION__, busnumber); + new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL); + + if (new_slot == NULL) { + return(new_slot); + } + + memset(new_slot, 0, sizeof(struct pci_func)); + + new_slot->next = NULL; + new_slot->configured = 1; + + if (pciehp_slot_list[busnumber] == NULL) { + pciehp_slot_list[busnumber] = new_slot; + } else { + next = pciehp_slot_list[busnumber]; + while (next->next != NULL) + next = next->next; + next->next = new_slot; + } + return(new_slot); +} + + +/* + * slot_remove - Removes a node from the linked list of slots. + * @old_slot: slot to remove + * + * Returns 0 if successful, !0 otherwise. + */ +static int slot_remove(struct pci_func * old_slot) +{ + struct pci_func *next; + + if (old_slot == NULL) + return(1); + + next = pciehp_slot_list[old_slot->bus]; + + if (next == NULL) { + return(1); + } + + if (next == old_slot) { + pciehp_slot_list[old_slot->bus] = old_slot->next; + pciehp_destroy_board_resources(old_slot); + kfree(old_slot); + return(0); + } + + while ((next->next != old_slot) && (next->next != NULL)) { + next = next->next; + } + + if (next->next == old_slot) { + next->next = old_slot->next; + pciehp_destroy_board_resources(old_slot); + kfree(old_slot); + return(0); + } else + return(2); +} + + +/** + * bridge_slot_remove - Removes a node from the linked list of slots. + * @bridge: bridge to remove + * + * Returns 0 if successful, !0 otherwise. + */ +static int bridge_slot_remove(struct pci_func *bridge) +{ + u8 subordinateBus, secondaryBus; + u8 tempBus; + struct pci_func *next; + + if (bridge == NULL) + return(1); + + secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; + subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; + + for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { + next = pciehp_slot_list[tempBus]; + + while (!slot_remove(next)) { + next = pciehp_slot_list[tempBus]; + } + } + + next = pciehp_slot_list[bridge->bus]; + + if (next == NULL) { + return(1); + } + + if (next == bridge) { + pciehp_slot_list[bridge->bus] = bridge->next; + kfree(bridge); + return(0); + } + + while ((next->next != bridge) && (next->next != NULL)) { + next = next->next; + } + + if (next->next == bridge) { + next->next = bridge->next; + kfree(bridge); + return(0); + } else + return(2); +} + + +/** + * pciehp_slot_find - Looks for a node by bus, and device, multiple functions accessed + * @bus: bus to find + * @device: device to find + * @index: is 0 for first function found, 1 for the second... + * + * Returns pointer to the node if successful, %NULL otherwise. + */ +struct pci_func *pciehp_slot_find(u8 bus, u8 device, u8 index) +{ + int found = -1; + struct pci_func *func; + + func = pciehp_slot_list[bus]; + dbg("%s: bus %x device %x index %x\n", + __FUNCTION__, bus, device, index); + if (func != NULL) { + dbg("%s: func-> bus %x device %x function %x pci_dev %p\n", + __FUNCTION__, func->bus, func->device, func->function, + func->pci_dev); + } else + dbg("%s: func == NULL\n", __FUNCTION__); + + if ((func == NULL) || ((func->device == device) && (index == 0))) + return(func); + + if (func->device == device) + found++; + + while (func->next != NULL) { + func = func->next; + + dbg("%s: In while loop, func-> bus %x device %x function %x pci_dev %p\n", + __FUNCTION__, func->bus, func->device, func->function, + func->pci_dev); + if (func->device == device) + found++; + dbg("%s: while loop, found %d, index %d\n", __FUNCTION__, + found, index); + + if ((found == index) ||(func->function == index)) { + dbg("%s: Found bus %x dev %x func %x\n", __FUNCTION__, + func->bus, func->device, func->function); + return(func); + } + } + + return(NULL); +} + +static int is_bridge(struct pci_func * func) +{ + /* Check the header type */ + if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) + return 1; + else + return 0; +} + + +/* the following routines constitute the bulk of the + hotplug controller logic + */ + + +/** + * board_added - Called after a board has been added to the system. + * + * Turns power on for the board + * Configures board + * + */ +static u32 board_added(struct pci_func * func, struct controller * ctrl) +{ + u8 hp_slot; + int index; + u32 temp_register = 0xFFFFFFFF; + u32 retval, rc = 0; + struct pci_func *new_func = NULL; + struct slot *p_slot; + struct resource_lists res_lists; + + p_slot = pciehp_find_slot(ctrl, func->device); + hp_slot = func->device - ctrl->slot_device_offset; + + dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", + __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot); + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Power on slot */ + rc = p_slot->hpc_ops->power_on_slot(p_slot); + if (rc) + return -1; + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + dbg("%s: after power on\n", __FUNCTION__); + + p_slot->hpc_ops->green_led_blink(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + dbg("%s: after green_led_blink", __FUNCTION__); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + /* Wait for ~1 second */ + dbg("%s: before long_delay\n", __FUNCTION__); + wait_for_ctrl_irq (ctrl); + dbg("%s: afterlong_delay\n", __FUNCTION__); + + dbg("%s: before check link status", __FUNCTION__); + /* Make this to check for link training status */ + rc = p_slot->hpc_ops->check_lnk_status(ctrl); + if (rc) { + err("%s: Failed to check link status\n", __FUNCTION__); + return -1; + } + + dbg("%s: func status = %x\n", __FUNCTION__, func->status); + + /* Check for a power fault */ + if (func->status == 0xFF) { + /* power fault occurred, but it was benign */ + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by power fault\n", + __FUNCTION__, temp_register); + rc = POWER_FAILURE; + func->status = 0; + } else { + /* Get vendor/device ID u32 */ + rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, + PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); + dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc); + dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); + + if (rc != 0) { + /* Something's wrong here */ + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by error\n", __FUNCTION__, + temp_register); + } + /* Preset return code. It will be changed later if things go okay. */ + rc = NO_ADAPTER_PRESENT; + } + + /* All F's is an empty slot or an invalid board */ + if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */ + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + res_lists.irqs = NULL; + + rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0); + dbg("%s: back from configure_new_device\n", __FUNCTION__); + + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + pciehp_resource_sort_and_combine(&(ctrl->mem_head)); + pciehp_resource_sort_and_combine(&(ctrl->p_mem_head)); + pciehp_resource_sort_and_combine(&(ctrl->io_head)); + pciehp_resource_sort_and_combine(&(ctrl->bus_head)); + + if (rc) { + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Turn off slot, turn on Amber LED, turn off Green LED */ + retval = p_slot->hpc_ops->power_off_slot(p_slot); + /* In PCI Express, just power off slot */ + if (retval) { + err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__); + return retval; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->green_led_off(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Turn on Amber LED */ + retval = p_slot->hpc_ops->set_attention_status(p_slot, 1); + if (retval) { + err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__); + return retval; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + return(rc); + } + pciehp_save_slot_config(ctrl, func); + + func->status = 0; + func->switch_save = 0x10; + func->is_a_board = 0x01; + + /* Next, we will instantiate the linux pci_dev structures + * (with appropriate driver notification, if already present) + */ + index = 0; + do { + new_func = pciehp_slot_find(ctrl->slot_bus, func->device, index++); + if (new_func && !new_func->pci_dev) { + dbg("%s:call pci_hp_configure_dev, func %x\n", + __FUNCTION__, index); + pciehp_configure_device(ctrl, new_func); + } + } while (new_func); + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->green_led_on(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + } else { + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Turn off slot, turn on Amber LED, turn off Green LED */ + retval = p_slot->hpc_ops->power_off_slot(p_slot); + /* In PCI Express, just power off slot */ + if (retval) { + err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__); + return retval; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->green_led_off(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Turn on Amber LED */ + retval = p_slot->hpc_ops->set_attention_status(p_slot, 1); + if (retval) { + err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__); + return retval; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + return(rc); + } + return 0; +} + + +/** + * remove_board - Turns off slot and LED's + * + */ +static u32 remove_board(struct pci_func *func, struct controller *ctrl) +{ + int index; + u8 skip = 0; + u8 device; + u8 hp_slot; + u32 rc; + struct resource_lists res_lists; + struct pci_func *temp_func; + struct slot *p_slot; + + if (func == NULL) + return(1); + + if (pciehp_unconfigure_device(func)) + return(1); + + device = func->device; + + hp_slot = func->device - ctrl->slot_device_offset; + p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); + + if ((ctrl->add_support) && + !(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) { + /* Here we check to see if we've saved any of the board's + * resources already. If so, we'll skip the attempt to + * determine what's being used. + */ + index = 0; + + temp_func = func; + + while ((temp_func = pciehp_slot_find(temp_func->bus, temp_func->device, + index++))) { + if (temp_func->bus_head || temp_func->mem_head + || temp_func->p_mem_head || temp_func->io_head) { + skip = 1; + break; + } + } + + if (!skip) + rc = pciehp_save_used_resources(ctrl, func, DISABLE_CARD); + } + /* Change status to shutdown */ + if (func->is_a_board) + func->status = 0x01; + func->configured = 0; + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Power off slot */ + rc = p_slot->hpc_ops->power_off_slot(p_slot); + if (rc) { + err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); + return rc; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Turn off Green LED */ + p_slot->hpc_ops->green_led_off(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + if (ctrl->add_support) { + while (func) { + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + + dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n", + func->bus, func->device, func->function); + + pciehp_return_board_resources(func, &res_lists); + + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + pciehp_resource_sort_and_combine(&(ctrl->mem_head)); + pciehp_resource_sort_and_combine(&(ctrl->p_mem_head)); + pciehp_resource_sort_and_combine(&(ctrl->io_head)); + pciehp_resource_sort_and_combine(&(ctrl->bus_head)); + + if (is_bridge(func)) { + dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", + ctrl->seg, func->bus, func->device, func->function); + bridge_slot_remove(func); + } else + dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", + ctrl->seg, func->bus, func->device, func->function); + slot_remove(func); + + func = pciehp_slot_find(ctrl->slot_bus, device, 0); + } + + /* Setup slot structure with entry for empty slot */ + func = pciehp_slot_create(ctrl->slot_bus); + + if (func == NULL) { + return(1); + } + + func->bus = ctrl->slot_bus; + func->device = device; + func->function = 0; + func->configured = 0; + func->switch_save = 0x10; + func->is_a_board = 0; + } + return 0; +} + + +static void pushbutton_helper_thread (unsigned long data) +{ + pushbutton_pending = data; + up(&event_semaphore); +} + + +/* this is the main worker thread */ +static int event_thread(void* data) +{ + struct controller *ctrl; + lock_kernel(); + daemonize(); + + /* New name */ + strcpy(current->comm, "pciehpd_event"); + + unlock_kernel(); + + while (1) { + dbg("!!!!event_thread sleeping\n"); + down_interruptible (&event_semaphore); + dbg("event_thread woken finished = %d\n", event_finished); + if (event_finished || signal_pending(current)) + break; + /* Do stuff here */ + if (pushbutton_pending) + pciehp_pushbutton_thread(pushbutton_pending); + else + for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next) + interrupt_event_handler(ctrl); + } + dbg("event_thread signals exit\n"); + up(&event_exit); + return 0; +} + +int pciehp_event_start_thread (void) +{ + int pid; + + /* Initialize our semaphores */ + init_MUTEX_LOCKED(&event_exit); + event_finished=0; + + init_MUTEX_LOCKED(&event_semaphore); + pid = kernel_thread(event_thread, 0, 0); + + if (pid < 0) { + err ("Can't start up our event thread\n"); + return -1; + } + dbg("Our event thread pid = %d\n", pid); + return 0; +} + + +void pciehp_event_stop_thread (void) +{ + event_finished = 1; + dbg("event_thread finish command given\n"); + up(&event_semaphore); + dbg("wait for event_thread to exit\n"); + down(&event_exit); +} + + +static int update_slot_info (struct slot *slot) +{ + struct hotplug_slot_info *info; + char buffer[SLOT_NAME_SIZE]; + int result; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); + + slot->hpc_ops->get_power_status(slot, &(info->power_status)); + slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); + slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); + slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); + + result = pci_hp_change_slot_info(buffer, info); + kfree (info); + return result; +} + +static void interrupt_event_handler(struct controller *ctrl) +{ + int loop = 0; + int change = 1; + struct pci_func *func; + u8 hp_slot; + u8 getstatus; + struct slot *p_slot; + + while (change) { + change = 0; + + for (loop = 0; loop < 10; loop++) { + if (ctrl->event_queue[loop].event_type != 0) { + hp_slot = ctrl->event_queue[loop].hp_slot; + + func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + dbg("hp_slot %d, func %p, p_slot %p\n", hp_slot, func, p_slot); + + if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { + dbg("button cancel\n"); + del_timer(&p_slot->task_event); + + switch (p_slot->state) { + case BLINKINGOFF_STATE: + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->green_led_on(p_slot); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->set_attention_status(p_slot, 0); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + break; + case BLINKINGON_STATE: + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->green_led_off(p_slot); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->set_attention_status(p_slot, 0); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + break; + default: + warn("Not a valid state\n"); + return; + } + info(msg_button_cancel, p_slot->number); + p_slot->state = STATIC_STATE; + } + /* ***********Button Pressed (No action on 1st press...) */ + else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { + dbg("Button pressed\n"); + + p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (getstatus) { + /* Slot is on */ + dbg("Slot is on\n"); + p_slot->state = BLINKINGOFF_STATE; + info(msg_button_off, p_slot->number); + } else { + /* Slot is off */ + dbg("Slot is off\n"); + p_slot->state = BLINKINGON_STATE; + info(msg_button_on, p_slot->number); + } + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* blink green LED and turn off amber */ + p_slot->hpc_ops->green_led_blink(p_slot); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->set_attention_status(p_slot, 0); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + init_timer(&p_slot->task_event); + p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ + p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; + p_slot->task_event.data = (unsigned long) p_slot; + + dbg("add_timer p_slot = %p\n", (void *) p_slot); + add_timer(&p_slot->task_event); + } + /***********POWER FAULT********************/ + else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { + dbg("power fault\n"); + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->set_attention_status(p_slot, 1); + p_slot->hpc_ops->green_led_off(p_slot); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } else { + /* refresh notification */ + if (p_slot) + update_slot_info(p_slot); + } + + ctrl->event_queue[loop].event_type = 0; + + change = 1; + } + } /* End of FOR loop */ + } + + return; +} + + +/** + * pciehp_pushbutton_thread + * + * Scheduled procedure to handle blocking stuff for the pushbuttons + * Handles all pending events and exits. + * + */ +void pciehp_pushbutton_thread (unsigned long slot) +{ + struct slot *p_slot = (struct slot *) slot; + u8 getstatus; + int rc; + + pushbutton_pending = 0; + + if (!p_slot) { + dbg("%s: Error! slot NULL\n", __FUNCTION__); + return; + } + + p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (getstatus) { + p_slot->state = POWEROFF_STATE; + dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); + + if (pciehp_disable_slot(p_slot)) { + /* Wait for exclusive access to hardware */ + down(&p_slot->ctrl->crit_sect); + + /* Turn on the Attention LED */ + rc = p_slot->hpc_ops->set_attention_status(p_slot, 1); + if (rc) { + err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__); + return; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (p_slot->ctrl); + + /* Done with exclusive hardware access */ + up(&p_slot->ctrl->crit_sect); + } + p_slot->state = STATIC_STATE; + } else { + p_slot->state = POWERON_STATE; + dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); + + if (pciehp_enable_slot(p_slot)) { + /* Wait for exclusive access to hardware */ + down(&p_slot->ctrl->crit_sect); + + /* Turn off the green LED */ + rc = p_slot->hpc_ops->set_attention_status(p_slot, 1); + if (rc) { + err("%s: Issue of Set Attn Indicator On command failed\n", __FUNCTION__); + return; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq (p_slot->ctrl); + + p_slot->hpc_ops->green_led_off(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq (p_slot->ctrl); + + /* Done with exclusive hardware access */ + up(&p_slot->ctrl->crit_sect); + } + p_slot->state = STATIC_STATE; + } + + return; +} + + +int pciehp_enable_slot (struct slot *p_slot) +{ + u8 getstatus = 0; + int rc; + struct pci_func *func; + + func = pciehp_slot_find(p_slot->bus, p_slot->device, 0); + if (!func) { + dbg("%s: Error! slot NULL\n", __FUNCTION__); + return (1); + } + + /* Check to see if (latch closed, card present, power off) */ + down(&p_slot->ctrl->crit_sect); + rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + if (rc || !getstatus) { + info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + + rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + if (rc || getstatus) { + info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + + rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (rc || getstatus) { + info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + up(&p_slot->ctrl->crit_sect); + + slot_remove(func); + + func = pciehp_slot_create(p_slot->bus); + if (func == NULL) + return (1); + + func->bus = p_slot->bus; + func->device = p_slot->device; + func->function = 0; + func->configured = 0; + func->is_a_board = 1; + + /* We have to save the presence info for these slots */ + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + func->switch_save = !getstatus? 0x10:0; + + rc = board_added(func, p_slot->ctrl); + if (rc) { + if (is_bridge(func)) + bridge_slot_remove(func); + else + slot_remove(func); + + /* Setup slot structure with entry for empty slot */ + func = pciehp_slot_create(p_slot->bus); + if (func == NULL) + return (1); /* Out of memory */ + + func->bus = p_slot->bus; + func->device = p_slot->device; + func->function = 0; + func->configured = 0; + func->is_a_board = 1; + + /* We have to save the presence info for these slots */ + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + func->switch_save = !getstatus? 0x10:0; + } + + if (p_slot) + update_slot_info(p_slot); + + return rc; +} + + +int pciehp_disable_slot (struct slot *p_slot) +{ + u8 class_code, header_type, BCR; + u8 index = 0; + u8 getstatus = 0; + u32 rc = 0; + int ret = 0; + unsigned int devfn; + struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate; + struct pci_func *func; + + if (!p_slot->ctrl) + return (1); + + /* Check to see if (latch closed, card present, power on) */ + down(&p_slot->ctrl->crit_sect); + + ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + if (ret || !getstatus) { + info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + + ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + if (ret || getstatus) { + info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + + ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (ret || !getstatus) { + info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + up(&p_slot->ctrl->crit_sect); + + func = pciehp_slot_find(p_slot->bus, p_slot->device, index++); + + /* Make sure there are no video controllers here + * for all func of p_slot + */ + while (func && !rc) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + /* Check the Class Code */ + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + if (rc) + return rc; + + if (class_code == PCI_BASE_CLASS_DISPLAY) { + /* Display/Video adapter (not supported) */ + rc = REMOVE_NOT_SUPPORTED; + } else { + /* See if it's a bridge */ + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + /* If it's a bridge, check the VGA Enable bit */ + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); + if (rc) + return rc; + + /* If the VGA Enable bit is set, remove isn't supported */ + if (BCR & PCI_BRIDGE_CTL_VGA) { + rc = REMOVE_NOT_SUPPORTED; + } + } + } + + func = pciehp_slot_find(p_slot->bus, p_slot->device, index++); + } + + func = pciehp_slot_find(p_slot->bus, p_slot->device, 0); + if ((func != NULL) && !rc) { + rc = remove_board(func, p_slot->ctrl); + } else if (!rc) + rc = 1; + + if (p_slot) + update_slot_info(p_slot); + + return(rc); +} + + +/** + * configure_new_device - Configures the PCI header information of one board. + * + * @ctrl: pointer to controller structure + * @func: pointer to function structure + * @behind_bridge: 1 if this is a recursive call, 0 if not + * @resources: pointer to set of resource lists + * + * Returns 0 if success + * + */ +static u32 configure_new_device (struct controller * ctrl, struct pci_func * func, + u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev) +{ + u8 temp_byte, function, max_functions, stop_it; + int rc; + u32 ID; + struct pci_func *new_slot; + struct pci_bus lpci_bus, *pci_bus; + int index; + + new_slot = func; + + dbg("%s\n", __FUNCTION__); + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + + /* Check for Multi-function device */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); + if (rc) { + dbg("%s: rc = %d\n", __FUNCTION__, rc); + return rc; + } + + if (temp_byte & 0x80) /* Multi-function device */ + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + rc = configure_new_function(ctrl, new_slot, behind_bridge, resources, bridge_bus, bridge_dev); + + if (rc) { + dbg("configure_new_function failed %d\n",rc); + index = 0; + + while (new_slot) { + new_slot = pciehp_slot_find(new_slot->bus, new_slot->device, index++); + + if (new_slot) + pciehp_return_board_resources(new_slot, resources); + } + + return(rc); + } + + function++; + + stop_it = 0; + + /* The following loop skips to the next present function + * and creates a board structure + */ + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); + + if (ID == 0xFFFFFFFF) { /* There's nothing there. */ + function++; + } else { /* There's something there */ + /* Setup slot structure. */ + new_slot = pciehp_slot_create(func->bus); + + if (new_slot == NULL) { + /* Out of memory */ + return(1); + } + + new_slot->bus = func->bus; + new_slot->device = func->device; + new_slot->function = function; + new_slot->is_a_board = 1; + new_slot->status = 0; + + stop_it++; + } + } + + } while (function < max_functions); + dbg("returning from configure_new_device\n"); + + return 0; +} + + +/* + * Configuration logic that involves the hotplug data structures and + * their bookkeeping + */ + + +/** + * configure_new_function - Configures the PCI header information of one device + * + * @ctrl: pointer to controller structure + * @func: pointer to function structure + * @behind_bridge: 1 if this is a recursive call, 0 if not + * @resources: pointer to set of resource lists + * + * Calls itself recursively for bridged devices. + * Returns 0 if success + * + */ +static int configure_new_function (struct controller * ctrl, struct pci_func * func, + u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev) +{ + int cloop; + u8 temp_byte; + u8 device; + u8 class_code; + u16 temp_word; + u32 rc; + u32 temp_register; + u32 base; + u32 ID; + unsigned int devfn; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_resource *hold_mem_node; + struct pci_resource *hold_p_mem_node; + struct pci_resource *hold_IO_node; + struct pci_resource *hold_bus_node; + struct irq_mapping irqs; + struct pci_func *new_slot; + struct pci_bus lpci_bus, *pci_bus; + struct resource_lists temp_resources; + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + /* Check for Bridge */ + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); + if (rc) + return rc; + dbg("%s: bus %x dev %x func %x temp_byte = %x\n", __FUNCTION__, + func->bus, func->device, func->function, temp_byte); + + if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + /* Set Primary bus */ + dbg("set Primary bus = 0x%x\n", func->bus); + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); + if (rc) + return rc; + + /* Find range of busses to use */ + bus_node = get_max_resource(&resources->bus_head, 1L); + + /* If we don't have any busses to allocate, we can't continue */ + if (!bus_node) { + err("Got NO bus resource to use\n"); + return -ENOMEM; + } + dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length); + + /* Set Secondary bus */ + dbg("set Secondary bus = 0x%x\n", temp_byte); + dbg("func->bus %x\n", func->bus); + + temp_byte = (u8)bus_node->base; + dbg("set Secondary bus = 0x%x\n", temp_byte); + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); + if (rc) + return rc; + + /* set subordinate bus */ + temp_byte = (u8)(bus_node->base + bus_node->length - 1); + dbg("set subordinate bus = 0x%x\n", temp_byte); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + if (rc) + return rc; + + /* Set HP parameters (Cache Line Size, Latency Timer) */ + rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE); + if (rc) + return rc; + + /* Setup the IO, memory, and prefetchable windows */ + + io_node = get_max_resource(&(resources->io_head), 0x1000L); + if (io_node) { + dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next); + } + + mem_node = get_max_resource(&(resources->mem_head), 0x100000L); + if (mem_node) { + dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next); + } + + if (resources->p_mem_head) + p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L); + else { + /* + * In some platform implementation, MEM and PMEM are not + * distinguished, and hence ACPI _CRS has only MEM entries + * for both MEM and PMEM. + */ + dbg("using MEM for PMEM\n"); + p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L); + } + if (p_mem_node) { + dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next); + } + + /* Set up the IRQ info */ + if (!resources->irqs) { + irqs.barber_pole = 0; + irqs.interrupt[0] = 0; + irqs.interrupt[1] = 0; + irqs.interrupt[2] = 0; + irqs.interrupt[3] = 0; + irqs.valid_INT = 0; + } else { + irqs.barber_pole = resources->irqs->barber_pole; + irqs.interrupt[0] = resources->irqs->interrupt[0]; + irqs.interrupt[1] = resources->irqs->interrupt[1]; + irqs.interrupt[2] = resources->irqs->interrupt[2]; + irqs.interrupt[3] = resources->irqs->interrupt[3]; + irqs.valid_INT = resources->irqs->valid_INT; + } + + /* Set up resource lists that are now aligned on top and bottom + * for anything behind the bridge. + */ + temp_resources.bus_head = bus_node; + temp_resources.io_head = io_node; + temp_resources.mem_head = mem_node; + temp_resources.p_mem_head = p_mem_node; + temp_resources.irqs = &irqs; + + /* Make copies of the nodes we are going to pass down so that + * if there is a problem,we can just use these to free resources + */ + hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { + if (hold_bus_node) + kfree(hold_bus_node); + if (hold_IO_node) + kfree(hold_IO_node); + if (hold_mem_node) + kfree(hold_mem_node); + if (hold_p_mem_node) + kfree(hold_p_mem_node); + + return(1); + } + + memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); + + bus_node->base += 1; + bus_node->length -= 1; + bus_node->next = NULL; + + /* If we have IO resources copy them and fill in the bridge's + * IO range registers + */ + if (io_node) { + memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); + io_node->next = NULL; + + /* set IO base and Limit registers */ + RES_CHECK(io_node->base, 8); + temp_byte = (u8)(io_node->base >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); + + RES_CHECK(io_node->base + io_node->length - 1, 8); + temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + } else { + kfree(hold_IO_node); + hold_IO_node = NULL; + } + + /* If we have memory resources copy them and fill in the bridge's + * memory range registers. Otherwise, fill in the range + * registers with values that disable them. + */ + if (mem_node) { + memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); + mem_node->next = NULL; + + /* set Mem base and Limit registers */ + RES_CHECK(mem_node->base, 16); + temp_word = (u32)(mem_node->base >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + RES_CHECK(mem_node->base + mem_node->length - 1, 16); + temp_word = (u32)((mem_node->base + mem_node->length - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + kfree(hold_mem_node); + hold_mem_node = NULL; + } + + /* If we have prefetchable memory resources copy them and + * fill in the bridge's memory range registers. Otherwise, + * fill in the range registers with values that disable them. + */ + if (p_mem_node) { + memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); + p_mem_node->next = NULL; + + /* Set Pre Mem base and Limit registers */ + RES_CHECK(p_mem_node->base, 16); + temp_word = (u32)(p_mem_node->base >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + RES_CHECK(p_mem_node->base + p_mem_node->length - 1, 16); + temp_word = (u32)((p_mem_node->base + p_mem_node->length - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + kfree(hold_p_mem_node); + hold_p_mem_node = NULL; + } + + /* Adjust this to compensate for extra adjustment in first loop */ + irqs.barber_pole--; + + rc = 0; + + /* Here we actually find the devices and configure them */ + for (device = 0; (device <= 0x1F) && !rc; device++) { + irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; + + ID = 0xFFFFFFFF; + pci_bus->number = hold_bus_node->base; + pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + pci_bus->number = func->bus; + + if (ID != 0xFFFFFFFF) { /* device Present */ + /* Setup slot structure. */ + new_slot = pciehp_slot_create(hold_bus_node->base); + + if (new_slot == NULL) { + /* Out of memory */ + rc = -ENOMEM; + continue; + } + + new_slot->bus = hold_bus_node->base; + new_slot->device = device; + new_slot->function = 0; + new_slot->is_a_board = 1; + new_slot->status = 0; + + rc = configure_new_device(ctrl, new_slot, 1, &temp_resources, func->bus, func->device); + dbg("configure_new_device rc=0x%x\n",rc); + } /* End of IF (device in slot?) */ + } /* End of FOR loop */ + + if (rc) { + pciehp_destroy_resource_list(&temp_resources); + + return_resource(&(resources->bus_head), hold_bus_node); + return_resource(&(resources->io_head), hold_IO_node); + return_resource(&(resources->mem_head), hold_mem_node); + return_resource(&(resources->p_mem_head), hold_p_mem_node); + return(rc); + } + + /* Save the interrupt routing information */ + if (resources->irqs) { + resources->irqs->interrupt[0] = irqs.interrupt[0]; + resources->irqs->interrupt[1] = irqs.interrupt[1]; + resources->irqs->interrupt[2] = irqs.interrupt[2]; + resources->irqs->interrupt[3] = irqs.interrupt[3]; + resources->irqs->valid_INT = irqs.valid_INT; + } else if (!behind_bridge) { + /* We need to hook up the interrupts here */ + for (cloop = 0; cloop < 4; cloop++) { + if (irqs.valid_INT & (0x01 << cloop)) { + rc = pciehp_set_irq(func->bus, func->device, + 0x0A + cloop, irqs.interrupt[cloop]); + if (rc) { + pciehp_destroy_resource_list (&temp_resources); + return_resource(&(resources->bus_head), hold_bus_node); + return_resource(&(resources->io_head), hold_IO_node); + return_resource(&(resources->mem_head), hold_mem_node); + return_resource(&(resources->p_mem_head), hold_p_mem_node); + return rc; + } + } + } /* end of for loop */ + } + + /* Return unused bus resources + * First use the temporary node to store information for the board + */ + if (hold_bus_node && bus_node && temp_resources.bus_head) { + hold_bus_node->length = bus_node->base - hold_bus_node->base; + + hold_bus_node->next = func->bus_head; + func->bus_head = hold_bus_node; + + temp_byte = (u8)(temp_resources.bus_head->base - 1); + + /* Set subordinate bus */ + dbg("re-set subordinate bus = 0x%x\n", temp_byte); + + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + + if (temp_resources.bus_head->length == 0) { + kfree(temp_resources.bus_head); + temp_resources.bus_head = NULL; + } else { + dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n", + func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length); + return_resource(&(resources->bus_head), temp_resources.bus_head); + } + } + + /* If we have IO space available and there is some left, + * return the unused portion + */ + if (hold_IO_node && temp_resources.io_head) { + io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), + &hold_IO_node, 0x1000); + + /* Check if we were able to split something off */ + if (io_node) { + hold_IO_node->base = io_node->base + io_node->length; + + RES_CHECK(hold_IO_node->base, 8); + temp_byte = (u8)((hold_IO_node->base) >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); + + return_resource(&(resources->io_head), io_node); + } + + io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); + + /* Check if we were able to split something off */ + if (io_node) { + /* First use the temporary node to store information for the board */ + hold_IO_node->length = io_node->base - hold_IO_node->base; + + /* If we used any, add it to the board's list */ + if (hold_IO_node->length) { + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + + RES_CHECK(io_node->base - 1, 8); + temp_byte = (u8)((io_node->base - 1) >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + + return_resource(&(resources->io_head), io_node); + } else { + /* It doesn't need any IO */ + temp_byte = 0x00; + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + + return_resource(&(resources->io_head), io_node); + kfree(hold_IO_node); + } + } else { + /* It used most of the range */ + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + } else if (hold_IO_node) { + /* It used the whole range */ + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + + /* If we have memory space available and there is some left, + * return the unused portion + */ + if (hold_mem_node && temp_resources.mem_head) { + mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L); + + /* Check if we were able to split something off */ + if (mem_node) { + hold_mem_node->base = mem_node->base + mem_node->length; + + RES_CHECK(hold_mem_node->base, 16); + temp_word = (u32)((hold_mem_node->base) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + return_resource(&(resources->mem_head), mem_node); + } + + mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L); + + /* Check if we were able to split something off */ + if (mem_node) { + /* First use the temporary node to store information for the board */ + hold_mem_node->length = mem_node->base - hold_mem_node->base; + + if (hold_mem_node->length) { + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + + /* Configure end address */ + RES_CHECK(mem_node->base - 1, 16); + temp_word = (u32)((mem_node->base - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + /* Return unused resources to the pool */ + return_resource(&(resources->mem_head), mem_node); + } else { + /* It doesn't need any Mem */ + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->mem_head), mem_node); + kfree(hold_mem_node); + } + } else { + /* It used most of the range */ + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + } else if (hold_mem_node) { + /* It used the whole range */ + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + + /* If we have prefetchable memory space available and there is some + * left at the end, return the unused portion + */ + if (hold_p_mem_node && temp_resources.p_mem_head) { + p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), + &hold_p_mem_node, 0x100000L); + + /* Check if we were able to split something off */ + if (p_mem_node) { + hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; + + RES_CHECK(hold_p_mem_node->base, 16); + temp_word = (u32)((hold_p_mem_node->base) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } + + p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L); + + /* Check if we were able to split something off */ + if (p_mem_node) { + /* First use the temporary node to store information for the board */ + hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; + + /* If we used any, add it to the board's list */ + if (hold_p_mem_node->length) { + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + + RES_CHECK(p_mem_node->base - 1, 16); + temp_word = (u32)((p_mem_node->base - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } else { + /* It doesn't need any PMem */ + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + kfree(hold_p_mem_node); + } + } else { + /* It used the most of the range */ + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + } else if (hold_p_mem_node) { + /* It used the whole range */ + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + + /* We should be configuring an IRQ and the bridge's base address + * registers if it needs them. Although we have never seen such + * a device + */ + + pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE); + + dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, + func->device, func->function); + } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + /* Standard device */ + u64 base64; + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + + if (class_code == PCI_BASE_CLASS_DISPLAY) + return (DEVICE_TYPE_NOT_SUPPORTED); + + /* Figure out IO and memory needs */ + for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { + temp_register = 0xFFFFFFFF; + + rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); + dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register, func->bus, func->device, func->function); + + if (!temp_register) + continue; + + base64 = 0L; + if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) { + /* Map IO */ + + /* Set base = amount of IO space */ + base = temp_register & 0xFFFFFFFC; + base = ~base + 1; + + dbg("NEED IO length(0x%x)\n", base); + io_node = get_io_resource(&(resources->io_head),(ulong)base); + + /* Allocate the resource to the board */ + if (io_node) { + dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length); + base = (u32)io_node->base; + io_node->next = func->io_head; + func->io_head = io_node; + } else { + err("Got NO IO resource(length=0x%x)\n", base); + return -ENOMEM; + } + } else { /* Map MEM */ + int prefetchable = 1; + struct pci_resource **res_node = &func->p_mem_head; + char *res_type_str = "PMEM"; + u32 temp_register2; + + if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) { + prefetchable = 0; + res_node = &func->mem_head; + res_type_str++; + } + + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { + case PCI_BASE_ADDRESS_MEM_TYPE_32: + dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base); + + if (prefetchable && resources->p_mem_head) + mem_node=get_resource(&(resources->p_mem_head), (ulong)base); + else { + if (prefetchable) + dbg("using MEM for PMEM\n"); + mem_node=get_resource(&(resources->mem_head), (ulong)base); + } + + /* Allocate the resource to the board */ + if (mem_node) { + base = (u32)mem_node->base; + mem_node->next = *res_node; + *res_node = mem_node; + dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base, mem_node->length); + } else { + err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base); + return -ENOMEM; + } + break; + case PCI_BASE_ADDRESS_MEM_TYPE_64: + rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); + dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2, temp_register, base); + + if (prefetchable && resources->p_mem_head) + mem_node = get_resource(&(resources->p_mem_head), (ulong)base); + else { + if (prefetchable) + dbg("using MEM for PMEM\n"); + mem_node = get_resource(&(resources->mem_head), (ulong)base); + } + + /* Allocate the resource to the board */ + if (mem_node) { + base64 = mem_node->base; + mem_node->next = *res_node; + *res_node = mem_node; + dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32), (u32)base64, mem_node->length); + } else { + err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base); + return -ENOMEM; + } + break; + default: + dbg("reserved BAR type=0x%x\n", temp_register); + break; + } + + } + + if (base64) { + rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); + cloop += 4; + base64 >>= 32; + + if (base64) { + dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64); + base64 = 0x0L; + } + + rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); + } else { + rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); + } + } /* End of base register loop */ + + /* Disable ROM base Address */ + temp_word = 0x00L; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word); + + /* Set HP parameters (Cache Line Size, Latency Timer) */ + rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL); + if (rc) + return rc; + + pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL); + + dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function); + } /* End of Not-A-Bridge else */ + else { + /* It's some strange type of PCI adapter (Cardbus?) */ + return(DEVICE_TYPE_NOT_SUPPORTED); + } + + func->configured = 1; + + dbg("%s: exit\n", __FUNCTION__); + + return 0; +} + diff -urN linux-2.4.26/drivers/hotplug/pciehp_hpc.c linux-2.4.27/drivers/hotplug/pciehp_hpc.c --- linux-2.4.26/drivers/hotplug/pciehp_hpc.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehp_hpc.c 2004-08-07 16:26:04.750350722 -0700 @@ -0,0 +1,1504 @@ +/* + * PCI Express PCI Hot Plug Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pciehp.h" + +#ifdef DEBUG +#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */ +#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */ +#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */ +#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */ +#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT) +#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE) +/* Redefine this flagword to set debug level */ +#define DEBUG_LEVEL DBG_K_STANDARD + +#define DEFINE_DBG_BUFFER char __dbg_str_buf[256]; + +#define DBG_PRINT( dbg_flags, args... ) \ + do { \ + if ( DEBUG_LEVEL & ( dbg_flags ) ) \ + { \ + int len; \ + len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \ + __FILE__, __LINE__, __FUNCTION__ ); \ + sprintf( __dbg_str_buf + len, args ); \ + printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \ + } \ + } while (0) + +#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]"); +#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]"); +#else +#define DEFINE_DBG_BUFFER +#define DBG_ENTER_ROUTINE +#define DBG_LEAVE_ROUTINE +#endif /* DEBUG */ + +struct ctrl_reg { + u8 cap_id; + u8 nxt_ptr; + u16 cap_reg; + u32 dev_cap; + u16 dev_ctrl; + u16 dev_status; + u32 lnk_cap; + u16 lnk_ctrl; + u16 lnk_status; + u32 slot_cap; + u16 slot_ctrl; + u16 slot_status; + u16 root_ctrl; + u16 rsvp; + u32 root_status; +} __attribute__ ((packed)); + +/* offsets to the controller registers based on the above structure layout */ +enum ctrl_offsets { + PCIECAPID = offsetof(struct ctrl_reg, cap_id), + NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr), + CAPREG = offsetof(struct ctrl_reg, cap_reg), + DEVCAP = offsetof(struct ctrl_reg, dev_cap), + DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl), + DEVSTATUS = offsetof(struct ctrl_reg, dev_status), + LNKCAP = offsetof(struct ctrl_reg, lnk_cap), + LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl), + LNKSTATUS = offsetof(struct ctrl_reg, lnk_status), + SLOTCAP = offsetof(struct ctrl_reg, slot_cap), + SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl), + SLOTSTATUS = offsetof(struct ctrl_reg, slot_status), + ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl), + ROOTSTATUS = offsetof(struct ctrl_reg, root_status), +}; +static int pcie_cap_base = 0; /* Base of the PCI Express capability item structure */ + +#define PCIE_CAP_ID ( pcie_cap_base + PCIECAPID ) +#define NXT_CAP_PTR ( pcie_cap_base + NXTCAPPTR ) +#define CAP_REG ( pcie_cap_base + CAPREG ) +#define DEV_CAP ( pcie_cap_base + DEVCAP ) +#define DEV_CTRL ( pcie_cap_base + DEVCTRL ) +#define DEV_STATUS ( pcie_cap_base + DEVSTATUS ) +#define LNK_CAP ( pcie_cap_base + LNKCAP ) +#define LNK_CTRL ( pcie_cap_base + LNKCTRL ) +#define LNK_STATUS ( pcie_cap_base + LNKSTATUS ) +#define SLOT_CAP ( pcie_cap_base + SLOTCAP ) +#define SLOT_CTRL ( pcie_cap_base + SLOTCTRL ) +#define SLOT_STATUS ( pcie_cap_base + SLOTSTATUS ) +#define ROOT_CTRL ( pcie_cap_base + ROOTCTRL ) +#define ROOT_STATUS ( pcie_cap_base + ROOTSTATUS ) + +#define hp_register_read_word(pdev, reg , value) \ + pci_read_config_word(pdev, reg, &value) + +#define hp_register_read_dword(pdev, reg , value) \ + pci_read_config_dword(pdev, reg, &value) + +#define hp_register_write_word(pdev, reg , value) \ + pci_write_config_word(pdev, reg, value) + +#define hp_register_dwrite_word(pdev, reg , value) \ + pci_write_config_dword(pdev, reg, value) + +/* Field definitions in PCI Express Capabilities Register */ +#define CAP_VER 0x000F +#define DEV_PORT_TYPE 0x00F0 +#define SLOT_IMPL 0x0100 +#define MSG_NUM 0x3E00 + +/* Device or Port Type */ +#define NAT_ENDPT 0x00 +#define LEG_ENDPT 0x01 +#define ROOT_PORT 0x04 +#define UP_STREAM 0x05 +#define DN_STREAM 0x06 +#define PCIE_PCI_BRDG 0x07 +#define PCI_PCIE_BRDG 0x10 + +/* Field definitions in Device Capabilities Register */ +#define DATTN_BUTTN_PRSN 0x1000 +#define DATTN_LED_PRSN 0x2000 +#define DPWR_LED_PRSN 0x4000 + +/* Field definitions in Link Capabilities Register */ +#define MAX_LNK_SPEED 0x000F +#define MAX_LNK_WIDTH 0x03F0 + +/* Link Width Encoding */ +#define LNK_X1 0x01 +#define LNK_X2 0x02 +#define LNK_X4 0x04 +#define LNK_X8 0x08 +#define LNK_X12 0x0C +#define LNK_X16 0x10 +#define LNK_X32 0x20 + +/*Field definitions of Link Status Register */ +#define LNK_SPEED 0x000F +#define NEG_LINK_WD 0x03F0 +#define LNK_TRN_ERR 0x0400 +#define LNK_TRN 0x0800 +#define SLOT_CLK_CONF 0x1000 + +/* Field definitions in Slot Capabilities Register */ +#define ATTN_BUTTN_PRSN 0x00000001 +#define PWR_CTRL_PRSN 0x00000002 +#define MRL_SENS_PRSN 0x00000004 +#define ATTN_LED_PRSN 0x00000008 +#define PWR_LED_PRSN 0x00000010 +#define HP_SUPR_RM 0x00000020 +#define HP_CAP 0x00000040 +#define SLOT_PWR_VALUE 0x000003F8 +#define SLOT_PWR_LIMIT 0x00000C00 +#define PSN 0xFFF80000 /* PSN: Physical Slot Number */ + +/* Field definitions in Slot Control Register */ +#define ATTN_BUTTN_ENABLE 0x0001 +#define PWR_FAULT_DETECT_ENABLE 0x0002 +#define MRL_DETECT_ENABLE 0x0004 +#define PRSN_DETECT_ENABLE 0x0008 +#define CMD_CMPL_INTR_ENABLE 0x0010 +#define HP_INTR_ENABLE 0x0020 +#define ATTN_LED_CTRL 0x00C0 +#define PWR_LED_CTRL 0x0300 +#define PWR_CTRL 0x0400 + +/* Attention indicator and Power indicator states */ +#define LED_ON 0x01 +#define LED_BLINK 0x10 +#define LED_OFF 0x11 + +/* Power Control Command */ +#define POWER_ON 0 +#define POWER_OFF 0x0400 + +/* Field definitions in Slot Status Register */ +#define ATTN_BUTTN_PRESSED 0x0001 +#define PWR_FAULT_DETECTED 0x0002 +#define MRL_SENS_CHANGED 0x0004 +#define PRSN_DETECT_CHANGED 0x0008 +#define CMD_COMPLETED 0x0010 +#define MRL_STATE 0x0020 +#define PRSN_STATE 0x0040 + +struct php_ctlr_state_s { + struct php_ctlr_state_s *pnext; + struct pci_dev *pci_dev; + unsigned int irq; + unsigned long flags; /* spinlock's */ + u32 slot_device_offset; + u32 num_slots; + struct timer_list int_poll_timer; /* Added for poll event */ + php_intr_callback_t attention_button_callback; + php_intr_callback_t switch_change_callback; + php_intr_callback_t presence_change_callback; + php_intr_callback_t power_fault_callback; + void *callback_instance_id; + struct ctrl_reg *creg; /* Ptr to controller register space */ +}; + +static spinlock_t hpc_event_lock; + +DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ +static struct php_ctlr_state_s *php_ctlr_list_head = 0; /* HPC state linked list */ +static int ctlr_seq_num = 0; /* Controller sequence # */ +static spinlock_t list_lock; +static void pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs); + +static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds); + +/* This is the interrupt polling timeout function. */ +static void int_poll_timeout(unsigned long lphp_ctlr) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr; + + DBG_ENTER_ROUTINE + + if ( !php_ctlr ) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return; + } + + /* Poll for interrupt events. regs == NULL => polling */ + pcie_isr( 0, (void *)php_ctlr, NULL ); + + init_timer(&php_ctlr->int_poll_timer); + + if (!pciehp_poll_time) + pciehp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/ + + start_int_poll_timer(php_ctlr, pciehp_poll_time); + + return; +} + +/* This function starts the interrupt polling timer. */ +static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds) +{ + if (!php_ctlr) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return; + } + + if ( ( seconds <= 0 ) || ( seconds > 60 ) ) + seconds = 2; /* Clamp to sane value */ + + php_ctlr->int_poll_timer.function = &int_poll_timeout; + php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */ + php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ; + add_timer(&php_ctlr->int_poll_timer); + + return; +} + +static int pcie_write_cmd(struct slot *slot, u16 cmd) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + int retval = 0; + u16 slot_status; + + DBG_ENTER_ROUTINE + + dbg("%s : Enter\n", __FUNCTION__); + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (retval) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return retval; + } + dbg("%s : hp_register_read_word SLOT_STATUS %x\n", __FUNCTION__, slot_status); + + if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) { + /* After 1 sec and CMD_COMPLETED still not set, just proceed forward to issue + the next command according to spec. Just print out the error message */ + dbg("%s : CMD_COMPLETED not clear after 1 sec.\n", __FUNCTION__); + } + + dbg("%s : Before hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd); + retval = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, cmd | CMD_CMPL_INTR_ENABLE); + if (retval) { + err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); + return retval; + } + dbg("%s : hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd|CMD_CMPL_INTR_ENABLE); + dbg("%s : Exit\n", __FUNCTION__); + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_check_lnk_status(struct controller *ctrl) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; + u16 lnk_status; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status); + + if (retval) { + err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); + return retval; + } + + if ( (lnk_status & (LNK_TRN | LNK_TRN_ERR)) == 0x0C00) { + err("%s : Link Training Error occurs \n", __FUNCTION__); + retval = -1; + return retval; + } + + DBG_LEAVE_ROUTINE + return retval; +} + + +static int hpc_get_attention_status(struct slot *slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_ctrl; + u8 atten_led_state; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (retval) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return retval; + } + + dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__,SLOT_CTRL, slot_ctrl); + + atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6; + //atten_led_state = (slot_ctrl & PWR_LED_CTRL) >> 8; + + switch (atten_led_state) { + case 0: + *status = 0xFF; /* Reserved */ + break; + case 1: + *status = 1; /* On */ + break; + case 2: + *status = 2; /* Blink */ + break; + case 3: + *status = 0; /* Off */ + break; + default: + *status = 0xFF; + break; + } + + DBG_LEAVE_ROUTINE + return 0; +} + +static int hpc_get_power_status(struct slot * slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_ctrl; + u8 pwr_state; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (retval) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return retval; + } + dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl); + + pwr_state = (slot_ctrl & PWR_CTRL) >> 10; + + switch (pwr_state) { + case 0: + *status = 1; + break; + case 1: + *status = 0; + break; + default: + *status = 0xFF; + break; + } + + DBG_LEAVE_ROUTINE + return retval; +} + + +static int hpc_get_latch_status(struct slot *slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_status; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + + if (retval) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return retval; + } + + *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1; + + DBG_LEAVE_ROUTINE + return 0; +} + +static int hpc_get_adapter_status(struct slot *slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_status; + u8 card_state; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + + if (retval) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return retval; + } + card_state = (u8)((slot_status & PRSN_STATE) >> 6); + *status = (card_state == 1) ? 1 : 0; + + DBG_LEAVE_ROUTINE + return 0; +} + + +static int hpc_query_power_fault(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_status; + u8 pwr_fault; + int retval = 0; + u8 status; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + + if (retval) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return retval; + } + pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1); + status = (pwr_fault != 1) ? 1 : 0; + + DBG_LEAVE_ROUTINE + /* Note: Logic 0 => fault */ + return status; +} + +static int hpc_set_attention_status(struct slot *slot, u8 value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_cmd = 0; + u16 slot_ctrl; + int rc = 0; + + dbg("%s: \n", __FUNCTION__); + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return rc; + } + dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); + + switch (value) { + case 0 : /* turn off */ + slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0; + //slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300; + break; + case 1: /* turn on */ + slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040; + //slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100; + break; + case 2: /* turn blink */ + slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080; + //slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200; + break; + default: + return -1; + } + if (!pciehp_poll_mode) + slot_cmd = slot_cmd | HP_INTR_ENABLE; + + pcie_write_cmd(slot, slot_cmd); + dbg("%s: SLOT_CTRL %x write cmd %x\n", + __FUNCTION__, SLOT_CTRL, slot_cmd); + + return rc; +} + + +static void hpc_set_green_led_on(struct slot *slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_cmd; + u16 slot_ctrl; + int rc = 0; + + dbg("%s: \n", __FUNCTION__); + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return ; + } + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return ; + } + dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); + slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100; + //slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040; + + if (!pciehp_poll_mode) + slot_cmd = slot_cmd | HP_INTR_ENABLE; + + pcie_write_cmd(slot, slot_cmd); + + dbg("%s: SLOT_CTRL %x write cmd %x\n", + __FUNCTION__, SLOT_CTRL, slot_cmd); + return; +} + +static void hpc_set_green_led_off(struct slot *slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_cmd; + u16 slot_ctrl; + int rc = 0; + + dbg("%s: \n", __FUNCTION__); + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return ; + } + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return; + } + dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); + + slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300; + //slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00c0; + + if (!pciehp_poll_mode) + slot_cmd = slot_cmd | HP_INTR_ENABLE; + pcie_write_cmd(slot, slot_cmd); + + dbg("%s: SLOT_CTRL %x write cmd %x\n", + __FUNCTION__, SLOT_CTRL, slot_cmd); + return; +} + +static void hpc_set_green_led_blink(struct slot *slot) +{ + struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_cmd; + u16 slot_ctrl; + int rc = 0; + + dbg("%s: \n", __FUNCTION__); + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return ; + } + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return; + } + dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl); + + slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200; + //slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080; + + if (!pciehp_poll_mode) + slot_cmd = slot_cmd | HP_INTR_ENABLE; + pcie_write_cmd(slot, slot_cmd); + + dbg("%s: SLOT_CTRL %x write cmd %x\n", + __FUNCTION__, SLOT_CTRL, slot_cmd); + return; +} + +int pcie_get_ctlr_slot_config(struct controller *ctrl, + int *num_ctlr_slots, /* number of slots in this HPC; only 1 in PCIE */ + int *first_device_num, /* PCI dev num of the first slot in this PCIE */ + int *physical_slot_num, /* phy slot num of the first slot in this PCIE */ + int *updown, /* physical_slot_num increament: 1 or -1 */ + int *flags) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; + u32 slot_cap; + int rc = 0; + + DBG_ENTER_ROUTINE + + if (!ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + *first_device_num = 0; + *num_ctlr_slots = 1; + + rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap); + if (rc) { + err("%s : hp_register_read_dword SLOT_CAP failed\n", __FUNCTION__); + return -1; + } + + *physical_slot_num = slot_cap >> 19; + *updown = -1; + + DBG_LEAVE_ROUTINE + return 0; +} + +static void hpc_release_ctlr(struct controller *ctrl) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; + struct php_ctlr_state_s *p, *p_prev; + + DBG_ENTER_ROUTINE + + if (!ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (pciehp_poll_mode) { + del_timer(&php_ctlr->int_poll_timer); + } else { + if (php_ctlr->irq) { + free_irq(php_ctlr->irq, ctrl); + php_ctlr->irq = 0; + } + } + if (php_ctlr->pci_dev) + php_ctlr->pci_dev = 0; + + spin_lock(&list_lock); + p = php_ctlr_list_head; + p_prev = NULL; + while (p) { + if (p == php_ctlr) { + if (p_prev) + p_prev->pnext = p->pnext; + else + php_ctlr_list_head = p->pnext; + break; + } else { + p_prev = p; + p = p->pnext; + } + } + spin_unlock(&list_lock); + + kfree(php_ctlr); + + DBG_LEAVE_ROUTINE + +} + +static int hpc_power_on_slot(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_cmd; + u16 slot_ctrl; + + int retval = 0; + + DBG_ENTER_ROUTINE + dbg("%s: \n", __FUNCTION__); + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (retval) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return retval; + } + dbg("%s: SLOT_CTRL %x, value read %xn", __FUNCTION__, SLOT_CTRL, + slot_ctrl); + + slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON; + + if (!pciehp_poll_mode) + slot_cmd = slot_cmd | HP_INTR_ENABLE; + + retval = pcie_write_cmd(slot, slot_cmd); + + if (retval) { + err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd); + return -1; + } + dbg("%s: SLOT_CTRL %x write cmd %x\n", + __FUNCTION__, SLOT_CTRL, slot_cmd); + + DBG_LEAVE_ROUTINE + + return retval; +} + +static int hpc_power_off_slot(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 slot_cmd; + u16 slot_ctrl; + + int retval = 0; + + DBG_ENTER_ROUTINE + dbg("%s: \n", __FUNCTION__); + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); + slot->hp_slot = 0; + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + + if (retval) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return retval; + } + dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__, SLOT_CTRL, + slot_ctrl); + + slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF; + + if (!pciehp_poll_mode) + slot_cmd = slot_cmd | HP_INTR_ENABLE; + + retval = pcie_write_cmd(slot, slot_cmd); + + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + dbg("%s: SLOT_CTRL %x write cmd %x\n", + __FUNCTION__, SLOT_CTRL, slot_cmd); + + DBG_LEAVE_ROUTINE + + return retval; +} + +static void pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs) +{ + struct controller *ctrl = NULL; + struct php_ctlr_state_s *php_ctlr; + u8 schedule_flag = 0; + u16 slot_status, intr_detect, intr_loc; + u16 temp_word; + int hp_slot = 0; /* only 1 slot per PCI Express port */ + int rc = 0; + + if (!dev_id) { + dbg("%s: dev_id == NULL\n", __FUNCTION__); + return; + } + + if (!pciehp_poll_mode) { + ctrl = (struct controller *)dev_id; + php_ctlr = ctrl->hpc_ctlr_handle; + } else { + php_ctlr = (struct php_ctlr_state_s *) dev_id; + ctrl = (struct controller *)php_ctlr->callback_instance_id; + } + + if (!ctrl) { + dbg("%s: dev_id %p ctlr == NULL\n", __FUNCTION__, (void*) dev_id); + return; + } + if (!php_ctlr) { + dbg("%s: php_ctlr == NULL\n", __FUNCTION__); + return; + } + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (rc) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return; + } + /* dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status); */ + + intr_detect = ( ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | MRL_SENS_CHANGED | + PRSN_DETECT_CHANGED | CMD_COMPLETED ); + + intr_loc = slot_status & intr_detect; + + /* Check to see if it was our interrupt */ + if ( !intr_loc ) + return; + + dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); + + /* Mask Hot-plug Interrupt Enable */ + if (!pciehp_poll_mode) { + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return; + } + + dbg("%s: Set Mask Hot-plug Interrupt Enable\n", __FUNCTION__); + dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); + temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00; + + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); + return; + } + dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (rc) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return; + } + dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status); + + /* Clear command complete interrupt caused by this write */ + temp_word = 0x1f; + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); + return; + } + dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word); + } + + if (intr_loc & CMD_COMPLETED) { + /* + * Command Complete Interrupt Pending + */ + dbg("%s: In Command Complete Interrupt Pending\n", __FUNCTION__); + wake_up_interruptible(&ctrl->queue); + } + if ((php_ctlr->switch_change_callback) && (intr_loc & MRL_SENS_CHANGED)) + schedule_flag += php_ctlr->switch_change_callback( + hp_slot, php_ctlr->callback_instance_id); + if ((php_ctlr->attention_button_callback) && (intr_loc & ATTN_BUTTN_PRESSED)) + schedule_flag += php_ctlr->attention_button_callback( + hp_slot, php_ctlr->callback_instance_id); + if ((php_ctlr->presence_change_callback) && (intr_loc & PRSN_DETECT_CHANGED)) + schedule_flag += php_ctlr->presence_change_callback( + hp_slot , php_ctlr->callback_instance_id); + if ((php_ctlr->power_fault_callback) && (intr_loc & PWR_FAULT_DETECTED)) + schedule_flag += php_ctlr->power_fault_callback( + hp_slot, php_ctlr->callback_instance_id); + + /* Clear all events after serving them */ + temp_word = 0x1F; + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); + return; + } + + /* Unmask Hot-plug Interrupt Enable */ + if (!pciehp_poll_mode) { + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + return; + } + dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__); + dbg("%s: hp_register_read_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); + temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; + + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); + return; + } + dbg("%s: hp_register_write_word SLOT_CTRL with value %x\n", __FUNCTION__, temp_word); + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (rc) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + return; + } + dbg("%s: hp_register_read_word SLOT_STATUS with value %x\n", __FUNCTION__, slot_status); + + /* Clear command complete interrupt caused by this write */ + temp_word = 0x1F; + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); + return; + } + dbg("%s: hp_register_write_word SLOT_STATUS with value %x\n", __FUNCTION__, temp_word); + } + return; +} + +static int hpc_get_max_lnk_speed (struct slot *slot, enum pcie_link_speed *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + enum pcie_link_speed lnk_speed; + u32 lnk_cap; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap); + + if (retval) { + err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__); + return retval; + } + + switch (lnk_cap & 0x000F) { + case 1: + lnk_speed = PCIE_2PT5GB; + break; + default: + lnk_speed = PCIE_LNK_SPEED_UNKNOWN; + break; + } + + *value = lnk_speed; + dbg("Max link speed = %d\n", lnk_speed); + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_get_max_lnk_width (struct slot *slot, enum pcie_link_width *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + enum pcie_link_width lnk_wdth; + u32 lnk_cap; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap); + + if (retval) { + err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__); + return retval; + } + + switch ((lnk_cap & 0x03F0) >> 4){ + case 0: + lnk_wdth = PCIE_LNK_WIDTH_RESRV; + break; + case 1: + lnk_wdth = PCIE_LNK_X1; + break; + case 2: + lnk_wdth = PCIE_LNK_X2; + break; + case 4: + lnk_wdth = PCIE_LNK_X4; + break; + case 8: + lnk_wdth = PCIE_LNK_X8; + break; + case 12: + lnk_wdth = PCIE_LNK_X12; + break; + case 16: + lnk_wdth = PCIE_LNK_X16; + break; + case 32: + lnk_wdth = PCIE_LNK_X32; + break; + default: + lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; + break; + } + + *value = lnk_wdth; + dbg("Max link width = %d\n", lnk_wdth); + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_get_cur_lnk_speed (struct slot *slot, enum pcie_link_speed *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN; + int retval = 0; + u16 lnk_status; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status); + + if (retval) { + err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); + return retval; + } + + switch (lnk_status & 0x0F) { + case 1: + lnk_speed = PCIE_2PT5GB; + break; + default: + lnk_speed = PCIE_LNK_SPEED_UNKNOWN; + break; + } + + *value = lnk_speed; + dbg("Current link speed = %d\n", lnk_speed); + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_get_cur_lnk_width (struct slot *slot, enum pcie_link_width *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; + int retval = 0; + u16 lnk_status; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status); + + if (retval) { + err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__); + return retval; + } + + switch ((lnk_status & 0x03F0) >> 4){ + case 0: + lnk_wdth = PCIE_LNK_WIDTH_RESRV; + break; + case 1: + lnk_wdth = PCIE_LNK_X1; + break; + case 2: + lnk_wdth = PCIE_LNK_X2; + break; + case 4: + lnk_wdth = PCIE_LNK_X4; + break; + case 8: + lnk_wdth = PCIE_LNK_X8; + break; + case 12: + lnk_wdth = PCIE_LNK_X12; + break; + case 16: + lnk_wdth = PCIE_LNK_X16; + break; + case 32: + lnk_wdth = PCIE_LNK_X32; + break; + default: + lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN; + break; + } + + *value = lnk_wdth; + dbg("Current link width = %d\n", lnk_wdth); + DBG_LEAVE_ROUTINE + return retval; +} + +static struct hpc_ops pciehp_hpc_ops = { + .power_on_slot = hpc_power_on_slot, + .power_off_slot = hpc_power_off_slot, + .set_attention_status = hpc_set_attention_status, + .get_power_status = hpc_get_power_status, + .get_attention_status = hpc_get_attention_status, + .get_latch_status = hpc_get_latch_status, + .get_adapter_status = hpc_get_adapter_status, + + .get_max_bus_speed = hpc_get_max_lnk_speed, + .get_cur_bus_speed = hpc_get_cur_lnk_speed, + .get_max_lnk_width = hpc_get_max_lnk_width, + .get_cur_lnk_width = hpc_get_cur_lnk_width, + + .query_power_fault = hpc_query_power_fault, + .green_led_on = hpc_set_green_led_on, + .green_led_off = hpc_set_green_led_off, + .green_led_blink = hpc_set_green_led_blink, + + .release_ctlr = hpc_release_ctlr, + .check_lnk_status = hpc_check_lnk_status, +}; + +int pcie_init(struct controller * ctrl, + struct pci_dev * pdev, + php_intr_callback_t attention_button_callback, + php_intr_callback_t switch_change_callback, + php_intr_callback_t presence_change_callback, + php_intr_callback_t power_fault_callback) +{ + struct php_ctlr_state_s *php_ctlr, *p; + void *instance_id = ctrl; + int rc; + static int first = 1; + u16 temp_word; + u16 cap_reg; + u16 intr_enable; + u32 slot_cap; + int cap_base, saved_cap_base; + u16 slot_status, slot_ctrl; + + DBG_ENTER_ROUTINE + + spin_lock_init(&list_lock); + php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL); + + if (!php_ctlr) { /* Allocate controller state data */ + err("%s: HPC controller memory allocation error!\n", __FUNCTION__); + goto abort; + } + + memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s)); + + php_ctlr->pci_dev = pdev; /* Save pci_dev in context */ + + dbg("%s: pdev->vendor %x pdev->device %x\n", __FUNCTION__, + pdev->vendor, pdev->device); + + saved_cap_base = pcie_cap_base; + + if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) { + dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__); + goto abort_free_ctlr; + } + pcie_cap_base = cap_base; + + dbg("%s: pcie_cap_base %x\n", __FUNCTION__, pcie_cap_base); + + rc = hp_register_read_word(pdev, CAP_REG, cap_reg); + if (rc) { + err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: CAP_REG offset %x cap_reg %x\n", __FUNCTION__, CAP_REG, cap_reg); + + if (((cap_reg & SLOT_IMPL) == 0) || ((cap_reg & DEV_PORT_TYPE) != 0x0040)){ + dbg("%s : This is not a root port or the port is not connected to a slot\n", __FUNCTION__); + goto abort_free_ctlr; + } + + rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap); + if (rc) { + err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_CAP offset %x slot_cap %x\n", __FUNCTION__, SLOT_CAP, slot_cap); + + if (!(slot_cap & HP_CAP)) { + dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__); + goto abort_free_ctlr; + } + + /* For debugging purpose */ + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (rc) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_STATUS offset %x slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status); + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl); + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_CTRL offset %x slot_ctrl %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl); + + if (first) { + spin_lock_init(&hpc_event_lock); + first = 0; + } + + dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq); + for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) + if (pci_resource_len(pdev, rc) > 0) + dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc, + pci_resource_start(pdev, rc), pci_resource_len(pdev, rc)); + + dbg("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + init_MUTEX(&ctrl->crit_sect); + /* Setup wait queue */ + init_waitqueue_head(&ctrl->queue); + + /* Find the IRQ */ + php_ctlr->irq = pdev->irq; + dbg("HPC interrupt = %d\n", php_ctlr->irq); + + /* Save interrupt callback info */ + php_ctlr->attention_button_callback = attention_button_callback; + php_ctlr->switch_change_callback = switch_change_callback; + php_ctlr->presence_change_callback = presence_change_callback; + php_ctlr->power_fault_callback = power_fault_callback; + php_ctlr->callback_instance_id = instance_id; + + /* Return PCI Controller Info */ + php_ctlr->slot_device_offset = 0; + php_ctlr->num_slots = 1; + + /* Mask Hot-plug Interrupt Enable */ + rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word); + temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | 0x00; + + rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s : Mask HPIE hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, temp_word); + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (rc) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: Mask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, + SLOT_STATUS, slot_status); + + temp_word = 0x1F; /* Clear all events */ + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, temp_word); + + if (pciehp_poll_mode) {/* Install interrupt polling code */ + /* Install and start the interrupt polling timer */ + init_timer(&php_ctlr->int_poll_timer); + start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */ + } else { + /* Installs the interrupt handler */ +#ifdef CONFIG_PCI_USE_VECTOR + if (!pciehp_msi_quirk) { + rc = pci_enable_msi(pdev); + if (rc) { + info("Can't get msi for the hotplug controller\n"); + info("Use INTx for the hotplug controller\n"); + dbg("%s: rc = %x\n", __FUNCTION__, rc); + } else + php_ctlr->irq = pdev->irq; + } +#endif + rc = request_irq(php_ctlr->irq, pcie_isr, SA_SHIRQ, MY_NAME, (void *) ctrl); + dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc); + if (rc) { + err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq); + goto abort_free_ctlr; + } + } + + rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word); + + intr_enable = ATTN_BUTTN_ENABLE | PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | + PRSN_DETECT_ENABLE; + + temp_word = (temp_word & ~intr_enable) | intr_enable; + + if (pciehp_poll_mode) { + temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; + } else { + temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; + } + dbg("%s: temp_word %x\n", __FUNCTION__, temp_word); + + /* Unmask Hot-plug Interrupt Enable for the interrupt notification mechanism case */ + rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s : Unmask HPIE hp_register_write_word SLOT_CTRL with %x\n", __FUNCTION__, temp_word); + + rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status); + if (rc) { + err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: Unmask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, + SLOT_STATUS, slot_status); + + temp_word = 0x1F; /* Clear all events */ + rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word); + if (rc) { + err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, temp_word); + + /* Add this HPC instance into the HPC list */ + spin_lock(&list_lock); + if (php_ctlr_list_head == 0) { + php_ctlr_list_head = php_ctlr; + p = php_ctlr_list_head; + p->pnext = 0; + } else { + p = php_ctlr_list_head; + + while (p->pnext) + p = p->pnext; + + p->pnext = php_ctlr; + } + spin_unlock(&list_lock); + + ctlr_seq_num++; + ctrl->hpc_ctlr_handle = php_ctlr; + ctrl->hpc_ops = &pciehp_hpc_ops; + + DBG_LEAVE_ROUTINE + return 0; + + /* We end up here for the many possible ways to fail this API. */ +abort_free_ctlr: + pcie_cap_base = saved_cap_base; + kfree(php_ctlr); +abort: + DBG_LEAVE_ROUTINE + return -1; +} diff -urN linux-2.4.26/drivers/hotplug/pciehp_pci.c linux-2.4.27/drivers/hotplug/pciehp_pci.c --- linux-2.4.26/drivers/hotplug/pciehp_pci.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehp_pci.c 2004-08-07 16:26:04.753350845 -0700 @@ -0,0 +1,1052 @@ +/* + * PCI Express Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pciehp.h" +#ifndef CONFIG_IA64 +#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */ +#endif + +static int is_pci_dev_in_use(struct pci_dev* dev) +{ + /* + * dev->driver will be set if the device is in use by a new-style + * driver -- otherwise, check the device's regions to see if any + * driver has claimed them + */ + + int i, inuse=0; + + if (dev->driver) return 1; /* Assume driver feels responsible */ + + for (i = 0; !dev->driver && !inuse && (i < 6); i++) { + if (!pci_resource_start(dev, i)) + continue; + + if (pci_resource_flags(dev, i) & IORESOURCE_IO) + inuse = check_region(pci_resource_start(dev, i), + pci_resource_len(dev, i)); + else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + inuse = check_mem_region(pci_resource_start(dev, i), + pci_resource_len(dev, i)); + } + + return inuse; + +} + + +static int pci_hp_remove_device(struct pci_dev *dev) +{ + if (is_pci_dev_in_use(dev)) { + err("***Cannot safely power down device -- " + "it appears to be in use***\n"); + return -EBUSY; + } + dbg("%s: dev %p\n", __FUNCTION__, dev); + pci_remove_device(dev); + return 0; +} + + +static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_bus* bus = wrapped_bus->bus; + struct pci_dev* dev = wrapped_dev->dev; + struct pci_func *temp_func; + int i=0; + + /* We need to fix up the hotplug function representation with the linux representation */ + do { + temp_func = pciehp_slot_find(dev->bus->number, dev->devfn >> 3, i++); + } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); + + if (temp_func) { + temp_func->pci_dev = dev; + } else { + /* We did not even find a hotplug rep of the function, create it + * This code might be taken out if we can guarantee the creation of functions + * in parallel (hotplug and Linux at the same time). + */ + dbg("@@@@@@@@@@@ pciehp_slot_create in %s\n", __FUNCTION__); + temp_func = pciehp_slot_create(bus->number); + if (temp_func == NULL) + return -ENOMEM; + temp_func->pci_dev = dev; + } + + /* Create /proc/bus/pci proc entry for this device and bus device is on */ + /* Notify the drivers of the change */ + if (temp_func->pci_dev) { + dbg("%s: PCI_ID=%04X:%04X\n", __FUNCTION__, temp_func->pci_dev->vendor, + temp_func->pci_dev->device); + dbg("%s: PCI BUS %x DEVFN %x\n", __FUNCTION__, + temp_func->pci_dev->bus->number, temp_func->pci_dev->devfn); + dbg("%s: PCI_SLOT_NAME=%s\n", __FUNCTION__, + temp_func->pci_dev->slot_name); + //pci_enable_device(temp_func->pci_dev); + //dbg("%s: after calling pci_enable_device, irq = %x\n", + // __FUNCTION__, temp_func->pci_dev->irq); + pci_proc_attach_device(temp_func->pci_dev); + pci_announce_device_to_drivers(temp_func->pci_dev); + } + + return 0; +} + + +static int unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev* dev = wrapped_dev->dev; + + struct pci_func *temp_func; + int i=0; + + dbg("%s: dev %p dev->bus->number %x bus->number %x\n", __FUNCTION__, wrapped_dev, + dev->bus->number, wrapped_bus->bus->number); + + /* We need to remove the hotplug function representation with the linux representation */ + do { + temp_func = pciehp_slot_find(dev->bus->number, dev->devfn >> 3, i++); + if (temp_func) { + dbg("%s: temp_func->function = %d\n", __FUNCTION__, temp_func->function); + } + } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); + + /* Now, remove the Linux Representation */ + if (dev) { + if (pci_hp_remove_device(dev) == 0) { + kfree(dev); /* Now, remove */ + } else { + return -1; /* problems while freeing, abort visitation */ + } + } + + if (temp_func) { + temp_func->pci_dev = NULL; + } else { + dbg("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn); + } + + return 0; +} + + +static int unconfigure_visit_pci_bus_phase2 (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev) +{ + struct pci_bus* bus = wrapped_bus->bus; + + /* The cleanup code for proc entries regarding buses should be in the kernel...*/ + if (bus->procdir) + dbg("detach_pci_bus %s\n", bus->procdir->name); + pci_proc_detach_bus(bus); + /* The cleanup code should live in the kernel... */ + bus->self->subordinate = NULL; + /* unlink from parent bus */ + list_del(&bus->node); + + /* Now, remove */ + if (bus) + kfree(bus); + + return 0; +} + + +static int unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev* dev = wrapped_dev->dev; + int rc; + + dbg("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + /* Now, remove the Linux Driver Representation */ + if (dev->driver) { + if (dev->driver->remove) { + dev->driver->remove(dev); + dbg("driver was properly removed\n"); + } + dev->driver = NULL; + } + + rc = is_pci_dev_in_use(dev); + if (rc) + info("%s: device still in use\n", __FUNCTION__); + return rc; +} + + +static struct pci_visit configure_functions = { + .visit_pci_dev = configure_visit_pci_dev, +}; + + +static struct pci_visit unconfigure_functions_phase1 = { + .post_visit_pci_dev = unconfigure_visit_pci_dev_phase1 +}; + +static struct pci_visit unconfigure_functions_phase2 = { + .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2, + .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2 +}; + + +int pciehp_configure_device (struct controller* ctrl, struct pci_func* func) +{ + unsigned char bus; + struct pci_dev dev0; + struct pci_bus *child; + struct pci_dev* temp; + int rc = 0; + + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); + + memset(&dev0, 0, sizeof(struct pci_dev)); + + dbg("%s: func->pci_dev %p bus %x dev %x func %x\n", __FUNCTION__, + func->pci_dev, func->bus, func->device, func->function); + if (func->pci_dev == NULL) + func->pci_dev = pci_find_slot(func->bus, (func->device << 3) | (func->function & 0x7)); + dbg("%s: func->pci_dev %p bus %x dev %x func %x\n", __FUNCTION__, + func->pci_dev, func->bus, func->device, func->function); + if (func->pci_dev != NULL) { + dbg("%s: pci_dev %p bus %x devfn %x\n", __FUNCTION__, + func->pci_dev, func->pci_dev->bus->number, func->pci_dev->devfn); + } + /* Still NULL ? Well then scan for it ! */ + if (func->pci_dev == NULL) { + dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__); + dev0.bus = ctrl->pci_dev->subordinate; + dbg("%s: dev0.bus %p\n", __FUNCTION__, dev0.bus); + dev0.bus->number = func->bus; + dbg("%s: dev0.bus->number %x\n", __FUNCTION__, func->bus); + dev0.devfn = PCI_DEVFN(func->device, func->function); + dev0.sysdata = ctrl->pci_dev->sysdata; + + /* This will generate pci_dev structures for all functions, + * but we will only call this case when lookup fails + */ + func->pci_dev = pci_scan_slot(&dev0); + dbg("%s: func->pci_dev %p\n", __FUNCTION__, func->pci_dev); + if (func->pci_dev == NULL) { + dbg("ERROR: pci_dev still null\n"); + return 0; + } + } + + if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); + dbg("%s: bridge device - func->pci_dev->bus %p func->pci_dev %p bus %x\n", + __FUNCTION__, func->pci_dev->bus,func->pci_dev,bus); + pci_do_scan_bus(child); + + } + + temp = func->pci_dev; + + if (temp) { + wrapped_dev.dev = temp; + wrapped_bus.bus = temp->bus; + rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); + } + return rc; +} + + +int pciehp_unconfigure_device(struct pci_func* func) +{ + int rc = 0; + int j; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + + memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); + + dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function); + + for (j=0; j<8 ; j++) { + struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j); + dbg("%s: temp %p\n", __FUNCTION__, temp); + if (temp) { + wrapped_dev.dev = temp; + wrapped_bus.bus = temp->bus; + rc = pci_visit_dev(&unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus); + if (rc) + break; + + rc = pci_visit_dev(&unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus); + if (rc) + break; + } + } + return rc; +} + +/* + * pciehp_set_irq + * + * @bus_num: bus number of PCI device + * @dev_num: device number of PCI device + * @slot: pointer to u8 where slot number will be returned + */ +int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) +{ +#if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64) + int rc; + u16 temp_word; + struct pci_dev fakedev; + struct pci_bus fakebus; + + fakedev.devfn = dev_num << 3; + fakedev.bus = &fakebus; + fakebus.number = bus_num; + dbg("%s: dev %d, bus %d, pin %d, num %d\n", + __FUNCTION__, dev_num, bus_num, int_pin, irq_num); + rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); + dbg("%s: rc %d\n", __FUNCTION__, rc); + if (!rc) + return !rc; + + /* set the Edge Level Control Register (ELCR) */ + temp_word = inb(0x4d0); + temp_word |= inb(0x4d1) << 8; + + temp_word |= 0x01 << irq_num; + + /* This should only be for x86 as it sets the Edge Level Control Register */ + outb((u8) (temp_word & 0xFF), 0x4d0); + outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); +#endif + return 0; +} + +/* More PCI configuration routines; this time centered around hotplug controller */ + + +/* + * pciehp_save_config + * + * Reads configuration for all slots in a PCI bus and saves info. + * + * Note: For non-hot plug busses, the slot # saved is the device # + * + * returns 0 if success + */ +int pciehp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num) +{ + int rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + struct pci_func *new_slot; + int sub_bus; + int max_functions; + int function; + u8 DevError; + int device = 0; + int cloop = 0; + int stop_it; + int index; + int is_hot_plug = num_ctlr_slots || first_device_num; + struct pci_bus lpci_bus, *pci_bus; + int FirstSupported, LastSupported; + + dbg("%s: Enter\n", __FUNCTION__); + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + + dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, num_ctlr_slots, + first_device_num); + + /* Decide which slots are supported */ + if (is_hot_plug) { + /********************************* + * is_hot_plug is the slot mask + *********************************/ + FirstSupported = first_device_num; + LastSupported = FirstSupported + num_ctlr_slots - 1; + } else { + FirstSupported = 0; + LastSupported = 0x1F; + } + + dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, LastSupported); + + /* Save PCI configuration space for all devices in supported slots */ + dbg("%s: pci_bus->number = %x\n", __FUNCTION__, pci_bus->number); + pci_bus->number = busnumber; + dbg("%s: bus = %x, dev = %x\n", __FUNCTION__, busnumber, device); + for (device = FirstSupported; device <= LastSupported; device++) { + ID = 0xFFFFFFFF; + rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + /* dbg("%s: ID = %x\n", __FUNCTION__, ID);*/ + + if (ID != 0xFFFFFFFF) { /* device in slot */ + dbg("%s: ID = %x\n", __FUNCTION__, ID); + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, + &header_type); + if (rc) + return rc; + + dbg("class_code = %x, header_type = %x\n", class_code, header_type); + + /* If multi-function device, set max_functions to 8 */ + if (header_type & 0x80) + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + DevError = 0; + dbg("%s: In do loop\n", __FUNCTION__); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */ + /* Recurse the subordinate bus + * get the subordinate bus number + */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), + PCI_SECONDARY_BUS, &secondary_bus); + if (rc) { + return rc; + } else { + sub_bus = (int) secondary_bus; + + /* Save secondary bus cfg spc with this recursive call. */ + rc = pciehp_save_config(ctrl, sub_bus, 0, 0); + if (rc) + return rc; + } + } + + index = 0; + new_slot = pciehp_slot_find(busnumber, device, index++); + + dbg("%s: new_slot = %p bus %x dev %x fun %x\n", + __FUNCTION__, new_slot, busnumber, device, index-1); + + while (new_slot && (new_slot->function != (u8) function)) { + new_slot = pciehp_slot_find(busnumber, device, index++); + dbg("%s: while loop, new_slot = %p bus %x dev %x fun %x\n", + __FUNCTION__, new_slot, busnumber, device, index-1); + } + if (!new_slot) { + /* Setup slot structure. */ + new_slot = pciehp_slot_create(busnumber); + dbg("%s: if, new_slot = %p bus %x dev %x fun %x\n", + __FUNCTION__, new_slot, busnumber, device, function); + + if (new_slot == NULL) + return(1); + } + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = (u8) function; + new_slot->is_a_board = 1; + new_slot->switch_save = 0x10; + /* In case of unsupported board */ + new_slot->status = DevError; + new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); + dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev); + + for (cloop = 0; cloop < 0x20; cloop++) { + rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot->config_space [cloop])); + /* dbg("new_slot->config_space[%x] = %x\n", cloop, new_slot->config_space[cloop]); */ + if (rc) + return rc; + } + + function++; + + stop_it = 0; + + /* This loop skips to the next present function + * reading in Class Code and Header type. + */ + + while ((function < max_functions)&&(!stop_it)) { + dbg("%s: In while loop \n", __FUNCTION__); + rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { /* Nothing there. */ + function++; + dbg("Nothing there\n"); + } else { /* Something there */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + dbg("class_code = %x, header_type = %x\n", class_code, header_type); + stop_it++; + } + } + + } while (function < max_functions); + } /* End of IF (device in slot?) */ + else if (is_hot_plug) { + /* Setup slot structure with entry for empty slot */ + new_slot = pciehp_slot_create(busnumber); + + if (new_slot == NULL) { + return(1); + } + dbg("new_slot = %p, bus = %x, dev = %x, fun = %x\n", new_slot, + new_slot->bus, new_slot->device, new_slot->function); + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = 0; + new_slot->is_a_board = 0; + new_slot->presence_save = 0; + new_slot->switch_save = 0; + } + /* dbg("%s: End of For loop\n", __FUNCTION__); */ + } /* End of FOR loop */ + + dbg("%s: Exit\n", __FUNCTION__); + return(0); +} + + +/* + * pciehp_save_slot_config + * + * Saves configuration info for all PCI devices in a given slot + * including subordinate busses. + * + * returns 0 if success + */ +int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) +{ + int rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + int sub_bus; + int max_functions; + int function; + int cloop = 0; + int stop_it; + struct pci_bus lpci_bus, *pci_bus; + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = new_slot->bus; + + ID = 0xFFFFFFFF; + + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); + + if (ID != 0xFFFFFFFF) { /* Device in slot */ + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); + + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); + + if (header_type & 0x80) /* Multi-function device */ + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + /* Recurse the subordinate bus */ + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + /* Save the config headers for the secondary bus. */ + rc = pciehp_save_config(ctrl, sub_bus, 0, 0); + + if (rc) + return(rc); + + } /* End of IF */ + + new_slot->status = 0; + + for (cloop = 0; cloop < 0x20; cloop++) { + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot->config_space [cloop])); + } + + function++; + + stop_it = 0; + + /* this loop skips to the next present function + * reading in the Class Code and the Header type. + */ + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { /* Nothing there. */ + function++; + } else { /* Something there */ + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); + + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); + + stop_it++; + } + } + + } while (function < max_functions); + } /* End of IF (device in slot?) */ + else { + return(2); + } + + return(0); +} + + +/* + * pciehp_save_used_resources + * + * Stores used resource information for existing boards. this is + * for boards that were in the system when this driver was loaded. + * this function is for hot plug ADD + * + * returns 0 if success + * if disable == 1(DISABLE_CARD), + * it loops for all functions of the slot and disables them. + * else, it just get resources of the function and return. + */ +int pciehp_save_used_resources (struct controller *ctrl, struct pci_func *func, int disable) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 temp_byte; + u16 command; + u16 save_command; + u16 w_base, w_length; + u32 temp_register; + u32 save_base; + u32 base, length; + u64 base64 = 0; + int index = 0; + unsigned int devfn; + struct pci_resource *mem_node = NULL; + struct pci_resource *p_mem_node = NULL; + struct pci_resource *t_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_bus lpci_bus, *pci_bus; + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + + if (disable) + func = pciehp_slot_find(func->bus, func->device, index++); + + while ((func != NULL) && func->is_a_board) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + /* Save the command register */ + pci_bus_read_config_word (pci_bus, devfn, PCI_COMMAND, &save_command); + + if (disable) { + /* Disable card */ + command = 0x00; + pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + } + + /* Check for Bridge */ + pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command); + if (disable) { + /* Clear Bridge Control Register */ + command = 0x00; + pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); + } + + pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + pci_bus_read_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); + + bus_node =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!bus_node) + return -ENOMEM; + + bus_node->base = (ulong)secondary_bus; + bus_node->length = (ulong)(temp_byte - secondary_bus + 1); + + bus_node->next = func->bus_head; + func->bus_head = bus_node; + + /* Save IO base and Limit registers */ + pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_BASE, &temp_byte); + base = temp_byte; + pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); + length = temp_byte; + + if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; + io_node->length = (ulong)(length - base + 0x10) << 8; + + io_node->next = func->io_head; + func->io_head = io_node; + } + + /* Save memory base and Limit registers */ + pci_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_BASE, &w_base); + pci_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = (ulong)w_base << 16; + mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + /* Save prefetchable memory base and Limit registers */ + pci_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); + pci_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = (ulong)w_base << 16; + p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } + } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command); + + /* Figure out IO and memory base lengths */ + for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { + pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); + + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register); + + if (!disable) { + pci_bus_write_config_dword (pci_bus, devfn, cloop, save_base); + } + + if (!temp_register) + continue; + + base = temp_register; + + if ((base & PCI_BASE_ADDRESS_SPACE_IO) && (!disable || (save_command & PCI_COMMAND_IO))) { + /* IO base */ + /* Set temp_register = amount of IO space requested */ + base = base & 0xFFFFFFFCL; + base = (~base) + 1; + + io_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; + io_node->length = (ulong)base; + dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", io_node->base, io_node->length); + + io_node->next = func->io_head; + func->io_head = io_node; + } else { /* Map Memory */ + int prefetchable = 1; + /* struct pci_resources **res_node; */ + char *res_type_str = "PMEM"; + u32 temp_register2; + + t_mem_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL); + if (!t_mem_node) + return -ENOMEM; + + if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { + prefetchable = 0; + mem_node = t_mem_node; + res_type_str++; + } else + p_mem_node = t_mem_node; + + base = base & 0xFFFFFFF0L; + base = (~base) + 1; + + switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { + case PCI_BASE_ADDRESS_MEM_TYPE_32: + if (prefetchable) { + p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; + p_mem_node->length = (ulong)base; + dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str, p_mem_node->base, p_mem_node->length); + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else { + mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; + mem_node->length = (ulong)base; + dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str, mem_node->base, mem_node->length); + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + break; + case PCI_BASE_ADDRESS_MEM_TYPE_64: + pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); + base64 = temp_register2; + base64 = (base64 << 32) | save_base; + + if (temp_register2) { + dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", res_type_str, temp_register2, (u32)base64); + base64 &= 0x00000000FFFFFFFFL; + } + + if (prefetchable) { + p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; + p_mem_node->length = base; + dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str, p_mem_node->base, p_mem_node->length); + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else { + mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; + mem_node->length = base; + dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str, mem_node->base, mem_node->length); + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + cloop += 4; + break; + default: + dbg("asur: reserved BAR type=0x%x\n", temp_register); + break; + } + } + } /* End of base register loop */ + } else { /* Some other unknown header type */ + dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", func->bus, func->device); + } + + /* Find the next device in this slot */ + if (!disable) + break; + func = pciehp_slot_find(func->bus, func->device, index++); + } + + return(0); +} + + +/* + * pciehp_return_board_resources + * + * this routine returns all resources allocated to a board to + * the available pool. + * + * returns 0 if success + */ +int pciehp_return_board_resources(struct pci_func * func, struct resource_lists * resources) +{ + int rc = 0; + struct pci_resource *node; + struct pci_resource *t_node; + dbg("%s\n", __FUNCTION__); + + if (!func) + return(1); + + node = func->io_head; + func->io_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->io_head), node); + node = t_node; + } + + node = func->mem_head; + func->mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->mem_head), node); + node = t_node; + } + + node = func->p_mem_head; + func->p_mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->p_mem_head), node); + node = t_node; + } + + node = func->bus_head; + func->bus_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->bus_head), node); + node = t_node; + } + + rc |= pciehp_resource_sort_and_combine(&(resources->mem_head)); + rc |= pciehp_resource_sort_and_combine(&(resources->p_mem_head)); + rc |= pciehp_resource_sort_and_combine(&(resources->io_head)); + rc |= pciehp_resource_sort_and_combine(&(resources->bus_head)); + + return(rc); +} + + +/* + * pciehp_destroy_resource_list + * + * Puts node back in the resource list pointed to by head + */ +void pciehp_destroy_resource_list (struct resource_lists * resources) +{ + struct pci_resource *res, *tres; + + res = resources->io_head; + resources->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->mem_head; + resources->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->p_mem_head; + resources->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->bus_head; + resources->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + + +/* + * pciehp_destroy_board_resources + * + * Puts node back in the resource list pointed to by head + */ +void pciehp_destroy_board_resources (struct pci_func * func) +{ + struct pci_resource *res, *tres; + + res = func->io_head; + func->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->mem_head; + func->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->p_mem_head; + func->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->bus_head; + func->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + diff -urN linux-2.4.26/drivers/hotplug/pciehprm.h linux-2.4.27/drivers/hotplug/pciehprm.h --- linux-2.4.26/drivers/hotplug/pciehprm.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehprm.h 2004-08-07 16:26:04.753350845 -0700 @@ -0,0 +1,54 @@ +/* + * PCIEHPRM : PCIEHP Resource Manager for ACPI/non-ACPI platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#ifndef _PCIEHPRM_H_ +#define _PCIEHPRM_H_ + +#ifdef CONFIG_HOTPLUG_PCI_PCIE_PHPRM_NONACPI +#include "pciehprm_nonacpi.h" +#endif + +int pciehprm_init(enum php_ctlr_type ct); +void pciehprm_cleanup(void); +int pciehprm_print_pirt(void); +void *pciehprm_get_slot(struct slot *slot); +int pciehprm_find_available_resources(struct controller *ctrl); +int pciehprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type); +void pciehprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type); +int pciehprm_get_interrupt(int seg, int bus, int device, int pin, int *irq); + +#ifdef DEBUG +#define RES_CHECK(this, bits) \ + { if (((this) & (bits - 1))) \ + printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); } +#else +#define RES_CHECK(this, bits) +#endif + +#endif /* _PCIEHPRM_H_ */ diff -urN linux-2.4.26/drivers/hotplug/pciehprm_acpi.c linux-2.4.27/drivers/hotplug/pciehprm_acpi.c --- linux-2.4.26/drivers/hotplug/pciehprm_acpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehprm_acpi.c 2004-08-07 16:26:04.756350969 -0700 @@ -0,0 +1,1696 @@ +/* + * PCIEHPRM ACPI: PHP Resource Manager for ACPI platform + * + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_IA64 +#include +#endif +#include +#include +#include +#include "pciehp.h" +#include "pciehprm.h" + +#define PCI_MAX_BUS 0x100 +#define ACPI_STA_DEVICE_PRESENT 0x01 + +#define METHOD_NAME__SUN "_SUN" +#define METHOD_NAME__HPP "_HPP" +#define METHOD_NAME_OSHP "OSHP" + +#define PHP_RES_BUS 0xA0 +#define PHP_RES_IO 0xA1 +#define PHP_RES_MEM 0xA2 +#define PHP_RES_PMEM 0xA3 + +#define BRIDGE_TYPE_P2P 0x00 +#define BRIDGE_TYPE_HOST 0x01 + +/* this should go to drivers/acpi/include/ */ +struct acpi__hpp { + u8 cache_line_size; + u8 latency_timer; + u8 enable_serr; + u8 enable_perr; +}; + +struct acpi_php_slot { + struct acpi_php_slot *next; + struct acpi_bridge *bridge; + acpi_handle handle; + int seg; + int bus; + int dev; + int fun; + u32 sun; + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + void *slot_ops; /* _STA, _EJx, etc */ + struct slot *slot; +}; /* per func */ + +struct acpi_bridge { + struct acpi_bridge *parent; + struct acpi_bridge *next; + struct acpi_bridge *child; + acpi_handle handle; + int seg; + int pbus; /* pdev->bus->number */ + int pdevice; /* PCI_SLOT(pdev->devfn) */ + int pfunction; /* PCI_DEVFN(pdev->devfn) */ + int bus; /* pdev->subordinate->number */ + struct acpi__hpp *_hpp; + struct acpi_php_slot *slots; + struct pci_resource *tmem_head; /* total from crs */ + struct pci_resource *tp_mem_head; /* total from crs */ + struct pci_resource *tio_head; /* total from crs */ + struct pci_resource *tbus_head; /* total from crs */ + struct pci_resource *mem_head; /* available */ + struct pci_resource *p_mem_head; /* available */ + struct pci_resource *io_head; /* available */ + struct pci_resource *bus_head; /* available */ + int scanned; + int type; +}; + +static struct acpi_bridge *acpi_bridges_head; + +static u8 * acpi_path_name( acpi_handle handle) +{ + acpi_status status; + static u8 path_name[ACPI_PATHNAME_MAX]; + struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name }; + + memset(path_name, 0, sizeof (path_name)); + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf); + + if (ACPI_FAILURE(status)) + return NULL; + else + return path_name; +} + +static void acpi_get__hpp ( struct acpi_bridge *ab); +static void acpi_run_oshp ( struct acpi_bridge *ab); + +static int acpi_add_slot_to_php_slots( + struct acpi_bridge *ab, + int bus_num, + acpi_handle handle, + u32 adr, + u32 sun + ) +{ + struct acpi_php_slot *aps; + static long samesun = -1; + + aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL); + if (!aps) { + err ("acpi_pciehprm: alloc for aps fail\n"); + return -1; + } + memset(aps, 0, sizeof(struct acpi_php_slot)); + + aps->handle = handle; + aps->bus = bus_num; + aps->dev = (adr >> 16) & 0xffff; + aps->fun = adr & 0xffff; + aps->sun = sun; + + aps->next = ab->slots; /* cling to the bridge */ + aps->bridge = ab; + ab->slots = aps; + + ab->scanned += 1; + if (!ab->_hpp) + acpi_get__hpp(ab); + + acpi_run_oshp(ab); + if (sun != samesun) { + info("acpi_pciehprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", + aps->sun, ab->seg, aps->bus, aps->dev, aps->fun); + samesun = sun; + } + return 0; +} + +static void acpi_get__hpp ( struct acpi_bridge *ab) +{ + acpi_status status; + u8 nui[4]; + struct acpi_buffer ret_buf = { 0, NULL}; + union acpi_object *ext_obj, *package; + u8 *path_name = acpi_path_name(ab->handle); + int i, len = 0; + + /* Get _hpp */ + status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); + switch (status) { + case AE_BUFFER_OVERFLOW: + ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); + if (!ret_buf.pointer) { + err ("acpi_pciehprm:%s alloc for _HPP fail\n", path_name); + return; + } + status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); + if (ACPI_SUCCESS(status)) + break; + default: + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s _HPP fail=0x%x\n", path_name, status); + return; + } + } + + ext_obj = (union acpi_object *) ret_buf.pointer; + if (ext_obj->type != ACPI_TYPE_PACKAGE) { + err ("acpi_pciehprm:%s _HPP obj not a package\n", path_name); + goto free_and_return; + } + + len = ext_obj->package.count; + package = (union acpi_object *) ret_buf.pointer; + for ( i = 0; (i < len) || (i < 4); i++) { + ext_obj = (union acpi_object *) &package->package.elements[i]; + switch (ext_obj->type) { + case ACPI_TYPE_INTEGER: + nui[i] = (u8)ext_obj->integer.value; + break; + default: + err ("acpi_pciehprm:%s _HPP obj type incorrect\n", path_name); + goto free_and_return; + } + } + + ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL); + memset(ab->_hpp, 0, sizeof(struct acpi__hpp)); + + ab->_hpp->cache_line_size = nui[0]; + ab->_hpp->latency_timer = nui[1]; + ab->_hpp->enable_serr = nui[2]; + ab->_hpp->enable_perr = nui[3]; + + dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); + dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); + dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); + dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); + +free_and_return: + kfree(ret_buf.pointer); +} +static void acpi_run_oshp ( struct acpi_bridge *ab) +{ + acpi_status status; + u8 *path_name = acpi_path_name(ab->handle); + struct acpi_buffer ret_buf = { 0, NULL}; + + /* Run OSHP */ + status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, &ret_buf); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status); + } else + dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status); + return; +} + +static acpi_status acpi_evaluate_crs( + acpi_handle handle, + struct acpi_resource **retbuf + ) +{ + acpi_status status; + struct acpi_buffer crsbuf; + u8 *path_name = acpi_path_name(handle); + + crsbuf.length = 0; + crsbuf.pointer = NULL; + + status = acpi_get_current_resources (handle, &crsbuf); + + switch (status) { + case AE_BUFFER_OVERFLOW: + break; /* Found */ + case AE_NOT_FOUND: + dbg("acpi_pciehprm:%s _CRS not found\n", path_name); + return status; + default: + err ("acpi_pciehprm:%s _CRS fail=0x%x\n", path_name, status); + return status; + } + + crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL); + if (!crsbuf.pointer) { + err ("acpi_pciehprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name); + return AE_NO_MEMORY; + } + + status = acpi_get_current_resources (handle, &crsbuf); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm: %s _CRS fail=0x%x.\n", path_name, status); + kfree(crsbuf.pointer); + return status; + } + + *retbuf = crsbuf.pointer; + + return status; +} + +static void free_pci_resource ( struct pci_resource *aprh) +{ + struct pci_resource *res, *next; + + for (res = aprh; res; res = next) { + next = res->next; + kfree(res); + } +} + +static void print_pci_resource ( struct pci_resource *aprh) +{ + struct pci_resource *res; + + for (res = aprh; res; res = res->next) + dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); +} + +static void print_slot_resources( struct acpi_php_slot *aps) +{ + if (aps->bus_head) { + dbg(" BUS Resources:\n"); + print_pci_resource (aps->bus_head); + } + + if (aps->io_head) { + dbg(" IO Resources:\n"); + print_pci_resource (aps->io_head); + } + + if (aps->mem_head) { + dbg(" MEM Resources:\n"); + print_pci_resource (aps->mem_head); + } + + if (aps->p_mem_head) { + dbg(" PMEM Resources:\n"); + print_pci_resource (aps->p_mem_head); + } +} + +static void print_pci_resources( struct acpi_bridge *ab) +{ + if (ab->tbus_head) { + dbg(" Total BUS Resources:\n"); + print_pci_resource (ab->tbus_head); + } + if (ab->bus_head) { + dbg(" BUS Resources:\n"); + print_pci_resource (ab->bus_head); + } + + if (ab->tio_head) { + dbg(" Total IO Resources:\n"); + print_pci_resource (ab->tio_head); + } + if (ab->io_head) { + dbg(" IO Resources:\n"); + print_pci_resource (ab->io_head); + } + + if (ab->tmem_head) { + dbg(" Total MEM Resources:\n"); + print_pci_resource (ab->tmem_head); + } + if (ab->mem_head) { + dbg(" MEM Resources:\n"); + print_pci_resource (ab->mem_head); + } + + if (ab->tp_mem_head) { + dbg(" Total PMEM Resources:\n"); + print_pci_resource (ab->tp_mem_head); + } + if (ab->p_mem_head) { + dbg(" PMEM Resources:\n"); + print_pci_resource (ab->p_mem_head); + } + if (ab->_hpp) { + dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); + dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); + dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); + dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); + } +} + +static int pciehprm_delete_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + struct pci_resource *prevnode; + struct pci_resource *split_node; + ulong tbase; + + pciehp_resource_sort_and_combine(aprh); + + for (res = *aprh; res; res = res->next) { + if (res->base > base) + continue; + + if ((res->base + res->length) < (base + size)) + continue; + + if (res->base < base) { + tbase = base; + + if ((res->length - (tbase - res->base)) < size) + continue; + + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base; + split_node->length = tbase - res->base; + res->base = tbase; + res->length -= split_node->length; + + split_node->next = res->next; + res->next = split_node; + } + + if (res->length >= size) { + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base + size; + split_node->length = res->length - size; + res->length = size; + + split_node->next = res->next; + res->next = split_node; + } + + if (*aprh == res) { + *aprh = res->next; + } else { + prevnode = *aprh; + while (prevnode->next != res) + prevnode = prevnode->next; + + prevnode->next = res->next; + } + res->next = NULL; + kfree(res); + break; + } + + return 0; +} + +static int pciehprm_delete_resources( + struct pci_resource **aprh, + struct pci_resource *this + ) +{ + struct pci_resource *res; + + for (res = this; res; res = res->next) + pciehprm_delete_resource(aprh, res->base, res->length); + + return 0; +} + +static int pciehprm_add_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + + for (res = *aprh; res; res = res->next) { + if ((res->base + res->length) == base) { + res->length += size; + size = 0L; + break; + } + if (res->next == *aprh) + break; + } + + if (size) { + res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!res) { + err ("acpi_pciehprm: alloc for res fail\n"); + return -ENOMEM; + } + memset(res, 0, sizeof (struct pci_resource)); + + res->base = base; + res->length = size; + res->next = *aprh; + *aprh = res; + } + + return 0; +} + +static int pciehprm_add_resources( + struct pci_resource **aprh, + struct pci_resource *this + ) +{ + struct pci_resource *res; + int rc = 0; + + for (res = this; res && !rc; res = res->next) + rc = pciehprm_add_resource(aprh, res->base, res->length); + + return rc; +} + +static void acpi_parse_io ( + struct acpi_bridge *ab, + union acpi_resource_data *data + ) +{ + struct acpi_resource_io *dataio; + dataio = (struct acpi_resource_io *) data; + + dbg("Io Resource\n"); + dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10); + dbg(" Range minimum base: %08X\n", dataio->min_base_address); + dbg(" Range maximum base: %08X\n", dataio->max_base_address); + dbg(" Alignment: %08X\n", dataio->alignment); + dbg(" Range Length: %08X\n", dataio->range_length); +} + +static void acpi_parse_fixed_io ( + struct acpi_bridge *ab, + union acpi_resource_data *data + ) +{ + struct acpi_resource_fixed_io *datafio; + datafio = (struct acpi_resource_fixed_io *) data; + + dbg("Fixed Io Resource\n"); + dbg(" Range base address: %08X", datafio->base_address); + dbg(" Range length: %08X", datafio->range_length); +} + +static void acpi_parse_address16_32 ( + struct acpi_bridge *ab, + union acpi_resource_data *data, + acpi_resource_type id + ) +{ + /* + * acpi_resource_address16 == acpi_resource_address32 + * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data; + */ + struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data; + struct pci_resource **aprh, **tprh; + + if (id == ACPI_RSTYPE_ADDRESS16) + dbg("acpi_pciehprm:16-Bit Address Space Resource\n"); + else + dbg("acpi_pciehprm:32-Bit Address Space Resource\n"); + + switch (data32->resource_type) { + case ACPI_MEMORY_RANGE: + dbg(" Resource Type: Memory Range\n"); + aprh = &ab->mem_head; + tprh = &ab->tmem_head; + + switch (data32->attribute.memory.cache_attribute) { + case ACPI_NON_CACHEABLE_MEMORY: + dbg(" Type Specific: Noncacheable memory\n"); + break; + case ACPI_CACHABLE_MEMORY: + dbg(" Type Specific: Cacheable memory\n"); + break; + case ACPI_WRITE_COMBINING_MEMORY: + dbg(" Type Specific: Write-combining memory\n"); + break; + case ACPI_PREFETCHABLE_MEMORY: + aprh = &ab->p_mem_head; + dbg(" Type Specific: Prefetchable memory\n"); + break; + default: + dbg(" Type Specific: Invalid cache attribute\n"); + break; + } + + dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only"); + break; + + case ACPI_IO_RANGE: + dbg(" Resource Type: I/O Range\n"); + aprh = &ab->io_head; + tprh = &ab->tio_head; + + switch (data32->attribute.io.range_attribute) { + case ACPI_NON_ISA_ONLY_RANGES: + dbg(" Type Specific: Non-ISA Io Addresses\n"); + break; + case ACPI_ISA_ONLY_RANGES: + dbg(" Type Specific: ISA Io Addresses\n"); + break; + case ACPI_ENTIRE_RANGE: + dbg(" Type Specific: ISA and non-ISA Io Addresses\n"); + break; + default: + dbg(" Type Specific: Invalid range attribute\n"); + break; + } + break; + + case ACPI_BUS_NUMBER_RANGE: + dbg(" Resource Type: Bus Number Range(fixed)\n"); + /* Fixup to be compatible with the rest of php driver */ + data32->min_address_range++; + data32->address_length--; + aprh = &ab->bus_head; + tprh = &ab->tbus_head; + break; + default: + dbg(" Resource Type: Invalid resource type. Exiting.\n"); + return; + } + + dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer"); + dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive"); + dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not"); + dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not"); + dbg(" Granularity: %08X\n", data32->granularity); + dbg(" Address range min: %08X\n", data32->min_address_range); + dbg(" Address range max: %08X\n", data32->max_address_range); + dbg(" Address translation offset: %08X\n", data32->address_translation_offset); + dbg(" Address Length: %08X\n", data32->address_length); + + if (0xFF != data32->resource_source.index) { + dbg(" Resource Source Index: %X\n", data32->resource_source.index); + /* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */ + } + + pciehprm_add_resource(aprh, data32->min_address_range, data32->address_length); +} + +static acpi_status acpi_parse_crs( + struct acpi_bridge *ab, + struct acpi_resource *crsbuf + ) +{ + acpi_status status = AE_OK; + struct acpi_resource *resource = crsbuf; + u8 count = 0; + u8 done = 0; + + while (!done) { + dbg("acpi_pciehprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++); + switch (resource->id) { + case ACPI_RSTYPE_IRQ: + dbg("Irq -------- Resource\n"); + break; + case ACPI_RSTYPE_DMA: + dbg("DMA -------- Resource\n"); + break; + case ACPI_RSTYPE_START_DPF: + dbg("Start DPF -------- Resource\n"); + break; + case ACPI_RSTYPE_END_DPF: + dbg("End DPF -------- Resource\n"); + break; + case ACPI_RSTYPE_IO: + acpi_parse_io (ab, &resource->data); + break; + case ACPI_RSTYPE_FIXED_IO: + acpi_parse_fixed_io (ab, &resource->data); + break; + case ACPI_RSTYPE_VENDOR: + dbg("Vendor -------- Resource\n"); + break; + case ACPI_RSTYPE_END_TAG: + dbg("End_tag -------- Resource\n"); + done = 1; + break; + case ACPI_RSTYPE_MEM24: + dbg("Mem24 -------- Resource\n"); + break; + case ACPI_RSTYPE_MEM32: + dbg("Mem32 -------- Resource\n"); + break; + case ACPI_RSTYPE_FIXED_MEM32: + dbg("Fixed Mem32 -------- Resource\n"); + break; + case ACPI_RSTYPE_ADDRESS16: + acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16); + break; + case ACPI_RSTYPE_ADDRESS32: + acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32); + break; + case ACPI_RSTYPE_ADDRESS64: + info("Address64 -------- Resource unparsed\n"); + break; + case ACPI_RSTYPE_EXT_IRQ: + dbg("Ext Irq -------- Resource\n"); + break; + default: + dbg("Invalid -------- resource type 0x%x\n", resource->id); + break; + } + + resource = (struct acpi_resource *) ((char *)resource + resource->length); + } + + return status; +} + +static acpi_status acpi_get_crs( struct acpi_bridge *ab) +{ + acpi_status status; + struct acpi_resource *crsbuf; + + status = acpi_evaluate_crs(ab->handle, &crsbuf); + if (ACPI_SUCCESS(status)) { + status = acpi_parse_crs(ab, crsbuf); + kfree(crsbuf); + + pciehp_resource_sort_and_combine(&ab->bus_head); + pciehp_resource_sort_and_combine(&ab->io_head); + pciehp_resource_sort_and_combine(&ab->mem_head); + pciehp_resource_sort_and_combine(&ab->p_mem_head); + + pciehprm_add_resources (&ab->tbus_head, ab->bus_head); + pciehprm_add_resources (&ab->tio_head, ab->io_head); + pciehprm_add_resources (&ab->tmem_head, ab->mem_head); + pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); + } + + return status; +} + +/* find acpi_bridge downword from ab. */ +static struct acpi_bridge * +find_acpi_bridge_by_bus( + struct acpi_bridge *ab, + int seg, + int bus /* pdev->subordinate->number */ + ) +{ + struct acpi_bridge *lab = NULL; + + if (!ab) + return NULL; + + if ((ab->bus == bus) && (ab->seg == seg)) + return ab; + + if (ab->child) + lab = find_acpi_bridge_by_bus(ab->child, seg, bus); + + if (!lab) + if (ab->next) + lab = find_acpi_bridge_by_bus(ab->next, seg, bus); + + return lab; +} + +/* + * Build a device tree of ACPI PCI Bridges + */ +static void pciehprm_acpi_register_a_bridge ( + struct acpi_bridge **head, + struct acpi_bridge *pab, /* parent bridge to which child bridge is added */ + struct acpi_bridge *cab /* child bridge to add */ + ) +{ + struct acpi_bridge *lpab; + struct acpi_bridge *lcab; + + lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus); + if (!lpab) { + if (!(pab->type & BRIDGE_TYPE_HOST)) + warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus); + pab->next = *head; + *head = pab; + lpab = pab; + } + + if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab)) + return; + + lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus); + if (lcab) { + if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus)) + err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus); + return; + } else + lcab = cab; + + lcab->parent = lpab; + lcab->next = lpab->child; + lpab->child = lcab; +} + +static acpi_status pciehprm_acpi_build_php_slots_callback( + acpi_handle handle, + u32 Level, + void *context, + void **retval + ) +{ + ulong bus_num; + ulong seg_num; + ulong sun, adr; + ulong padr = 0; + acpi_handle phandle = NULL; + struct acpi_bridge *pab = (struct acpi_bridge *)context; + struct acpi_bridge *lab; + acpi_status status; + u8 *path_name = acpi_path_name(handle); + + /* Get _SUN */ + status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun); + switch(status) { + case AE_NOT_FOUND: + return AE_OK; + default: + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s _SUN fail=0x%x\n", path_name, status); + return status; + } + } + + /* Get _ADR. _ADR must exist if _SUN exists */ + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status); + return status; + } + + dbg("acpi_pciehprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr); + + status = acpi_get_parent(handle, &phandle); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s get_parent fail=0x%x\n", path_name, status); + return (status); + } + + bus_num = pab->bus; + seg_num = pab->seg; + + if (pab->bus == bus_num) { + lab = pab; + } else { + dbg("WARN: pab is not parent\n"); + lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num); + if (!lab) { + dbg("acpi_pciehprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); + lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL); + if (!lab) { + err("acpi_pciehprm: alloc for ab fail\n"); + return AE_NO_MEMORY; + } + memset(lab, 0, sizeof(struct acpi_bridge)); + + lab->handle = phandle; + lab->pbus = pab->bus; + lab->pdevice = (int)(padr >> 16) & 0xffff; + lab->pfunction = (int)(padr & 0xffff); + lab->bus = (int)bus_num; + lab->scanned = 0; + lab->type = BRIDGE_TYPE_P2P; + + pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab); + } else + dbg("acpi_pciehprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); + } + + acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun); + + return (status); +} + +static int pciehprm_acpi_build_php_slots( + struct acpi_bridge *ab, + u32 depth + ) +{ + acpi_status status; + u8 *path_name = acpi_path_name(ab->handle); + + /* Walk down this pci bridge to get _SUNs if any behind P2P */ + status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, + ab->handle, + depth, + pciehprm_acpi_build_php_slots_callback, + ab, + NULL ); + if (ACPI_FAILURE(status)) { + dbg("acpi_pciehprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", + path_name, ab->seg, ab->bus, status); + return -1; + } + + return 0; +} + +static void build_a_bridge( + struct acpi_bridge *pab, + struct acpi_bridge *ab + ) +{ + u8 *path_name = acpi_path_name(ab->handle); + + pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab); + + switch (ab->type) { + case BRIDGE_TYPE_HOST: + dbg("acpi_pciehprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", + ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); + break; + case BRIDGE_TYPE_P2P: + dbg("acpi_pciehprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", + ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); + break; + }; + + /* build any immediate PHP slots under this pci bridge */ + pciehprm_acpi_build_php_slots(ab, 1); +} + +static struct acpi_bridge * add_p2p_bridge( + acpi_handle handle, + struct acpi_bridge *pab, /* parent */ + ulong adr + ) +{ + struct acpi_bridge *ab; + struct pci_dev *pdev; + ulong devnum, funcnum; + u8 *path_name = acpi_path_name(handle); + + ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); + if (!ab) { + err("acpi_pciehprm: alloc for ab fail\n"); + return NULL; + } + memset(ab, 0, sizeof(struct acpi_bridge)); + + devnum = (adr >> 16) & 0xffff; + funcnum = adr & 0xffff; + + pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); + if (!pdev || !pdev->subordinate) { + err("acpi_pciehprm:%s is not a P2P Bridge\n", path_name); + kfree(ab); + return NULL; + } + + ab->handle = handle; + ab->seg = pab->seg; + ab->pbus = pab->bus; /* or pdev->bus->number */ + ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */ + ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */ + ab->bus = pdev->subordinate->number; + ab->scanned = 0; + ab->type = BRIDGE_TYPE_P2P; + + dbg("acpi_pciehprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n", + pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pab->bus, (u32)devnum, (u32)funcnum, path_name); + + build_a_bridge(pab, ab); + + return ab; +} + +static acpi_status scan_p2p_bridge( + acpi_handle handle, + u32 Level, + void *context, + void **retval + ) +{ + struct acpi_bridge *pab = (struct acpi_bridge *)context; + struct acpi_bridge *ab; + acpi_status status; + ulong adr = 0; + u8 *path_name = acpi_path_name(handle); + ulong devnum, funcnum; + struct pci_dev *pdev; + + /* Get device, function */ + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status); + return AE_OK; + } + + devnum = (adr >> 16) & 0xffff; + funcnum = adr & 0xffff; + + pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); + if (!pdev) + return AE_OK; + if (!pdev->subordinate) + return AE_OK; + + ab = add_p2p_bridge(handle, pab, adr); + if (ab) { + status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, + handle, + (u32)1, + scan_p2p_bridge, + ab, + NULL); + if (ACPI_FAILURE(status)) + dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status); + } + + return AE_OK; +} + +static struct acpi_bridge * add_host_bridge( + acpi_handle handle, + ulong segnum, + ulong busnum + ) +{ + ulong adr = 0; + acpi_status status; + struct acpi_bridge *ab; + u8 *path_name = acpi_path_name(handle); + + /* get device, function: host br adr is always 0000 though. */ + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status); + return NULL; + } + dbg("acpi_pciehprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, + (u32)busnum, (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name); + + ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); + if (!ab) { + err("acpi_pciehprm: alloc for ab fail\n"); + return NULL; + } + memset(ab, 0, sizeof(struct acpi_bridge)); + + ab->handle = handle; + ab->seg = (int)segnum; + ab->bus = ab->pbus = (int)busnum; + ab->pdevice = (int)(adr >> 16) & 0xffff; + ab->pfunction = (int)(adr & 0xffff); + ab->scanned = 0; + ab->type = BRIDGE_TYPE_HOST; + + /* get root pci bridge's current resources */ + status = acpi_get_crs(ab); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s evaluate _CRS fail=0x%x\n", path_name, status); + kfree(ab); + return NULL; + } + build_a_bridge(ab, ab); + + return ab; +} + +static acpi_status acpi_scan_from_root_pci_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval + ) +{ + ulong segnum = 0; + ulong busnum = 0; + acpi_status status; + struct acpi_bridge *ab; + u8 *path_name = acpi_path_name(handle); + + /* Get bus number of this pci root bridge */ + status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + err("acpi_pciehprm:%s evaluate _SEG fail=0x%x\n", path_name, status); + return status; + } + segnum = 0; + } + + /* Get bus number of this pci root bridge */ + status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s evaluate _BBN fail=0x%x\n", path_name, status); + return (status); + } + + ab = add_host_bridge(handle, segnum, busnum); + if (ab) { + status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, + handle, + 1, + scan_p2p_bridge, + ab, + NULL); + if (ACPI_FAILURE(status)) + dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status); + } + + return AE_OK; +} + +static int pciehprm_acpi_scan_pci (void) +{ + acpi_status status; + + /* + * TBD: traverse LDM device tree with the help of + * unified ACPI augmented for php device population. + */ + status = acpi_get_devices ( PCI_ROOT_HID_STRING, + acpi_scan_from_root_pci_callback, + NULL, + NULL ); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:get_device PCI ROOT HID fail=0x%x\n", status); + return -1; + } + + return 0; +} + +int pciehprm_init(enum php_ctlr_type ctlr_type) +{ + int rc; + + if (ctlr_type != PCI) + return -ENODEV; + + dbg("pciehprm ACPI init \n"); + acpi_bridges_head = NULL; + + /* Construct PCI bus:device tree of acpi_handles */ + rc = pciehprm_acpi_scan_pci(); + if (rc) + return rc; + + dbg("pciehprm ACPI init %s\n", (rc)?"fail":"success"); + return rc; +} + +static void free_a_slot(struct acpi_php_slot *aps) +{ + dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun); + + free_pci_resource (aps->io_head); + free_pci_resource (aps->bus_head); + free_pci_resource (aps->mem_head); + free_pci_resource (aps->p_mem_head); + + kfree(aps); +} + +static void free_a_bridge( struct acpi_bridge *ab) +{ + struct acpi_php_slot *aps, *next; + + switch (ab->type) { + case BRIDGE_TYPE_HOST: + dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", + ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); + break; + case BRIDGE_TYPE_P2P: + dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", + ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); + break; + }; + + /* Free slots first */ + for (aps = ab->slots; aps; aps = next) { + next = aps->next; + free_a_slot(aps); + } + + free_pci_resource (ab->io_head); + free_pci_resource (ab->tio_head); + free_pci_resource (ab->bus_head); + free_pci_resource (ab->tbus_head); + free_pci_resource (ab->mem_head); + free_pci_resource (ab->tmem_head); + free_pci_resource (ab->p_mem_head); + free_pci_resource (ab->tp_mem_head); + + kfree(ab); +} + +static void pciehprm_free_bridges ( struct acpi_bridge *ab) +{ + if (!ab) + return; + + if (ab->child) + pciehprm_free_bridges (ab->child); + + if (ab->next) + pciehprm_free_bridges (ab->next); + + free_a_bridge(ab); +} + +void pciehprm_cleanup(void) +{ + pciehprm_free_bridges (acpi_bridges_head); +} + +static int get_number_of_slots ( + struct acpi_bridge *ab, + int selfonly + ) +{ + struct acpi_php_slot *aps; + int prev_slot = -1; + int slot_num = 0; + + for ( aps = ab->slots; aps; aps = aps->next) + if (aps->dev != prev_slot) { + prev_slot = aps->dev; + slot_num++; + } + + if (ab->child) + slot_num += get_number_of_slots (ab->child, 0); + + if (selfonly) + return slot_num; + + if (ab->next) + slot_num += get_number_of_slots (ab->next, 0); + + return slot_num; +} + +static int print_acpi_resources (struct acpi_bridge *ab) +{ + struct acpi_php_slot *aps; + int i; + + switch (ab->type) { + case BRIDGE_TYPE_HOST: + dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle)); + break; + case BRIDGE_TYPE_P2P: + dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle)); + break; + }; + + print_pci_resources (ab); + + for ( i = -1, aps = ab->slots; aps; aps = aps->next) { + if (aps->dev == i) + continue; + dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, + aps->dev, aps->fun); + print_slot_resources(aps); + i = aps->dev; + } + + if (ab->child) + print_acpi_resources (ab->child); + + if (ab->next) + print_acpi_resources (ab->next); + + return 0; +} + +int pciehprm_print_pirt(void) +{ + dbg("PCIEHPRM ACPI Slots\n"); + if (acpi_bridges_head) + print_acpi_resources (acpi_bridges_head); + + return 0; +} + +static struct acpi_php_slot * get_acpi_slot ( + struct acpi_bridge *ab, + u32 sun + ) +{ + struct acpi_php_slot *aps = NULL; + + for ( aps = ab->slots; aps; aps = aps->next) + if (aps->sun == sun) + return aps; + + if (!aps && ab->child) { + aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun); + if (aps) + return aps; + } + + if (!aps && ab->next) { + aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun); + if (aps) + return aps; + } + + return aps; + +} + +void * pciehprm_get_slot(struct slot *slot) +{ + struct acpi_bridge *ab = acpi_bridges_head; + struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number); + + aps->slot = slot; + + dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, + aps->dev, aps->fun); + + return (void *)aps; +} + +static void pciehprm_dump_func_res( struct pci_func *fun) +{ + struct pci_func *func = fun; + + if (func->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (func->bus_head); + } + if (func->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (func->io_head); + } + if (func->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (func->mem_head); + } + if (func->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (func->p_mem_head); + } +} + +static void pciehprm_dump_ctrl_res( struct controller *ctlr) +{ + struct controller *ctrl = ctlr; + + if (ctrl->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (ctrl->bus_head); + } + if (ctrl->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (ctrl->io_head); + } + if (ctrl->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (ctrl->mem_head); + } + if (ctrl->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (ctrl->p_mem_head); + } +} + +static int pciehprm_get_used_resources ( + struct controller *ctrl, + struct pci_func *func + ) +{ + return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD); +} + +static int configure_existing_function( + struct controller *ctrl, + struct pci_func *func + ) +{ + int rc; + + /* See how much resources the func has used. */ + rc = pciehprm_get_used_resources (ctrl, func); + + if (!rc) { + /* Subtract the resources used by the func from ctrl resources */ + rc = pciehprm_delete_resources (&ctrl->bus_head, func->bus_head); + rc |= pciehprm_delete_resources (&ctrl->io_head, func->io_head); + rc |= pciehprm_delete_resources (&ctrl->mem_head, func->mem_head); + rc |= pciehprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); + if (rc) + warn("aCEF: cannot del used resources\n"); + } else + err("aCEF: cannot get used resources\n"); + + return rc; +} + +static int bind_pci_resources_to_slots ( struct controller *ctrl) +{ + struct pci_func *func; + int busn = ctrl->slot_bus; + int devn, funn; + u32 vid; + + for (devn = 0; devn < 32; devn++) { + for (funn = 0; funn < 8; funn++) { + /* + if (devn == ctrl->device && funn == ctrl->function) + continue; + */ + /* Find out if this entry is for an occupied slot */ + vid = 0xFFFFFFFF; + pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), + PCI_VENDOR_ID, &vid); + + if (vid != 0xFFFFFFFF) { + dbg("%s: vid = %x\n", __FUNCTION__, vid); + func = pciehp_slot_find(busn, devn, funn); + if (!func) + continue; + configure_existing_function(ctrl, func); + dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); + pciehprm_dump_func_res(func); + } + } + } + + return 0; +} + +static int bind_pci_resources( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int status = 0; + + if (ab->bus_head) { + dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus); + status = pciehprm_add_resources (&ctrl->bus_head, ab->bus_head); + if (pciehprm_delete_resources (&ab->bus_head, ctrl->bus_head)) + warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus); + + if (ab->io_head) { + dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus); + status = pciehprm_add_resources (&ctrl->io_head, ab->io_head); + if (pciehprm_delete_resources (&ab->io_head, ctrl->io_head)) + warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus); + + if (ab->mem_head) { + dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus); + status = pciehprm_add_resources (&ctrl->mem_head, ab->mem_head); + if (pciehprm_delete_resources (&ab->mem_head, ctrl->mem_head)) + warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus); + + if (ab->p_mem_head) { + dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus); + status = pciehprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head); + if (pciehprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head)) + warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus); + + return status; +} + +static int no_pci_resources( struct acpi_bridge *ab) +{ + return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head); +} + +static int find_pci_bridge_resources ( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int rc = 0; + struct pci_func func; + + memset(&func, 0, sizeof(struct pci_func)); + + func.bus = ab->pbus; + func.device = ab->pdevice; + func.function = ab->pfunction; + func.is_a_board = 1; + + /* Get used resources for this PCI bridge */ + rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD); + + ab->io_head = func.io_head; + ab->mem_head = func.mem_head; + ab->p_mem_head = func.p_mem_head; + ab->bus_head = func.bus_head; + if (ab->bus_head) + pciehprm_delete_resource(&ab->bus_head, ctrl->pci_dev->subordinate->number, 1); + return rc; +} + +static int get_pci_resources_from_bridge( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int rc = 0; + + dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus); + + rc = find_pci_bridge_resources (ctrl, ab); + + pciehp_resource_sort_and_combine(&ab->bus_head); + pciehp_resource_sort_and_combine(&ab->io_head); + pciehp_resource_sort_and_combine(&ab->mem_head); + pciehp_resource_sort_and_combine(&ab->p_mem_head); + + pciehprm_add_resources (&ab->tbus_head, ab->bus_head); + pciehprm_add_resources (&ab->tio_head, ab->io_head); + pciehprm_add_resources (&ab->tmem_head, ab->mem_head); + pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); + + return rc; +} + +static int get_pci_resources( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int rc = 0; + + if (no_pci_resources(ab)) { + dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus); + rc = get_pci_resources_from_bridge(ctrl, ab); + } + + return rc; +} + +/* + * Get resources for this ctrl. + * 1. get total resources from ACPI _CRS or bridge (this ctrl) + * 2. find used resources of existing adapters + * 3. subtract used resources from total resources + */ +int pciehprm_find_available_resources( struct controller *ctrl) +{ + int rc = 0; + struct acpi_bridge *ab; + + ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number); + if (!ab) { + err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); + return -1; + } + if (no_pci_resources(ab)) { + rc = get_pci_resources(ctrl, ab); + if (rc) { + err("pfar:cannot get pci resources of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); + return -1; + } + } + + rc = bind_pci_resources(ctrl, ab); + dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); + pciehprm_dump_ctrl_res(ctrl); + + bind_pci_resources_to_slots (ctrl); + + dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); + pciehprm_dump_ctrl_res(ctrl); + + return rc; +} + +int pciehprm_set_hpp( + struct controller *ctrl, + struct pci_func *func, + u8 card_type + ) +{ + struct acpi_bridge *ab; + struct pci_bus lpci_bus, *pci_bus; + int rc = 0; + unsigned int devfn; + u8 cls= 0x08; /* default cache line size */ + u8 lt = 0x40; /* default latency timer */ + u8 ep = 0; + u8 es = 0; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); + + if (ab) { + if (ab->_hpp) { + lt = (u8)ab->_hpp->latency_timer; + cls = (u8)ab->_hpp->cache_line_size; + ep = (u8)ab->_hpp->enable_perr; + es = (u8)ab->_hpp->enable_serr; + } else + dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", + func->bus, func->device, func->function); + } else + dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", + func->bus, func->device, func->function); + + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + /* Set subordinate Latency Timer */ + rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt); + } + + /* Set base Latency Timer */ + rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt); + dbg(" set latency timer =0x%02x: %x\n", lt, rc); + + rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls); + dbg(" set cache_line_size=0x%02x: %x\n", cls, rc); + + return rc; +} + +void pciehprm_enable_card( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u16 command, cmd, bcommand, bcmd; + struct pci_bus lpci_bus, *pci_bus; + struct acpi_bridge *ab; + unsigned int devfn; + int rc; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); + } + + cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA; + + ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); + if (ab) { + if (ab->_hpp) { + if (ab->_hpp->enable_perr) { + command |= PCI_COMMAND_PARITY; + bcommand |= PCI_BRIDGE_CTL_PARITY; + } else { + command &= ~PCI_COMMAND_PARITY; + bcommand &= ~PCI_BRIDGE_CTL_PARITY; + } + if (ab->_hpp->enable_serr) { + command |= PCI_COMMAND_SERR; + bcommand |= PCI_BRIDGE_CTL_SERR; + } else { + command &= ~PCI_COMMAND_SERR; + bcommand &= ~PCI_BRIDGE_CTL_SERR; + } + } else + dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); + } else + dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); + + if (command != cmd) { + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + } + if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) { + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); + } +} diff -urN linux-2.4.26/drivers/hotplug/pciehprm_nonacpi.c linux-2.4.27/drivers/hotplug/pciehprm_nonacpi.c --- linux-2.4.26/drivers/hotplug/pciehprm_nonacpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehprm_nonacpi.c 2004-08-07 16:26:04.758351051 -0700 @@ -0,0 +1,497 @@ +/* + * PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_IA64 +#include +#endif +#include "pciehp.h" +#include "pciehprm.h" +#include "pciehprm_nonacpi.h" + + +void pciehprm_cleanup(void) +{ + return; +} + +int pciehprm_print_pirt(void) +{ + return 0; +} + + +void * pciehprm_get_slot(struct slot *slot) +{ + return NULL; +} + +int pciehprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) +{ + + *sun = (u8) (ctrl->first_slot); + return 0; +} + +static void print_pci_resource ( struct pci_resource *aprh) +{ + struct pci_resource *res; + + for (res = aprh; res; res = res->next) + dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); +} + + +static void phprm_dump_func_res( struct pci_func *fun) +{ + struct pci_func *func = fun; + + if (func->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (func->bus_head); + } + if (func->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (func->io_head); + } + if (func->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (func->mem_head); + } + if (func->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (func->p_mem_head); + } +} + +static int phprm_get_used_resources ( + struct controller *ctrl, + struct pci_func *func + ) +{ + return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD); +} + +static int phprm_delete_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + struct pci_resource *prevnode; + struct pci_resource *split_node; + ulong tbase; + + pciehp_resource_sort_and_combine(aprh); + + for (res = *aprh; res; res = res->next) { + if (res->base > base) + continue; + + if ((res->base + res->length) < (base + size)) + continue; + + if (res->base < base) { + tbase = base; + + if ((res->length - (tbase - res->base)) < size) + continue; + + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base; + split_node->length = tbase - res->base; + res->base = tbase; + res->length -= split_node->length; + + split_node->next = res->next; + res->next = split_node; + } + + if (res->length >= size) { + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base + size; + split_node->length = res->length - size; + res->length = size; + + split_node->next = res->next; + res->next = split_node; + } + + if (*aprh == res) { + *aprh = res->next; + } else { + prevnode = *aprh; + while (prevnode->next != res) + prevnode = prevnode->next; + + prevnode->next = res->next; + } + res->next = NULL; + kfree(res); + break; + } + + return 0; +} + + +static int phprm_delete_resources( + struct pci_resource **aprh, + struct pci_resource *this + ) +{ + struct pci_resource *res; + + for (res = this; res; res = res->next) + phprm_delete_resource(aprh, res->base, res->length); + + return 0; +} + + +static int configure_existing_function( + struct controller *ctrl, + struct pci_func *func + ) +{ + int rc; + + /* See how much resources the func has used. */ + rc = phprm_get_used_resources (ctrl, func); + + if (!rc) { + /* Subtract the resources used by the func from ctrl resources */ + rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head); + rc |= phprm_delete_resources (&ctrl->io_head, func->io_head); + rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head); + rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); + if (rc) + warn("aCEF: cannot del used resources\n"); + } else + err("aCEF: cannot get used resources\n"); + + return rc; +} + +static int pciehprm_delete_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + struct pci_resource *prevnode; + struct pci_resource *split_node; + ulong tbase; + + pciehp_resource_sort_and_combine(aprh); + + for (res = *aprh; res; res = res->next) { + if (res->base > base) + continue; + + if ((res->base + res->length) < (base + size)) + continue; + + if (res->base < base) { + tbase = base; + + if ((res->length - (tbase - res->base)) < size) + continue; + + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base; + split_node->length = tbase - res->base; + res->base = tbase; + res->length -= split_node->length; + + split_node->next = res->next; + res->next = split_node; + } + + if (res->length >= size) { + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base + size; + split_node->length = res->length - size; + res->length = size; + + split_node->next = res->next; + res->next = split_node; + } + + if (*aprh == res) { + *aprh = res->next; + } else { + prevnode = *aprh; + while (prevnode->next != res) + prevnode = prevnode->next; + + prevnode->next = res->next; + } + res->next = NULL; + kfree(res); + break; + } + + return 0; +} + +static int bind_pci_resources_to_slots ( struct controller *ctrl) +{ + struct pci_func *func; + int busn = ctrl->slot_bus; + int devn, funn; + u32 vid; + + for (devn = 0; devn < 32; devn++) { + for (funn = 0; funn < 8; funn++) { + /* + if (devn == ctrl->device && funn == ctrl->function) + continue; + */ + /* Find out if this entry is for an occupied slot */ + vid = 0xFFFFFFFF; + + pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); + if (vid != 0xFFFFFFFF) { + dbg("%s: vid = %x bus %x dev %x fun %x\n", __FUNCTION__, + vid, busn, devn, funn); + func = pciehp_slot_find(busn, devn, funn); + dbg("%s: func = %p\n", __FUNCTION__,func); + if (!func) + continue; + configure_existing_function(ctrl, func); + dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); + phprm_dump_func_res(func); + } + } + } + + return 0; +} + +static void phprm_dump_ctrl_res( struct controller *ctlr) +{ + struct controller *ctrl = ctlr; + + if (ctrl->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (ctrl->bus_head); + } + if (ctrl->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (ctrl->io_head); + } + if (ctrl->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (ctrl->mem_head); + } + if (ctrl->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (ctrl->p_mem_head); + } +} + +/* + * phprm_find_available_resources + * + * Finds available memory, IO, and IRQ resources for programming + * devices which may be added to the system + * this function is for hot plug ADD! + * + * returns 0 if success + */ +int pciehprm_find_available_resources(struct controller *ctrl) +{ + struct pci_func func; + u32 rc; + + memset(&func, 0, sizeof(struct pci_func)); + + func.bus = ctrl->bus; + func.device = ctrl->device; + func.function = ctrl->function; + func.is_a_board = 1; + + /* Get resources for this PCI bridge */ + rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD); + dbg("%s: pciehp_save_used_resources rc = %d\n", __FUNCTION__, rc); + + if (func.mem_head) + func.mem_head->next = ctrl->mem_head; + ctrl->mem_head = func.mem_head; + + if (func.p_mem_head) + func.p_mem_head->next = ctrl->p_mem_head; + ctrl->p_mem_head = func.p_mem_head; + + if (func.io_head) + func.io_head->next = ctrl->io_head; + ctrl->io_head = func.io_head; + + if (func.bus_head) + func.bus_head->next = ctrl->bus_head; + ctrl->bus_head = func.bus_head; + if (ctrl->bus_head) + pciehprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1); + + dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); + phprm_dump_ctrl_res(ctrl); + + dbg("%s: before bind_pci_resources_to slots\n", __FUNCTION__); + + bind_pci_resources_to_slots (ctrl); + + dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); + phprm_dump_ctrl_res(ctrl); + + return (rc); +} + +int pciehprm_set_hpp( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u32 rc; + u8 temp_byte; + struct pci_bus lpci_bus, *pci_bus; + unsigned int devfn; + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + temp_byte = 0x40; /* Hard coded value for LT */ + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + /* Set subordinate Latency Timer */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); + + if (rc) { + dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + return rc; + } + } + + /* Set base Latency Timer */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + + if (rc) { + dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, + func->device, func->function); + return rc; + } + + /* set Cache Line size */ + temp_byte = 0x08; /* hard coded value for CLS */ + + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + + if (rc) { + dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, + func->device, func->function); + } + + /* set enable_perr */ + /* set enable_serr */ + + return rc; +} + +void pciehprm_enable_card( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u16 command, bcommand; + struct pci_bus lpci_bus, *pci_bus; + unsigned int devfn; + int rc; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); + + command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR + | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); + + bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR + | PCI_BRIDGE_CTL_NO_ISA; + + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); + } +} + +static int legacy_pciehprm_init_pci(void) +{ + return 0; +} + +int pciehprm_init(enum php_ctlr_type ctrl_type) +{ + int retval; + + switch (ctrl_type) { + case PCI: + retval = legacy_pciehprm_init_pci(); + break; + default: + retval = -ENODEV; + break; + } + + return retval; +} diff -urN linux-2.4.26/drivers/hotplug/pciehprm_nonacpi.h linux-2.4.27/drivers/hotplug/pciehprm_nonacpi.h --- linux-2.4.26/drivers/hotplug/pciehprm_nonacpi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/pciehprm_nonacpi.h 2004-08-07 16:26:04.758351051 -0700 @@ -0,0 +1,56 @@ +/* + * PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#ifndef _PCIEHPRM_NONACPI_H_ +#define _PCIEHPRM_NONACPI_H_ + +struct irq_info { + u8 bus, devfn; /* bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__ ((packed)) irq[4]; + u8 slot; /* slot number, 0=onboard */ + u8 rfu; +} __attribute__ ((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__ ((packed)); + +#endif /* _PCIEHPRM_NONACPI_H_ */ diff -urN linux-2.4.26/drivers/hotplug/shpchp.h linux-2.4.27/drivers/hotplug/shpchp.h --- linux-2.4.26/drivers/hotplug/shpchp.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchp.h 2004-08-07 16:26:04.760351133 -0700 @@ -0,0 +1,464 @@ +/* + * Standard Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ +#ifndef _SHPCHP_H +#define _SHPCHP_H + +#include +#include +#include +#include +#include "pci_hotplug.h" + +#if !defined(CONFIG_HOTPLUG_PCI_SHPC_MODULE) + #define MY_NAME "shpchp.o" +#else + #define MY_NAME THIS_MODULE->name +#endif + +extern int shpchp_poll_mode; +extern int shpchp_poll_time; +extern int shpchp_debug; + +/*#define dbg(format, arg...) do { if (shpchp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/ +#define dbg(format, arg...) do { if (shpchp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0) +#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) +#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) + +struct pci_func { + struct pci_func *next; + u8 bus; + u8 device; + u8 function; + u8 is_a_board; + u16 status; + u8 configured; + u8 switch_save; + u8 presence_save; + u32 base_length[0x06]; + u8 base_type[0x06]; + u16 reserved2; + u32 config_space[0x20]; + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct pci_dev* pci_dev; +}; + +#define SLOT_MAGIC 0x67267321 +struct slot { + u32 magic; + struct slot *next; + u8 bus; + u8 device; + u32 number; + u8 is_a_board; + u8 configured; + u8 state; + u8 switch_save; + u8 presence_save; + u32 capabilities; + u16 reserved2; + struct timer_list task_event; + u8 hp_slot; + struct controller *ctrl; + struct hpc_ops *hpc_ops; + struct hotplug_slot *hotplug_slot; + struct list_head slot_list; +}; + +struct pci_resource { + struct pci_resource * next; + u32 base; + u32 length; +}; + +struct event_info { + u32 event_type; + u8 hp_slot; +}; + +struct controller { + struct controller *next; + struct semaphore crit_sect; /* critical section semaphore */ + void * hpc_ctlr_handle; /* HPC controller handle */ + int num_slots; /* Number of slots on ctlr */ + int slot_num_inc; /* 1 or -1 */ + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct pci_dev *pci_dev; + struct pci_bus *pci_bus; + struct event_info event_queue[10]; + struct slot *slot; + struct hpc_ops *hpc_ops; + wait_queue_head_t queue; /* sleep & wake process */ + u8 next_event; + u8 seg; + u8 bus; + u8 device; + u8 function; + u8 rev; + u8 slot_device_offset; + u8 add_support; + enum pci_bus_speed speed; + u32 first_slot; /* First physical slot number */ + u8 slot_bus; /* Bus where the slots handled by this controller sit */ + u8 push_flag; + u16 ctlrcap; + u16 vendor_id; +}; + +struct irq_mapping { + u8 barber_pole; + u8 valid_INT; + u8 interrupt[4]; +}; + +struct resource_lists { + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + struct irq_mapping *irqs; +}; + +/* Define AMD SHPC ID */ +#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 + +/* Define SHPC CAP ID - not defined in kernel yet */ +#define PCI_CAP_ID_SHPC 0x0C + +#define INT_BUTTON_IGNORE 0 +#define INT_PRESENCE_ON 1 +#define INT_PRESENCE_OFF 2 +#define INT_SWITCH_CLOSE 3 +#define INT_SWITCH_OPEN 4 +#define INT_POWER_FAULT 5 +#define INT_POWER_FAULT_CLEAR 6 +#define INT_BUTTON_PRESS 7 +#define INT_BUTTON_RELEASE 8 +#define INT_BUTTON_CANCEL 9 + +#define STATIC_STATE 0 +#define BLINKINGON_STATE 1 +#define BLINKINGOFF_STATE 2 +#define POWERON_STATE 3 +#define POWEROFF_STATE 4 + +#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400 + +/* Error messages */ +#define INTERLOCK_OPEN 0x00000002 +#define ADD_NOT_SUPPORTED 0x00000003 +#define CARD_FUNCTIONING 0x00000005 +#define ADAPTER_NOT_SAME 0x00000006 +#define NO_ADAPTER_PRESENT 0x00000009 +#define NOT_ENOUGH_RESOURCES 0x0000000B +#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C +#define WRONG_BUS_FREQUENCY 0x0000000D +#define POWER_FAILURE 0x0000000E + +#define REMOVE_NOT_SUPPORTED 0x00000003 + +#define DISABLE_CARD 1 + +/* + * error Messages + */ +#define msg_initialization_err "Initialization failure, error=%d\n" +#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n" +#define msg_HPC_non_shpc "The PCI hot plug controller is not supported by this driver.\n" +#define msg_HPC_not_supported "This system is not supported by this version of shpcphd mdoule. Upgrade to a newer version of shpchpd\n" +#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n" +#define msg_button_on "PCI slot #%d - powering on due to button press.\n" +#define msg_button_off "PCI slot #%d - powering off due to button press.\n" +#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n" +#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n" + +/* controller functions */ +extern void shpchp_pushbutton_thread (unsigned long event_pointer); +extern int shpchprm_find_available_resources (struct controller *ctrl); +extern int shpchp_event_start_thread (void); +extern void shpchp_event_stop_thread (void); +extern struct pci_func *shpchp_slot_create (unsigned char busnumber); +extern struct pci_func *shpchp_slot_find (unsigned char bus, unsigned char device, unsigned char index); +extern int shpchp_enable_slot (struct slot *slot); +extern int shpchp_disable_slot (struct slot *slot); + +extern u8 shpchp_handle_attention_button (u8 hp_slot, void *inst_id); +extern u8 shpchp_handle_switch_change (u8 hp_slot, void *inst_id); +extern u8 shpchp_handle_presence_change (u8 hp_slot, void *inst_id); +extern u8 shpchp_handle_power_fault (u8 hp_slot, void *inst_id); + +/* resource functions */ +extern int shpchp_resource_sort_and_combine (struct pci_resource **head); + +/* pci functions */ +extern int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); +extern int shpchp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot); +extern int shpchp_save_config (struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num); +extern int shpchp_save_used_resources (struct controller *ctrl, struct pci_func * func, int flag); +extern int shpchp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot); +extern void shpchp_destroy_board_resources (struct pci_func * func); +extern int shpchp_return_board_resources (struct pci_func * func, struct resource_lists * resources); +extern void shpchp_destroy_resource_list (struct resource_lists * resources); +extern int shpchp_configure_device (struct controller* ctrl, struct pci_func* func); +extern int shpchp_unconfigure_device (struct pci_func* func); + + +/* Global variables */ +extern struct controller *shpchp_ctrl_list; +extern struct pci_func *shpchp_slot_list[256]; + +/* These are added to support AMD SHPC */ +extern u8 shpchp_nic_irq; +extern u8 shpchp_disk_irq; + +struct ctrl_reg { + volatile u32 base_offset; + volatile u32 slot_avail1; + volatile u32 slot_avail2; + volatile u32 slot_config; + volatile u16 sec_bus_config; + volatile u8 msi_ctrl; + volatile u8 prog_interface; + volatile u16 cmd; + volatile u16 cmd_status; + volatile u32 intr_loc; + volatile u32 serr_loc; + volatile u32 serr_intr_enable; + volatile u32 slot1; + volatile u32 slot2; + volatile u32 slot3; + volatile u32 slot4; + volatile u32 slot5; + volatile u32 slot6; + volatile u32 slot7; + volatile u32 slot8; + volatile u32 slot9; + volatile u32 slot10; + volatile u32 slot11; + volatile u32 slot12; +} __attribute__ ((packed)); + +/* Offsets to the controller registers based on the above structure layout */ +enum ctrl_offsets { + BASE_OFFSET = offsetof(struct ctrl_reg, base_offset), + SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1), + SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2), + SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config), + SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config), + MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl), + PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface), + CMD = offsetof(struct ctrl_reg, cmd), + CMD_STATUS = offsetof(struct ctrl_reg, cmd_status), + INTR_LOC = offsetof(struct ctrl_reg, intr_loc), + SERR_LOC = offsetof(struct ctrl_reg, serr_loc), + SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable), + SLOT1 = offsetof(struct ctrl_reg, slot1), + SLOT2 = offsetof(struct ctrl_reg, slot2), + SLOT3 = offsetof(struct ctrl_reg, slot3), + SLOT4 = offsetof(struct ctrl_reg, slot4), + SLOT5 = offsetof(struct ctrl_reg, slot5), + SLOT6 = offsetof(struct ctrl_reg, slot6), + SLOT7 = offsetof(struct ctrl_reg, slot7), + SLOT8 = offsetof(struct ctrl_reg, slot8), + SLOT9 = offsetof(struct ctrl_reg, slot9), + SLOT10 = offsetof(struct ctrl_reg, slot10), + SLOT11 = offsetof(struct ctrl_reg, slot11), + SLOT12 = offsetof(struct ctrl_reg, slot12), +}; +typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id); +struct php_ctlr_state_s { + struct php_ctlr_state_s *pnext; + struct pci_dev *pci_dev; + unsigned int irq; + unsigned long flags; /* spinlock's */ + u32 slot_device_offset; + u32 num_slots; + struct timer_list int_poll_timer; /* Added for poll event */ + php_intr_callback_t attention_button_callback; + php_intr_callback_t switch_change_callback; + php_intr_callback_t presence_change_callback; + php_intr_callback_t power_fault_callback; + void *callback_instance_id; + void *creg; /* Ptr to controller register space */ +}; +/* Inline functions */ + + +/* Inline functions to check the sanity of a pointer that is passed to us */ +static inline int slot_paranoia_check (struct slot *slot, const char *function) +{ + if (!slot) { + dbg("%s - slot == NULL", function); + return -1; + } + if (slot->magic != SLOT_MAGIC) { + dbg("%s - bad magic number for slot", function); + return -1; + } + if (!slot->hotplug_slot) { + dbg("%s - slot->hotplug_slot == NULL!", function); + return -1; + } + return 0; +} + +static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function) +{ + struct slot *slot; + + if (!hotplug_slot) { + dbg("%s - hotplug_slot == NULL\n", function); + return NULL; + } + + slot = (struct slot *)hotplug_slot->private; + if (slot_paranoia_check (slot, function)) + return NULL; + return slot; +} + +static inline struct slot *shpchp_find_slot (struct controller *ctrl, u8 device) +{ + struct slot *p_slot, *tmp_slot = NULL; + + if (!ctrl) + return NULL; + + p_slot = ctrl->slot; + + dbg("p_slot = %p\n", p_slot); + + while (p_slot && (p_slot->device != device)) { + tmp_slot = p_slot; + p_slot = p_slot->next; + dbg("In while loop, p_slot = %p\n", p_slot); + } + if (p_slot == NULL) { + err("ERROR: shpchp_find_slot device=0x%x\n", device); + p_slot = tmp_slot; + } + + return (p_slot); +} + +static inline int wait_for_ctrl_irq (struct controller *ctrl) +{ + DECLARE_WAITQUEUE(wait, current); + int retval = 0; + + dbg("%s : start\n",__FUNCTION__); + + add_wait_queue(&ctrl->queue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + if (!shpchp_poll_mode) { + /* Sleep for up to 1 second */ + schedule_timeout(1*HZ); + } else { + /* Sleep for up to 2 second */ + schedule_timeout(2*HZ); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&ctrl->queue, &wait); + if (signal_pending(current)) + retval = -EINTR; + + dbg("%s : end\n", __FUNCTION__); + return retval; +} + +/* Puts node back in the resource list pointed to by head */ +static inline void return_resource(struct pci_resource **head, struct pci_resource *node) +{ + if (!node || !head) + return; + node->next = *head; + *head = node; +} + +#define SLOT_NAME_SIZE 10 + +static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot) +{ + snprintf(buffer, buffer_size, "%d", slot->number); +} + +enum php_ctlr_type { + PCI, + ISA, + ACPI +}; + +int shpc_init( struct controller *ctrl, struct pci_dev *pdev, + php_intr_callback_t attention_button_callback, + php_intr_callback_t switch_change_callback, + php_intr_callback_t presence_change_callback, + php_intr_callback_t power_fault_callback); + + +int shpc_get_ctlr_slot_config( struct controller *ctrl, + int *num_ctlr_slots, + int *first_device_num, + int *physical_slot_num, + int *updown, + int *flags); + +struct hpc_ops { + int (*power_on_slot ) (struct slot *slot); + int (*slot_enable ) (struct slot *slot); + int (*slot_disable ) (struct slot *slot); + int (*enable_all_slots) (struct slot *slot); + int (*pwr_on_all_slots) (struct slot *slot); + int (*set_bus_speed_mode) (struct slot *slot, enum pci_bus_speed speed); + int (*get_power_status) (struct slot *slot, u8 *status); + int (*get_attention_status) (struct slot *slot, u8 *status); + int (*set_attention_status) (struct slot *slot, u8 status); + int (*get_latch_status) (struct slot *slot, u8 *status); + int (*get_adapter_status) (struct slot *slot, u8 *status); + + int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); + int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed); + int (*get_adapter_speed) (struct slot *slot, enum pci_bus_speed *speed); + int (*get_mode1_ECC_cap) (struct slot *slot, u8 *mode); + int (*get_prog_int) (struct slot *slot, u8 *prog_int); + + int (*query_power_fault) (struct slot *slot); + void (*green_led_on) (struct slot *slot); + void (*green_led_off) (struct slot *slot); + void (*green_led_blink) (struct slot *slot); + void (*release_ctlr) (struct controller *ctrl); + int (*check_cmd_status) (struct controller *ctrl); +}; + +#endif /* _SHPCHP_H */ diff -urN linux-2.4.26/drivers/hotplug/shpchp_core.c linux-2.4.27/drivers/hotplug/shpchp_core.c --- linux-2.4.26/drivers/hotplug/shpchp_core.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchp_core.c 2004-08-07 16:26:04.761351174 -0700 @@ -0,0 +1,700 @@ +/* + * Standard Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shpchp.h" +#include "shpchprm.h" + +/* Global variables */ +int shpchp_debug; +int shpchp_poll_mode; +int shpchp_poll_time; +struct controller *shpchp_ctrl_list; /* = NULL */ +struct pci_func *shpchp_slot_list[256]; + +#define DRIVER_VERSION "0.4" +#define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " +#define DRIVER_DESC "Standard Hot Plug PCI Controller Driver" + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +MODULE_PARM(shpchp_debug, "i"); +MODULE_PARM(shpchp_poll_mode, "i"); +MODULE_PARM(shpchp_poll_time, "i"); +MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not"); +MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not"); +MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); + +#define SHPC_MODULE_NAME "shpchp.o" + +static int shpc_start_thread (void); +static int set_attention_status (struct hotplug_slot *slot, u8 value); +static int enable_slot (struct hotplug_slot *slot); +static int disable_slot (struct hotplug_slot *slot); +static int hardware_test (struct hotplug_slot *slot, u32 value); +static int get_power_status (struct hotplug_slot *slot, u8 *value); +static int get_attention_status (struct hotplug_slot *slot, u8 *value); +static int get_latch_status (struct hotplug_slot *slot, u8 *value); +static int get_adapter_status (struct hotplug_slot *slot, u8 *value); +static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); +static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); + +static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { + .owner = THIS_MODULE, + .set_attention_status = set_attention_status, + .enable_slot = enable_slot, + .disable_slot = disable_slot, + .hardware_test = hardware_test, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, + .get_max_bus_speed = get_max_bus_speed, + .get_cur_bus_speed = get_cur_bus_speed, +}; + +static int init_slots(struct controller *ctrl) +{ + struct slot *new_slot; + u8 number_of_slots; + u8 slot_device; + u32 slot_number, sun; + int result; + + dbg("%s\n",__FUNCTION__); + + number_of_slots = ctrl->num_slots; + slot_device = ctrl->slot_device_offset; + slot_number = ctrl->first_slot; + + while (number_of_slots) { + new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL); + if (!new_slot) + return -ENOMEM; + + memset(new_slot, 0, sizeof(struct slot)); + new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL); + if (!new_slot->hotplug_slot) { + kfree (new_slot); + return -ENOMEM; + } + memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot)); + + new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!new_slot->hotplug_slot->info) { + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return -ENOMEM; + } + memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info)); + new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL); + if (!new_slot->hotplug_slot->name) { + kfree (new_slot->hotplug_slot->info); + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return -ENOMEM; + } + + new_slot->magic = SLOT_MAGIC; + new_slot->ctrl = ctrl; + new_slot->bus = ctrl->slot_bus; + new_slot->device = slot_device; + new_slot->hpc_ops = ctrl->hpc_ops; + + if (shpchprm_get_physical_slot_number(ctrl, &sun, new_slot->bus, new_slot->device)) { + kfree (new_slot->hotplug_slot->info); + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return -ENOMEM; + } + + new_slot->number = sun; + new_slot->hp_slot = slot_device - ctrl->slot_device_offset; + + /* Register this slot with the hotplug pci core */ + new_slot->hotplug_slot->private = new_slot; + make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot); + new_slot->hotplug_slot->ops = &shpchp_hotplug_slot_ops; + + new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status)); + new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status)); + new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status)); + new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status)); + + dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", new_slot->bus, new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset); + result = pci_hp_register (new_slot->hotplug_slot); + if (result) { + err ("pci_hp_register failed with error %d\n", result); + kfree (new_slot->hotplug_slot->info); + kfree (new_slot->hotplug_slot->name); + kfree (new_slot->hotplug_slot); + kfree (new_slot); + return result; + } + + new_slot->next = ctrl->slot; + ctrl->slot = new_slot; + + number_of_slots--; + slot_device++; + slot_number += ctrl->slot_num_inc; + } + + return(0); +} + + +static int cleanup_slots (struct controller * ctrl) +{ + struct slot *old_slot, *next_slot; + + old_slot = ctrl->slot; + ctrl->slot = NULL; + + while (old_slot) { + next_slot = old_slot->next; + pci_hp_deregister (old_slot->hotplug_slot); + kfree(old_slot->hotplug_slot->info); + kfree(old_slot->hotplug_slot->name); + kfree(old_slot->hotplug_slot); + kfree(old_slot); + old_slot = next_slot; + } + + + return(0); +} + +static int get_ctlr_slot_config(struct controller *ctrl) +{ + int num_ctlr_slots; + int first_device_num; + int physical_slot_num; + int updown; + int rc; + int flags; + + rc = shpc_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, + &updown, &flags); + if (rc) { + err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device); + return (-1); + } + + ctrl->num_slots = num_ctlr_slots; + ctrl->slot_device_offset = first_device_num; + ctrl->first_slot = physical_slot_num; + ctrl->slot_num_inc = updown; /* either -1 or 1 */ + + dbg("%s: num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n", + __FUNCTION__, num_ctlr_slots, first_device_num, physical_slot_num, updown, + ctrl->bus, ctrl->device); + + return (0); +} + + +/* + * set_attention_status - Turns the Amber LED for a slot on, off or blink + */ +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + hotplug_slot->info->attention_status = status; + slot->hpc_ops->set_attention_status(slot, status); + + + return 0; +} + + +static int enable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + return shpchp_enable_slot(slot); +} + + +static int disable_slot (struct hotplug_slot *hotplug_slot) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + return shpchp_disable_slot(slot); +} + + +static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value) +{ + return 0; +} + + +static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_power_status(slot, value); + if (retval < 0) + *value = hotplug_slot->info->power_status; + + return 0; +} + +static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_attention_status(slot, value); + if (retval < 0) + *value = hotplug_slot->info->attention_status; + + return 0; +} + +static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_latch_status(slot, value); + if (retval < 0) + *value = hotplug_slot->info->latch_status; + + return 0; +} + +static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_adapter_status(slot, value); + + if (retval < 0) + *value = hotplug_slot->info->adapter_status; + + return 0; +} + +static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_max_bus_speed(slot, value); + if (retval < 0) + *value = PCI_SPEED_UNKNOWN; + + return 0; +} + +static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value) +{ + struct slot *slot = get_slot (hotplug_slot, __FUNCTION__); + int retval; + + if (slot == NULL) + return -ENODEV; + + dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name); + + retval = slot->hpc_ops->get_cur_bus_speed(slot, value); + if (retval < 0) + *value = PCI_SPEED_UNKNOWN; + + return 0; +} + +static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int rc; + struct controller *ctrl; + struct slot *t_slot; + int first_device_num; /* first PCI device number supported by this SHPC */ + int num_ctlr_slots; /* number of slots supported by this SHPC */ + + ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL); + if (!ctrl) { + err("%s : out of memory\n", __FUNCTION__); + goto err_out_none; + } + memset(ctrl, 0, sizeof(struct controller)); + + dbg("DRV_thread pid = %d\n", current->pid); + + rc = shpc_init(ctrl, pdev, + (php_intr_callback_t) shpchp_handle_attention_button, + (php_intr_callback_t) shpchp_handle_switch_change, + (php_intr_callback_t) shpchp_handle_presence_change, + (php_intr_callback_t) shpchp_handle_power_fault); + if (rc) { + dbg("%s: controller initialization failed\n", SHPC_MODULE_NAME); + goto err_out_free_ctrl; + } + + dbg("%s: controller initialization success\n", __FUNCTION__); + ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */ + + ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL); + if (!ctrl->pci_bus) { + err("out of memory\n"); + rc = -ENOMEM; + goto err_out_unmap_mmio_region; + } + memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus)); + ctrl->bus = pdev->bus->number; + ctrl->slot_bus = pdev->subordinate->number; + + ctrl->device = PCI_SLOT(pdev->devfn); + ctrl->function = PCI_FUNC(pdev->devfn); + dbg("ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", ctrl->bus, ctrl->device, + ctrl->function, pdev->irq); + + /* + * Save configuration headers for this and subordinate PCI buses + */ + + rc = get_ctlr_slot_config(ctrl); + if (rc) { + err(msg_initialization_err, rc); + goto err_out_free_ctrl_bus; + } + first_device_num = ctrl->slot_device_offset; + num_ctlr_slots = ctrl->num_slots; + + /* Store PCI Config Space for all devices on this bus */ + rc = shpchp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num); + if (rc) { + err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc); + goto err_out_free_ctrl_bus; + } + + /* Get IO, memory, and IRQ resources for new devices */ + rc = shpchprm_find_available_resources(ctrl); + ctrl->add_support = !rc; + + if (rc) { + dbg("shpchprm_find_available_resources = %#x\n", rc); + err("unable to locate PCI configuration resources for hot plug add.\n"); + goto err_out_free_ctrl_bus; + } + + /* Setup the slot information structures */ + rc = init_slots(ctrl); + if (rc) { + err(msg_initialization_err, 6); + goto err_out_free_ctrl_slot; + } + + /* Now hpc_functions (slot->hpc_ops->functions) are ready */ + t_slot = shpchp_find_slot(ctrl, first_device_num); + + /* Check for operation bus speed */ + rc = t_slot->hpc_ops->get_cur_bus_speed(t_slot, &ctrl->speed); + dbg("%s: t_slot->hp_slot %x\n", __FUNCTION__,t_slot->hp_slot); + + if (rc || ctrl->speed == PCI_SPEED_UNKNOWN) { + err(SHPC_MODULE_NAME ": Can't get current bus speed. Set to 33MHz PCI.\n"); + ctrl->speed = PCI_SPEED_33MHz; + } + + /* Finish setting up the hot plug ctrl device */ + ctrl->next_event = 0; + + if (!shpchp_ctrl_list) { + shpchp_ctrl_list = ctrl; + ctrl->next = NULL; + } else { + ctrl->next = shpchp_ctrl_list; + shpchp_ctrl_list = ctrl; + } + + return 0; + +err_out_free_ctrl_slot: + cleanup_slots(ctrl); +err_out_free_ctrl_bus: + kfree(ctrl->pci_bus); +err_out_unmap_mmio_region: + ctrl->hpc_ops->release_ctlr(ctrl); +err_out_free_ctrl: + kfree(ctrl); +err_out_none: + return -ENODEV; +} + + +static int shpc_start_thread(void) +{ + int loop; + int retval = 0; + + dbg("Initialize + Start the notification/polling mechanism \n"); + + retval = shpchp_event_start_thread(); + if (retval) { + dbg("shpchp_event_start_thread() failed\n"); + return retval; + } + + dbg("Initialize slot lists\n"); + /* One slot list for each bus in the system */ + for (loop = 0; loop < 256; loop++) { + shpchp_slot_list[loop] = NULL; + } + + return retval; +} + + +static void unload_shpchpd(void) +{ + struct pci_func *next; + struct pci_func *TempSlot; + int loop; + struct controller *ctrl; + struct controller *tctrl; + struct pci_resource *res; + struct pci_resource *tres; + + ctrl = shpchp_ctrl_list; + + while (ctrl) { + cleanup_slots(ctrl); + + res = ctrl->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = ctrl->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + kfree (ctrl->pci_bus); + + dbg("%s: calling release_ctlr\n", __FUNCTION__); + ctrl->hpc_ops->release_ctlr(ctrl); + + tctrl = ctrl; + ctrl = ctrl->next; + + kfree(tctrl); + } + + for (loop = 0; loop < 256; loop++) { + next = shpchp_slot_list[loop]; + while (next != NULL) { + res = next->io_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->p_mem_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = next->bus_head; + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + TempSlot = next; + next = next->next; + kfree(TempSlot); + } + } + + /* Stop the notification mechanism */ + shpchp_event_stop_thread(); + +} + + +static struct pci_device_id shpcd_pci_tbl[] __devinitdata = { + { + .class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), + .class_mask = ~0, + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl); + + + +static struct pci_driver shpc_driver = { + .name = SHPC_MODULE_NAME, + .id_table = shpcd_pci_tbl, + .probe = shpc_probe, + /* remove: shpc_remove_one, */ +}; + + + +static int __init shpcd_init(void) +{ + int retval = 0; + +#ifdef CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE + shpchp_poll_mode = 1; +#endif + + retval = shpc_start_thread(); + if (retval) + goto error_hpc_init; + + retval = shpchprm_init(PCI); + if (!retval) { + retval = pci_module_init(&shpc_driver); + dbg("%s: pci_module_init = %d\n", __FUNCTION__,retval); + info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + } + +error_hpc_init: + if (retval) { + shpchprm_cleanup(); + shpchp_event_stop_thread(); + } else + shpchprm_print_pirt(); + + return retval; +} + +static void __exit shpcd_cleanup(void) +{ + dbg("unload_shpchpd()\n"); + unload_shpchpd(); + + shpchprm_cleanup(); + + dbg("pci_unregister_driver\n"); + pci_unregister_driver(&shpc_driver); + + info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); +} + + +module_init(shpcd_init); +module_exit(shpcd_cleanup); + + diff -urN linux-2.4.26/drivers/hotplug/shpchp_ctrl.c linux-2.4.27/drivers/hotplug/shpchp_ctrl.c --- linux-2.4.26/drivers/hotplug/shpchp_ctrl.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchp_ctrl.c 2004-08-07 16:26:04.766351380 -0700 @@ -0,0 +1,3071 @@ +/* + * Standard Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shpchp.h" +#include "shpchprm.h" + +static u32 configure_new_device(struct controller *ctrl, struct pci_func *func, + u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); +static int configure_new_function( struct controller *ctrl, struct pci_func *func, + u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev); +static void interrupt_event_handler(struct controller *ctrl); + +static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ +static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ +static int event_finished; +static unsigned long pushbutton_pending; /* = 0 */ + +u8 shpchp_disk_irq; +u8 shpchp_nic_irq; + +u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + u8 getstatus; + struct pci_func *func; + struct event_info *taskInfo; + + /* Attention Button Change */ + dbg("shpchp: Attention button interrupt received.\n"); + + func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread what to do */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + + /* + * Button pressed - See if need to TAKE ACTION!!! + */ + info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_BUTTON_PRESS; + + if ((p_slot->state == BLINKINGON_STATE) + || (p_slot->state == BLINKINGOFF_STATE)) { + /* Cancel if we are still blinking; this means that we press the + * attention again before the 5 sec. limit expires to cancel hot-add + * or hot-remove + */ + taskInfo->event_type = INT_BUTTON_CANCEL; + info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); + } else if ((p_slot->state == POWERON_STATE) + || (p_slot->state == POWEROFF_STATE)) { + /* Ignore if the slot is on power-on or power-off state; this + * means that the previous attention button action to hot-add or + * hot-remove is undergoing + */ + taskInfo->event_type = INT_BUTTON_IGNORE; + info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); + } + + if (rc) + up(&event_semaphore); /* Signal event thread that new event is posted */ + + return 0; + +} + +u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + u8 getstatus; + struct pci_func *func; + struct event_info *taskInfo; + + /* Switch Change */ + dbg("shpchp: Switch interrupt received.\n"); + + func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread + * what to do + */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + + if (getstatus) { + /* + * Switch opened + */ + info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); + func->switch_save = 0; + taskInfo->event_type = INT_SWITCH_OPEN; + } else { + /* + * Switch closed + */ + info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); + func->switch_save = 0x10; + taskInfo->event_type = INT_SWITCH_CLOSE; + } + + if (rc) + up(&event_semaphore); /* Signal event thread that new event is posted */ + + return rc; +} + +u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + struct pci_func *func; + struct event_info *taskInfo; + + /* Presence Change */ + dbg("shpchp: Presence/Notify input change.\n"); + + func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread + * what to do + */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + /* + * Save the presence state + */ + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + if (func->presence_save) { + /* + * Card Present + */ + info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_PRESENCE_ON; + } else { + /* + * Not Present + */ + info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_PRESENCE_OFF; + } + + if (rc) + up(&event_semaphore); /* Signal event thread that new event is posted */ + + return rc; +} + +u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id) +{ + struct controller *ctrl = (struct controller *) inst_id; + struct slot *p_slot; + u8 rc = 0; + struct pci_func *func; + struct event_info *taskInfo; + + /* Power fault */ + dbg("shpchp: Power fault interrupt received.\n"); + + func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + /* This is the structure that tells the worker thread + * what to do + */ + taskInfo = &(ctrl->event_queue[ctrl->next_event]); + ctrl->next_event = (ctrl->next_event + 1) % 10; + taskInfo->hp_slot = hp_slot; + + rc++; + p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { + /* + * Power fault Cleared + */ + info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); + func->status = 0x00; + taskInfo->event_type = INT_POWER_FAULT_CLEAR; + } else { + /* + * Power fault + */ + info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); + taskInfo->event_type = INT_POWER_FAULT; + /* Set power fault status for this board */ + func->status = 0xFF; + info("power fault bit %x set\n", hp_slot); + } + if (rc) + up(&event_semaphore); /* Signal event thread that new event is posted */ + + return rc; +} + + +/* + * sort_by_size + * + * Sorts nodes on the list by their length. + * Smallest first. + * + */ +static int sort_by_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return(1); + + if (!((*head)->next)) + return(0); + + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->length > (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length > current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } /* End of out_of_order loop */ + + return(0); +} + + +/* + * sort_by_max_size + * + * Sorts nodes on the list by their length. + * Largest first. + * + */ +static int sort_by_max_size(struct pci_resource **head) +{ + struct pci_resource *current_res; + struct pci_resource *next_res; + int out_of_order = 1; + + if (!(*head)) + return(1); + + if (!((*head)->next)) + return(0); + + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->length < (*head)->next->length)) { + out_of_order++; + current_res = *head; + *head = (*head)->next; + current_res->next = (*head)->next; + (*head)->next = current_res; + } + + current_res = *head; + + while (current_res->next && current_res->next->next) { + if (current_res->next->length < current_res->next->next->length) { + out_of_order++; + next_res = current_res->next; + current_res->next = current_res->next->next; + current_res = current_res->next; + next_res->next = current_res->next; + current_res->next = next_res; + } else + current_res = current_res->next; + } + } /* End of out_of_order loop */ + + return(0); +} + + +/* + * do_pre_bridge_resource_split + * + * Returns zero or one node of resources that aren't in use + * + */ +static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment) +{ + struct pci_resource *prevnode = NULL; + struct pci_resource *node; + struct pci_resource *split_node; + u32 rc; + u32 temp_dword; + dbg("do_pre_bridge_resource_split\n"); + + if (!(*head) || !(*orig_head)) + return(NULL); + + rc = shpchp_resource_sort_and_combine(head); + + if (rc) + return(NULL); + + if ((*head)->base != (*orig_head)->base) + return(NULL); + + if ((*head)->length == (*orig_head)->length) + return(NULL); + + + /* If we got here, there the bridge requires some of the resource, but + * we may be able to split some off of the front + */ + node = *head; + + if (node->length & (alignment -1)) { + /* This one isn't an aligned length, so we'll make a new entry + * and split it up. + */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + temp_dword = (node->length | (alignment-1)) + 1 - alignment; + + split_node->base = node->base; + split_node->length = temp_dword; + + node->length -= temp_dword; + node->base += split_node->length; + + /* Put it in the list */ + *head = split_node; + split_node->next = node; + } + + if (node->length < alignment) { + return(NULL); + } + + /* Now unlink it */ + if (*head == node) { + *head = node->next; + node->next = NULL; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + node->next = NULL; + } + + return(node); +} + + +/* + * do_bridge_resource_split + * + * Returns zero or one node of resources that aren't in use + * + */ +static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment) +{ + struct pci_resource *prevnode = NULL; + struct pci_resource *node; + u32 rc; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + rc = shpchp_resource_sort_and_combine(head); + + if (rc) + return(NULL); + + node = *head; + + while (node->next) { + prevnode = node; + node = node->next; + kfree(prevnode); + } + + if (node->length < alignment) { + kfree(node); + return(NULL); + } + + if (node->base & (alignment - 1)) { + /* Short circuit if adjusted size is too small */ + temp_dword = (node->base | (alignment-1)) + 1; + if ((node->length - (temp_dword - node->base)) < alignment) { + kfree(node); + return(NULL); + } + + node->length -= (temp_dword - node->base); + node->base = temp_dword; + } + + if (node->length & (alignment - 1)) { + /* There's stuff in use after this node */ + kfree(node); + return(NULL); + } + + return(node); +} + + +/* + * get_io_resource + * + * this function sorts the resource list by size and then + * returns the first node of "size" length that is not in the + * ISA aliasing window. If it finds a node larger than "size" + * it will split it up. + * + * size must be a power of two. + */ +static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node = NULL; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if ( shpchp_resource_sort_and_combine(head) ) + return(NULL); + + if ( sort_by_size(head) ) + return(NULL); + + for (node = *head; node; node = node->next) { + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + /* This one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_dword = (node->base | (size-1)) + 1; + + /*/ Short circuit if adjusted size is too small */ + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base; + split_node->length = temp_dword - node->base; + node->base = temp_dword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of non-aligned base */ + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + /* This one is longer than we need + so we'll make a new entry and split it up */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base + size; + split_node->length = node->length - size; + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + /* For IO make sure it's not in the ISA aliasing space */ + if (node->base & 0x300L) + continue; + + /* If we got here, then it is the right size + Now take it out of the list */ + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + /* Stop looping */ + break; + } + + return(node); +} + + +/* + * get_max_resource + * + * Gets the largest node that is at least "size" big from the + * list pointed to by head. It aligns the node on top and bottom + * to "size" alignment before returning it. + * J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M + * This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot. + */ +static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *max; + struct pci_resource *temp; + struct pci_resource *split_node; + u32 temp_dword; + u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 }; + int i; + + if (!(*head)) + return(NULL); + + if (shpchp_resource_sort_and_combine(head)) + return(NULL); + + if (sort_by_max_size(head)) + return(NULL); + + for (max = *head;max; max = max->next) { + + /* If not big enough we could probably just bail, + instead we'll continue to the next. */ + if (max->length < size) + continue; + + if (max->base & (size - 1)) { + /* this one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_dword = (max->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((max->length - (temp_dword - max->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = max->base; + split_node->length = temp_dword - max->base; + max->base = temp_dword; + max->length -= split_node->length; + + /* Put it next in the list */ + split_node->next = max->next; + max->next = split_node; + } + + if ((max->base + max->length) & (size - 1)) { + /* this one isn't end aligned properly at the top + so we'll make a new entry and split it up */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + temp_dword = ((max->base + max->length) & ~(size - 1)); + split_node->base = temp_dword; + split_node->length = max->length + max->base + - split_node->base; + max->length -= split_node->length; + + /* Put it in the list */ + split_node->next = max->next; + max->next = split_node; + } + + /* Make sure it didn't shrink too much when we aligned it */ + if (max->length < size) + continue; + + for ( i = 0; max_size[i] > size; i++) { + if (max->length > max_size[i]) { + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), + GFP_KERNEL); + if (!split_node) + break; /* return (NULL); */ + split_node->base = max->base + max_size[i]; + split_node->length = max->length - max_size[i]; + max->length = max_size[i]; + /* Put it next in the list */ + split_node->next = max->next; + max->next = split_node; + break; + } + } + + /* Now take it out of the list */ + temp = (struct pci_resource*) *head; + if (temp == max) { + *head = max->next; + } else { + while (temp && temp->next != max) { + temp = temp->next; + } + + temp->next = max->next; + } + + max->next = NULL; + return(max); + } + + /* If we get here, we couldn't find one */ + return(NULL); +} + + +/* + * get_resource + * + * this function sorts the resource list by size and then + * returns the first node of "size" length. If it finds a node + * larger than "size" it will split it up. + * + * size must be a power of two. + */ +static struct pci_resource *get_resource (struct pci_resource **head, u32 size) +{ + struct pci_resource *prevnode; + struct pci_resource *node; + struct pci_resource *split_node; + u32 temp_dword; + + if (!(*head)) + return(NULL); + + if ( shpchp_resource_sort_and_combine(head) ) + return(NULL); + + if ( sort_by_size(head) ) + return(NULL); + + for (node = *head; node; node = node->next) { + dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n", + __FUNCTION__, size, node, node->base, node->length); + if (node->length < size) + continue; + + if (node->base & (size - 1)) { + dbg("%s: not aligned\n", __FUNCTION__); + /* This one isn't base aligned properly + so we'll make a new entry and split it up */ + temp_dword = (node->base | (size-1)) + 1; + + /* Short circuit if adjusted size is too small */ + if ((node->length - (temp_dword - node->base)) < size) + continue; + + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base; + split_node->length = temp_dword - node->base; + node->base = temp_dword; + node->length -= split_node->length; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of non-aligned base */ + + /* Don't need to check if too small since we already did */ + if (node->length > size) { + dbg("%s: too big\n", __FUNCTION__); + /* This one is longer than we need + so we'll make a new entry and split it up */ + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!split_node) + return(NULL); + + split_node->base = node->base + size; + split_node->length = node->length - size; + node->length = size; + + /* Put it in the list */ + split_node->next = node->next; + node->next = split_node; + } /* End of too big on top end */ + + dbg("%s: got one!!!\n", __FUNCTION__); + /* If we got here, then it is the right size + Now take it out of the list */ + if (*head == node) { + *head = node->next; + } else { + prevnode = *head; + while (prevnode->next != node) + prevnode = prevnode->next; + + prevnode->next = node->next; + } + node->next = NULL; + /* Stop looping */ + break; + } + return(node); +} + + +/* + * shpchp_resource_sort_and_combine + * + * Sorts all of the nodes in the list in ascending order by + * their base addresses. Also does garbage collection by + * combining adjacent nodes. + * + * returns 0 if success + */ +int shpchp_resource_sort_and_combine(struct pci_resource **head) +{ + struct pci_resource *node1; + struct pci_resource *node2; + int out_of_order = 1; + + dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head); + + if (!(*head)) + return(1); + + dbg("*head->next = %p\n",(*head)->next); + + if (!(*head)->next) + return(0); /* only one item on the list, already sorted! */ + + dbg("*head->base = 0x%x\n",(*head)->base); + dbg("*head->next->base = 0x%x\n",(*head)->next->base); + while (out_of_order) { + out_of_order = 0; + + /* Special case for swapping list head */ + if (((*head)->next) && + ((*head)->base > (*head)->next->base)) { + node1 = *head; + (*head) = (*head)->next; + node1->next = (*head)->next; + (*head)->next = node1; + out_of_order++; + } + + node1 = (*head); + + while (node1->next && node1->next->next) { + if (node1->next->base > node1->next->next->base) { + out_of_order++; + node2 = node1->next; + node1->next = node1->next->next; + node1 = node1->next; + node2->next = node1->next; + node1->next = node2; + } else + node1 = node1->next; + } + } /* End of out_of_order loop */ + + node1 = *head; + + while (node1 && node1->next) { + if ((node1->base + node1->length) == node1->next->base) { + /* Combine */ + dbg("8..\n"); + node1->length += node1->next->length; + node2 = node1->next; + node1->next = node1->next->next; + kfree(node2); + } else + node1 = node1->next; + } + + return(0); +} + + +/** + * shpchp_slot_create - Creates a node and adds it to the proper bus. + * @busnumber - bus where new node is to be located + * + * Returns pointer to the new node or NULL if unsuccessful + */ +struct pci_func *shpchp_slot_create(u8 busnumber) +{ + struct pci_func *new_slot; + struct pci_func *next; + + new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL); + + if (new_slot == NULL) { + return(new_slot); + } + + memset(new_slot, 0, sizeof(struct pci_func)); + + new_slot->next = NULL; + new_slot->configured = 1; + + if (shpchp_slot_list[busnumber] == NULL) { + shpchp_slot_list[busnumber] = new_slot; + } else { + next = shpchp_slot_list[busnumber]; + while (next->next != NULL) + next = next->next; + next->next = new_slot; + } + return(new_slot); +} + + +/* + * slot_remove - Removes a node from the linked list of slots. + * @old_slot: slot to remove + * + * Returns 0 if successful, !0 otherwise. + */ +static int slot_remove(struct pci_func * old_slot) +{ + struct pci_func *next; + + if (old_slot == NULL) + return(1); + + next = shpchp_slot_list[old_slot->bus]; + + if (next == NULL) { + return(1); + } + + if (next == old_slot) { + shpchp_slot_list[old_slot->bus] = old_slot->next; + shpchp_destroy_board_resources(old_slot); + kfree(old_slot); + return(0); + } + + while ((next->next != old_slot) && (next->next != NULL)) { + next = next->next; + } + + if (next->next == old_slot) { + next->next = old_slot->next; + shpchp_destroy_board_resources(old_slot); + kfree(old_slot); + return(0); + } else + return(2); +} + + +/** + * bridge_slot_remove - Removes a node from the linked list of slots. + * @bridge: bridge to remove + * + * Returns 0 if successful, !0 otherwise. + */ +static int bridge_slot_remove(struct pci_func *bridge) +{ + u8 subordinateBus, secondaryBus; + u8 tempBus; + struct pci_func *next; + + if (bridge == NULL) + return(1); + + secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; + subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; + + for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { + next = shpchp_slot_list[tempBus]; + + while (!slot_remove(next)) { + next = shpchp_slot_list[tempBus]; + } + } + + next = shpchp_slot_list[bridge->bus]; + + if (next == NULL) { + return(1); + } + + if (next == bridge) { + shpchp_slot_list[bridge->bus] = bridge->next; + kfree(bridge); + return(0); + } + + while ((next->next != bridge) && (next->next != NULL)) { + next = next->next; + } + + if (next->next == bridge) { + next->next = bridge->next; + kfree(bridge); + return(0); + } else + return(2); +} + + +/** + * shpchp_slot_find - Looks for a node by bus, and device, multiple functions accessed + * @bus: bus to find + * @device: device to find + * @index: is 0 for first function found, 1 for the second... + * + * Returns pointer to the node if successful, %NULL otherwise. + */ +struct pci_func *shpchp_slot_find(u8 bus, u8 device, u8 index) +{ + int found = -1; + struct pci_func *func; + + func = shpchp_slot_list[bus]; + + if ((func == NULL) || ((func->device == device) && (index == 0))) + return(func); + + if (func->device == device) + found++; + + while (func->next != NULL) { + func = func->next; + + if (func->device == device) + found++; + + if (found == index) + return(func); + } + + return(NULL); +} + +static int is_bridge(struct pci_func * func) +{ + /* Check the header type */ + if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) { + dbg("%s: Is a bridge\n", __FUNCTION__); + return 1; + } else { + dbg("%s: Not a bridge\n", __FUNCTION__); + return 0; + } +} + + +/* the following routines constitute the bulk of the + hotplug controller logic + */ + + +/** + * board_added - Called after a board has been added to the system. + * + * Turns power on for the board + * Configures board + * + */ +static u32 board_added(struct pci_func * func, struct controller * ctrl) +{ + u8 hp_slot, slot; + u8 slots_not_empty = 0; + int index; + u32 temp_register = 0xFFFFFFFF; + u32 retval, rc = 0; + struct pci_func *new_func = NULL; + struct pci_func *t_func = NULL; + struct slot *p_slot, *pslot; + struct resource_lists res_lists; + enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed; + u8 pi, mode; + + p_slot = shpchp_find_slot(ctrl, func->device); + hp_slot = func->device - ctrl->slot_device_offset; + + dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, + func->device, ctrl->slot_device_offset, hp_slot); + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Power on slot without connecting to bus */ + rc = p_slot->hpc_ops->power_on_slot(p_slot); + if (rc) { + err("%s: Failed to power on slot\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return -1; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Failed to power on slot, error code(%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return -1; + } + + rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed); + /* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */ + /* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC, 0xa = PCI-X 133 Mhz 266, */ + /* 0xd = PCI-X 133 Mhz 533 */ + /* This encoding is different from the one used in cur_bus_speed & */ + /* max_bus_speed */ + + if (rc || adapter_speed == PCI_SPEED_UNKNOWN) { + err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed); + if (rc || bus_speed == PCI_SPEED_UNKNOWN) { + err("%s: Can't get bus operation speed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed); + if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) { + err("%s: Can't get max bus operation speed\n", __FUNCTION__); + max_bus_speed = bus_speed; + } + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->get_prog_int(p_slot, &pi); + if (rc) { + err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__); + pi = 1; + } + + if (pi == 2) { + for ( slot = 0; slot < ctrl->num_slots; slot++) { + if (slot != hp_slot) { + pslot = shpchp_find_slot(ctrl, slot + ctrl->slot_device_offset); + t_func = shpchp_slot_find(pslot->bus, pslot->device, 0); + slots_not_empty |= t_func->is_a_board; + } + } + + switch (adapter_speed) { + case PCI_SPEED_133MHz_PCIX_533: + case PCI_SPEED_133MHz_PCIX_266: + if ((( bus_speed < 0xa ) || (bus_speed < 0xd)) && (max_bus_speed > bus_speed) && + ((max_bus_speed <= 0xa) || (max_bus_speed <= 0xd)) && (!slots_not_empty)) { + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } + break; + case PCI_SPEED_133MHz_PCIX_ECC: + case PCI_SPEED_133MHz_PCIX: + + rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode); + + if (rc) { + err("%s: PI is 1 \n", __FUNCTION__); + return WRONG_BUS_FREQUENCY; + } + + if (mode) { /* Bus - Mode 1 ECC */ + + if (bus_speed > 0x7) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + + if ((bus_speed < 0x7) && (max_bus_speed <= 0x7) && + (bus_speed < max_bus_speed) && (!slots_not_empty)) { + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } + } else { + if (bus_speed > 0x4) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + + if ((bus_speed < 0x4) && (max_bus_speed <= 0x4) && + (bus_speed < max_bus_speed) && (!slots_not_empty)) { + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } + } + break; + case PCI_SPEED_66MHz_PCIX_ECC: + case PCI_SPEED_66MHz_PCIX: + + rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode); + + if (rc) { + err("%s: PI is 1 \n", __FUNCTION__); + return WRONG_BUS_FREQUENCY; + } + + if (mode) { /* Bus - Mode 1 ECC */ + + if (bus_speed > 0x5) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + + if ((bus_speed < 0x5) && (max_bus_speed <= 0x5) && + (bus_speed < max_bus_speed) && (!slots_not_empty)) { + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } + } else { + if (bus_speed > 0x2) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + + if ((bus_speed < 0x2) && (max_bus_speed <= 0x2) && + (bus_speed < max_bus_speed) && (!slots_not_empty)) { + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } + } + break; + case PCI_SPEED_66MHz: + if (bus_speed > 0x1) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + if (bus_speed == 0x1) + ; + if ((bus_speed == 0x0) && ( max_bus_speed == 0x1)) { + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } + break; + case PCI_SPEED_33MHz: + if (bus_speed > 0x0) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + break; + default: + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + } else { + /* if adpater_speed == bus_speed, nothing to do here */ + if (adapter_speed != bus_speed) { + for ( slot = 0; slot < ctrl->num_slots; slot++) { + if (slot != hp_slot) { + pslot = shpchp_find_slot(ctrl, slot + ctrl->slot_device_offset); + t_func = shpchp_slot_find(pslot->bus, pslot->device, 0); + slots_not_empty |= t_func->is_a_board; + } + } + + if (slots_not_empty != 0) { /* Other slots on the same bus are occupied */ + if ( adapter_speed < bus_speed ) { + err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); + return WRONG_BUS_FREQUENCY; + } + /* Do nothing if adapter_speed >= bus_speed */ + } + } + + if ((adapter_speed != bus_speed) && (slots_not_empty == 0)) { + /* Other slots on the same bus are empty */ + + rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed); + if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) { + err("%s: Can't get max bus operation speed\n", __FUNCTION__); + max_bus_speed = bus_speed; + } + + if (max_bus_speed == bus_speed) { + /* if adapter_speed >= bus_speed, do nothing */ + if (adapter_speed < bus_speed) { + /* + * Try to lower bus speed to accommodate the adapter if other slots + * on the same controller are empty + */ + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, adapter_speed); + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + } + } else { + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* max_bus_speed != bus_speed. Note: max_bus_speed should be > than bus_speed */ + if (adapter_speed < max_bus_speed) + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, adapter_speed); + else + rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed); + + if (rc) { + err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", + __FUNCTION__); + err("%s: Error code (%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return WRONG_BUS_FREQUENCY; + } + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + } + } + } + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Turn on board, blink green LED, turn off Amber LED */ + rc = p_slot->hpc_ops->slot_enable(p_slot); + + if (rc) { + err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq (ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + /* Wait for ~1 second */ + dbg("%s: before long_delay\n", __FUNCTION__); + wait_for_ctrl_irq (ctrl); + dbg("%s: afterlong_delay\n", __FUNCTION__); + + dbg("%s: func status = %x\n", __FUNCTION__, func->status); + /* Check for a power fault */ + if (func->status == 0xFF) { + /* power fault occurred, but it was benign */ + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register); + rc = POWER_FAILURE; + func->status = 0; + } else { + /* Get vendor/device ID u32 */ + rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); + dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc); + dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register); + + if (rc != 0) { + /* Something's wrong here */ + temp_register = 0xFFFFFFFF; + dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register); + } + /* Preset return code. It will be changed later if things go okay. */ + rc = NO_ADAPTER_PRESENT; + } + + /* All F's is an empty slot or an invalid board */ + if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */ + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + res_lists.irqs = NULL; + + rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0); + dbg("%s: back from configure_new_device\n", __FUNCTION__); + + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + shpchp_resource_sort_and_combine(&(ctrl->mem_head)); + shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); + shpchp_resource_sort_and_combine(&(ctrl->io_head)); + shpchp_resource_sort_and_combine(&(ctrl->bus_head)); + + if (rc) { + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* turn off slot, turn on Amber LED, turn off Green LED */ + retval = p_slot->hpc_ops->slot_disable(p_slot); + if (retval) { + err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return retval; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + retval = p_slot->hpc_ops->check_cmd_status(ctrl); + if (retval) { + err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return retval; + } + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + return(rc); + } + shpchp_save_slot_config(ctrl, func); + + func->status = 0; + func->switch_save = 0x10; + func->is_a_board = 0x01; + + /* Next, we will instantiate the linux pci_dev structures + * (with appropriate driver notification, if already present) + */ + index = 0; + do { + new_func = shpchp_slot_find(ctrl->slot_bus, func->device, index++); + if (new_func && !new_func->pci_dev) { + dbg("%s:call pci_hp_configure_dev\n", __FUNCTION__); + shpchp_configure_device(ctrl, new_func); + } + } while (new_func); + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->green_led_on(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + } else { + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* turn off slot, turn on Amber LED, turn off Green LED */ + rc = p_slot->hpc_ops->slot_disable(p_slot); + if (rc) { + err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + return(rc); + } + return 0; +} + + +/** + * remove_board - Turns off slot and LED's + * + */ +static u32 remove_board(struct pci_func *func, struct controller *ctrl) +{ + int index; + u8 skip = 0; + u8 device; + u8 hp_slot; + u32 rc; + struct resource_lists res_lists; + struct pci_func *temp_func; + struct slot *p_slot; + + if (func == NULL) + return(1); + + if (shpchp_unconfigure_device(func)) + return(1); + + device = func->device; + + hp_slot = func->device - ctrl->slot_device_offset; + p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); + + if ((ctrl->add_support) && + !(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) { + /* Here we check to see if we've saved any of the board's + * resources already. If so, we'll skip the attempt to + * determine what's being used. + */ + index = 0; + + temp_func = func; + + while ((temp_func = shpchp_slot_find(temp_func->bus, temp_func->device, index++))) { + if (temp_func->bus_head || temp_func->mem_head + || temp_func->p_mem_head || temp_func->io_head) { + skip = 1; + break; + } + } + + if (!skip) + rc = shpchp_save_used_resources(ctrl, func, DISABLE_CARD); + } + /* Change status to shutdown */ + if (func->is_a_board) + func->status = 0x01; + func->configured = 0; + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* Turn off slot, turn on Amber LED, turn off Green LED */ + rc = p_slot->hpc_ops->slot_disable(p_slot); + if (rc) { + err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + rc = p_slot->hpc_ops->check_cmd_status(ctrl); + if (rc) { + err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + + rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); + if (rc) { + err("%s: Issue of Set Attention command failed\n", __FUNCTION__); + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + return rc; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + if (ctrl->add_support) { + while (func) { + res_lists.io_head = ctrl->io_head; + res_lists.mem_head = ctrl->mem_head; + res_lists.p_mem_head = ctrl->p_mem_head; + res_lists.bus_head = ctrl->bus_head; + + dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n", + func->bus, func->device, func->function); + + shpchp_return_board_resources(func, &res_lists); + + ctrl->io_head = res_lists.io_head; + ctrl->mem_head = res_lists.mem_head; + ctrl->p_mem_head = res_lists.p_mem_head; + ctrl->bus_head = res_lists.bus_head; + + shpchp_resource_sort_and_combine(&(ctrl->mem_head)); + shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); + shpchp_resource_sort_and_combine(&(ctrl->io_head)); + shpchp_resource_sort_and_combine(&(ctrl->bus_head)); + + if (is_bridge(func)) { + dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", + ctrl->seg, func->bus, func->device, func->function); + bridge_slot_remove(func); + } else { + dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", + ctrl->seg, func->bus, func->device, func->function); + slot_remove(func); + } + + func = shpchp_slot_find(ctrl->slot_bus, device, 0); + } + + /* Setup slot structure with entry for empty slot */ + func = shpchp_slot_create(ctrl->slot_bus); + + if (func == NULL) { + return(1); + } + + func->bus = ctrl->slot_bus; + func->device = device; + func->function = 0; + func->configured = 0; + func->switch_save = 0x10; + func->is_a_board = 0; + } + + return 0; +} + + +static void pushbutton_helper_thread (unsigned long data) +{ + pushbutton_pending = data; + + up(&event_semaphore); +} + + +/* This is the main worker thread */ +static int event_thread(void* data) +{ + struct controller *ctrl; + lock_kernel(); + daemonize(); + + /* New name */ + strcpy(current->comm, "shpchpd_event"); + + unlock_kernel(); + + while (1) { + dbg("!!!!event_thread sleeping\n"); + down_interruptible (&event_semaphore); + dbg("event_thread woken finished = %d\n", event_finished); + if (event_finished || signal_pending(current)) + break; + /* Do stuff here */ + if (pushbutton_pending) + shpchp_pushbutton_thread(pushbutton_pending); + else + for (ctrl = shpchp_ctrl_list; ctrl; ctrl=ctrl->next) + interrupt_event_handler(ctrl); + } + dbg("event_thread signals exit\n"); + up(&event_exit); + return 0; +} + +int shpchp_event_start_thread (void) +{ + int pid; + + /* Initialize our semaphores */ + init_MUTEX_LOCKED(&event_exit); + event_finished=0; + + init_MUTEX_LOCKED(&event_semaphore); + pid = kernel_thread(event_thread, 0, 0); + + if (pid < 0) { + err ("Can't start up our event thread\n"); + return -1; + } + dbg("Our event thread pid = %d\n", pid); + return 0; +} + + +void shpchp_event_stop_thread (void) +{ + event_finished = 1; + dbg("event_thread finish command given\n"); + up(&event_semaphore); + dbg("wait for event_thread to exit\n"); + down(&event_exit); +} + + +static int update_slot_info (struct slot *slot) +{ + struct hotplug_slot_info *info; + char buffer[SLOT_NAME_SIZE]; + int result; + + info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); + + slot->hpc_ops->get_power_status(slot, &(info->power_status)); + slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); + slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); + slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); + + result = pci_hp_change_slot_info(buffer, info); + kfree (info); + return result; +} + +static void interrupt_event_handler(struct controller *ctrl) +{ + int loop = 0; + int change = 1; + struct pci_func *func; + u8 hp_slot; + u8 getstatus; + struct slot *p_slot; + + dbg("%s:\n", __FUNCTION__); + while (change) { + change = 0; + + for (loop = 0; loop < 10; loop++) { + if (ctrl->event_queue[loop].event_type != 0) { + dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop, + ctrl->event_queue[loop].event_type); + hp_slot = ctrl->event_queue[loop].hp_slot; + + func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0); + + p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); + + dbg("%s:hp_slot %d, func %p, p_slot %p\n", __FUNCTION__,hp_slot, func, p_slot); + + if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { + dbg("%s: button cancel\n", __FUNCTION__); + del_timer(&p_slot->task_event); + + switch (p_slot->state) { + case BLINKINGOFF_STATE: + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->green_led_on(p_slot); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->set_attention_status(p_slot, 0); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + break; + case BLINKINGON_STATE: + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->green_led_off(p_slot); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->set_attention_status(p_slot, 0); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + break; + default: + warn("Not a valid state\n"); + return; + } + info(msg_button_cancel, p_slot->number); + p_slot->state = STATIC_STATE; + } + /* Button Pressed (No action on 1st press...) */ + else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { + dbg("%s: Button pressed\n", __FUNCTION__); + + p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (getstatus) { + /* slot is on */ + dbg("%s: slot is on\n", __FUNCTION__); + p_slot->state = BLINKINGOFF_STATE; + info(msg_button_off, p_slot->number); + } else { + /* slot is off */ + dbg("%s: slot is off\n", __FUNCTION__); + p_slot->state = BLINKINGON_STATE; + info(msg_button_on, p_slot->number); + } + + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + /* blink green LED and turn off amber */ + p_slot->hpc_ops->green_led_blink(p_slot); + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->set_attention_status(p_slot, 0); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + + init_timer(&p_slot->task_event); + p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ + p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; + p_slot->task_event.data = (unsigned long) p_slot; + + dbg("%s: add_timer p_slot = %p\n", __FUNCTION__, (void *) p_slot); + add_timer(&p_slot->task_event); + } + /***********POWER FAULT********************/ + else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { + dbg("%s: power fault\n", __FUNCTION__); + /* Wait for exclusive access to hardware */ + down(&ctrl->crit_sect); + + p_slot->hpc_ops->set_attention_status(p_slot, 1); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + p_slot->hpc_ops->green_led_off(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(ctrl); + + /* Done with exclusive hardware access */ + up(&ctrl->crit_sect); + } else { + /* refresh notification */ + if (p_slot) + update_slot_info(p_slot); + } + + ctrl->event_queue[loop].event_type = 0; + + change = 1; + } + } /* End of FOR loop */ + } + + return; +} + + +/** + * shpchp_pushbutton_thread + * + * Scheduled procedure to handle blocking stuff for the pushbuttons + * Handles all pending events and exits. + * + */ +void shpchp_pushbutton_thread (unsigned long slot) +{ + struct slot *p_slot = (struct slot *) slot; + u8 getstatus; + int rc; + + pushbutton_pending = 0; + + if (!p_slot) { + dbg("%s: Error! slot NULL\n", __FUNCTION__); + return; + } + + p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (getstatus) { + p_slot->state = POWEROFF_STATE; + dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); + + if (shpchp_disable_slot(p_slot)) { + /* Wait for exclusive access to hardware */ + down(&p_slot->ctrl->crit_sect); + + /* Turn on the Attention LED */ + rc = p_slot->hpc_ops->set_attention_status(p_slot, 1); + if (rc) { + err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__); + return; + } + + /* Wait for the command to complete */ + wait_for_ctrl_irq(p_slot->ctrl); + + /* Done with exclusive hardware access */ + up(&p_slot->ctrl->crit_sect); + } + p_slot->state = STATIC_STATE; + } else { + p_slot->state = POWERON_STATE; + dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device); + + if (shpchp_enable_slot(p_slot)) { + /* Wait for exclusive access to hardware */ + down(&p_slot->ctrl->crit_sect); + + /* Turn off the green LED */ + rc = p_slot->hpc_ops->set_attention_status(p_slot, 1); + if (rc) { + err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__); + return; + } + /* Wait for the command to complete */ + wait_for_ctrl_irq(p_slot->ctrl); + + p_slot->hpc_ops->green_led_off(p_slot); + + /* Wait for the command to complete */ + wait_for_ctrl_irq(p_slot->ctrl); + + /* Done with exclusive hardware access */ + up(&p_slot->ctrl->crit_sect); + } + p_slot->state = STATIC_STATE; + } + + return; +} + + +int shpchp_enable_slot (struct slot *p_slot) +{ + u8 getstatus = 0; + int rc; + struct pci_func *func; + + func = shpchp_slot_find(p_slot->bus, p_slot->device, 0); + if (!func) { + dbg("%s: Error! slot NULL\n", __FUNCTION__); + return (1); + } + + /* Check to see if (latch closed, card present, power off) */ + down(&p_slot->ctrl->crit_sect); + rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + if (rc || !getstatus) { + info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + if (rc || getstatus) { + info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (rc || getstatus) { + info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + up(&p_slot->ctrl->crit_sect); + + slot_remove(func); + + func = shpchp_slot_create(p_slot->bus); + if (func == NULL) + return (1); + + func->bus = p_slot->bus; + func->device = p_slot->device; + func->function = 0; + func->configured = 0; + func->is_a_board = 1; + + /* We have to save the presence info for these slots */ + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + func->switch_save = !getstatus? 0x10:0; + + rc = board_added(func, p_slot->ctrl); + if (rc) { + if (is_bridge(func)) + bridge_slot_remove(func); + else + slot_remove(func); + + /* Setup slot structure with entry for empty slot */ + func = shpchp_slot_create(p_slot->bus); + if (func == NULL) + return (1); /* Out of memory */ + + func->bus = p_slot->bus; + func->device = p_slot->device; + func->function = 0; + func->configured = 0; + func->is_a_board = 1; + + /* We have to save the presence info for these slots */ + p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save)); + p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + func->switch_save = !getstatus? 0x10:0; + } + + if (p_slot) + update_slot_info(p_slot); + + return rc; +} + + +int shpchp_disable_slot (struct slot *p_slot) +{ + u8 class_code, header_type, BCR; + u8 index = 0; + u8 getstatus = 0; + u32 rc = 0; + int ret = 0; + unsigned int devfn; + struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate; + struct pci_func *func; + + if (!p_slot->ctrl) + return (1); + + /* Check to see if (latch closed, card present, power on) */ + down(&p_slot->ctrl->crit_sect); + + ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); + if (ret || !getstatus) { + info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); + if (ret || getstatus) { + info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); + if (ret || !getstatus) { + info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); + up(&p_slot->ctrl->crit_sect); + return (0); + } + up(&p_slot->ctrl->crit_sect); + + func = shpchp_slot_find(p_slot->bus, p_slot->device, index++); + + /* Make sure there are no video controllers here + * for all func of p_slot + */ + while (func && !rc) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + /* Check the Class Code */ + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + if (rc) + return rc; + + if (class_code == PCI_BASE_CLASS_DISPLAY) { + /* Display/Video adapter (not supported) */ + rc = REMOVE_NOT_SUPPORTED; + } else { + /* See if it's a bridge */ + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + /* If it's a bridge, check the VGA Enable bit */ + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); + if (rc) + return rc; + + /* If the VGA Enable bit is set, remove isn't supported */ + if (BCR & PCI_BRIDGE_CTL_VGA) { + rc = REMOVE_NOT_SUPPORTED; + } + } + } + + func = shpchp_slot_find(p_slot->bus, p_slot->device, index++); + } + + func = shpchp_slot_find(p_slot->bus, p_slot->device, 0); + if ((func != NULL) && !rc) { + rc = remove_board(func, p_slot->ctrl); + } else if (!rc) + rc = 1; + + if (p_slot) + update_slot_info(p_slot); + + return(rc); +} + + +/** + * configure_new_device - Configures the PCI header information of one board. + * + * @ctrl: pointer to controller structure + * @func: pointer to function structure + * @behind_bridge: 1 if this is a recursive call, 0 if not + * @resources: pointer to set of resource lists + * + * Returns 0 if success + * + */ +static u32 configure_new_device (struct controller * ctrl, struct pci_func * func, + u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev) +{ + u8 temp_byte, function, max_functions, stop_it; + int rc; + u32 ID; + struct pci_func *new_slot; + struct pci_bus lpci_bus, *pci_bus; + int index; + + new_slot = func; + + dbg("%s\n", __FUNCTION__); + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + + /* Check for Multi-function device */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); + if (rc) { + dbg("%s: rc = %d\n", __FUNCTION__, rc); + return rc; + } + + if (temp_byte & 0x80) /* Multi-function device */ + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + rc = configure_new_function(ctrl, new_slot, behind_bridge, resources, bridge_bus, bridge_dev); + + if (rc) { + dbg("configure_new_function failed %d\n",rc); + index = 0; + + while (new_slot) { + new_slot = shpchp_slot_find(new_slot->bus, new_slot->device, index++); + + if (new_slot) + shpchp_return_board_resources(new_slot, resources); + } + + return(rc); + } + + function++; + + stop_it = 0; + + /* The following loop skips to the next present function + * and creates a board structure + */ + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); + + if (ID == 0xFFFFFFFF) { /* There's nothing there. */ + function++; + } else { /* There's something there */ + /* Setup slot structure. */ + new_slot = shpchp_slot_create(func->bus); + + if (new_slot == NULL) { + /* Out of memory */ + return(1); + } + + new_slot->bus = func->bus; + new_slot->device = func->device; + new_slot->function = function; + new_slot->is_a_board = 1; + new_slot->status = 0; + + stop_it++; + } + } + + } while (function < max_functions); + dbg("returning from configure_new_device\n"); + + return 0; +} + + +/* + * Configuration logic that involves the hotplug data structures and + * their bookkeeping + */ + + +/** + * configure_new_function - Configures the PCI header information of one device + * + * @ctrl: pointer to controller structure + * @func: pointer to function structure + * @behind_bridge: 1 if this is a recursive call, 0 if not + * @resources: pointer to set of resource lists + * + * Calls itself recursively for bridged devices. + * Returns 0 if success + * + */ +static int configure_new_function (struct controller * ctrl, struct pci_func * func, + u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev) +{ + int cloop; + u8 temp_byte; + u8 device; + u8 class_code; + u16 temp_word; + u32 rc; + u32 temp_register; + u32 base; + u32 ID; + unsigned int devfn; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_resource *hold_mem_node; + struct pci_resource *hold_p_mem_node; + struct pci_resource *hold_IO_node; + struct pci_resource *hold_bus_node; + struct irq_mapping irqs; + struct pci_func *new_slot; + struct pci_bus lpci_bus, *pci_bus; + struct resource_lists temp_resources; +#if defined(CONFIG_X86_64) + u8 IRQ = 0; +#endif + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + /* Check for Bridge */ + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); + if (rc) + return rc; + + if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + /* set Primary bus */ + dbg("set Primary bus = 0x%x\n", func->bus); + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); + if (rc) + return rc; + + /* find range of busses to use */ + bus_node = get_max_resource(&resources->bus_head, 1L); + + /* If we don't have any busses to allocate, we can't continue */ + if (!bus_node) { + err("Got NO bus resource to use\n"); + return -ENOMEM; + } + dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length); + + /* set Secondary bus */ + temp_byte = (u8)bus_node->base; + dbg("set Secondary bus = 0x%x\n", temp_byte); + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); + if (rc) + return rc; + + /* set subordinate bus */ + temp_byte = (u8)(bus_node->base + bus_node->length - 1); + dbg("set subordinate bus = 0x%x\n", temp_byte); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + if (rc) + return rc; + + /* Set HP parameters (Cache Line Size, Latency Timer) */ + rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE); + if (rc) + return rc; + + /* Setup the IO, memory, and prefetchable windows */ + + io_node = get_max_resource(&(resources->io_head), 0x1000L); + if (io_node) { + dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next); + } + + mem_node = get_max_resource(&(resources->mem_head), 0x100000L); + if (mem_node) { + dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next); + } + + if (resources->p_mem_head) + p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L); + else { + /* + * In some platform implementation, MEM and PMEM are not + * distinguished, and hence ACPI _CRS has only MEM entries + * for both MEM and PMEM. + */ + dbg("using MEM for PMEM\n"); + p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L); + } + if (p_mem_node) { + dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next); + } + + /* Set up the IRQ info */ + if (!resources->irqs) { + irqs.barber_pole = 0; + irqs.interrupt[0] = 0; + irqs.interrupt[1] = 0; + irqs.interrupt[2] = 0; + irqs.interrupt[3] = 0; + irqs.valid_INT = 0; + } else { + irqs.barber_pole = resources->irqs->barber_pole; + irqs.interrupt[0] = resources->irqs->interrupt[0]; + irqs.interrupt[1] = resources->irqs->interrupt[1]; + irqs.interrupt[2] = resources->irqs->interrupt[2]; + irqs.interrupt[3] = resources->irqs->interrupt[3]; + irqs.valid_INT = resources->irqs->valid_INT; + } + + /* Set up resource lists that are now aligned on top and bottom + * for anything behind the bridge. + */ + temp_resources.bus_head = bus_node; + temp_resources.io_head = io_node; + temp_resources.mem_head = mem_node; + temp_resources.p_mem_head = p_mem_node; + temp_resources.irqs = &irqs; + + /* Make copies of the nodes we are going to pass down so that + * if there is a problem,we can just use these to free resources + */ + hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + + if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { + if (hold_bus_node) + kfree(hold_bus_node); + if (hold_IO_node) + kfree(hold_IO_node); + if (hold_mem_node) + kfree(hold_mem_node); + if (hold_p_mem_node) + kfree(hold_p_mem_node); + + return(1); + } + + memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); + + bus_node->base += 1; + bus_node->length -= 1; + bus_node->next = NULL; + + /* If we have IO resources copy them and fill in the bridge's + * IO range registers + */ + if (io_node) { + memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); + io_node->next = NULL; + + /* Set IO base and Limit registers */ + RES_CHECK(io_node->base, 8); + temp_byte = (u8)(io_node->base >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); + + RES_CHECK(io_node->base + io_node->length - 1, 8); + temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + } else { + kfree(hold_IO_node); + hold_IO_node = NULL; + } + + /* If we have memory resources copy them and fill in the bridge's + * memory range registers. Otherwise, fill in the range + * registers with values that disable them. + */ + if (mem_node) { + memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); + mem_node->next = NULL; + + /* Set Mem base and Limit registers */ + RES_CHECK(mem_node->base, 16); + temp_word = (u32)(mem_node->base >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + RES_CHECK(mem_node->base + mem_node->length - 1, 16); + temp_word = (u32)((mem_node->base + mem_node->length - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + kfree(hold_mem_node); + hold_mem_node = NULL; + } + + /* If we have prefetchable memory resources copy them and + * fill in the bridge's memory range registers. Otherwise, + * fill in the range registers with values that disable them. + */ + if (p_mem_node) { + memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); + p_mem_node->next = NULL; + + /* Set Pre Mem base and Limit registers */ + RES_CHECK(p_mem_node->base, 16); + temp_word = (u32)(p_mem_node->base >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + RES_CHECK(p_mem_node->base + p_mem_node->length - 1, 16); + temp_word = (u32)((p_mem_node->base + p_mem_node->length - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + } else { + temp_word = 0xFFFF; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + kfree(hold_p_mem_node); + hold_p_mem_node = NULL; + } + + /* Adjust this to compensate for extra adjustment in first loop */ + irqs.barber_pole--; + + rc = 0; + + /* Here we actually find the devices and configure them */ + for (device = 0; (device <= 0x1F) && !rc; device++) { + irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; + + ID = 0xFFFFFFFF; + pci_bus->number = hold_bus_node->base; + pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + pci_bus->number = func->bus; + + if (ID != 0xFFFFFFFF) { /* device Present */ + /* Setup slot structure. */ + new_slot = shpchp_slot_create(hold_bus_node->base); + + if (new_slot == NULL) { + /* Out of memory */ + rc = -ENOMEM; + continue; + } + + new_slot->bus = hold_bus_node->base; + new_slot->device = device; + new_slot->function = 0; + new_slot->is_a_board = 1; + new_slot->status = 0; + + rc = configure_new_device(ctrl, new_slot, 1, &temp_resources, func->bus, func->device); + dbg("configure_new_device rc=0x%x\n",rc); + } /* End of IF (device in slot?) */ + } /* End of FOR loop */ + + if (rc) { + shpchp_destroy_resource_list(&temp_resources); + + return_resource(&(resources->bus_head), hold_bus_node); + return_resource(&(resources->io_head), hold_IO_node); + return_resource(&(resources->mem_head), hold_mem_node); + return_resource(&(resources->p_mem_head), hold_p_mem_node); + return(rc); + } + + /* save the interrupt routing information */ + if (resources->irqs) { + resources->irqs->interrupt[0] = irqs.interrupt[0]; + resources->irqs->interrupt[1] = irqs.interrupt[1]; + resources->irqs->interrupt[2] = irqs.interrupt[2]; + resources->irqs->interrupt[3] = irqs.interrupt[3]; + resources->irqs->valid_INT = irqs.valid_INT; + } else if (!behind_bridge) { + /* We need to hook up the interrupts here */ + for (cloop = 0; cloop < 4; cloop++) { + if (irqs.valid_INT & (0x01 << cloop)) { + rc = shpchp_set_irq(func->bus, func->device, + 0x0A + cloop, irqs.interrupt[cloop]); + if (rc) { + shpchp_destroy_resource_list (&temp_resources); + return_resource(&(resources->bus_head), hold_bus_node); + return_resource(&(resources->io_head), hold_IO_node); + return_resource(&(resources->mem_head), hold_mem_node); + return_resource(&(resources->p_mem_head), hold_p_mem_node); + return rc; + } + } + } /* end of for loop */ + } + + /* Return unused bus resources + * First use the temporary node to store information for the board + */ + if (hold_bus_node && bus_node && temp_resources.bus_head) { + hold_bus_node->length = bus_node->base - hold_bus_node->base; + + hold_bus_node->next = func->bus_head; + func->bus_head = hold_bus_node; + + temp_byte = (u8)(temp_resources.bus_head->base - 1); + + /* set subordinate bus */ + dbg("re-set subordinate bus = 0x%x\n", temp_byte); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); + + if (temp_resources.bus_head->length == 0) { + kfree(temp_resources.bus_head); + temp_resources.bus_head = NULL; + } else { + dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n", + func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length); + return_resource(&(resources->bus_head), temp_resources.bus_head); + } + } + + /* If we have IO space available and there is some left, + * return the unused portion + */ + if (hold_IO_node && temp_resources.io_head) { + io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), + &hold_IO_node, 0x1000); + + /* Check if we were able to split something off */ + if (io_node) { + hold_IO_node->base = io_node->base + io_node->length; + + RES_CHECK(hold_IO_node->base, 8); + temp_byte = (u8)((hold_IO_node->base) >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte); + + return_resource(&(resources->io_head), io_node); + } + + io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); + + /* Check if we were able to split something off */ + if (io_node) { + /* First use the temporary node to store information for the board */ + hold_IO_node->length = io_node->base - hold_IO_node->base; + + /* If we used any, add it to the board's list */ + if (hold_IO_node->length) { + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + + RES_CHECK(io_node->base - 1, 8); + temp_byte = (u8)((io_node->base - 1) >> 8); + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + + return_resource(&(resources->io_head), io_node); + } else { + /* it doesn't need any IO */ + temp_byte = 0x00; + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); + + return_resource(&(resources->io_head), io_node); + kfree(hold_IO_node); + } + } else { + /* It used most of the range */ + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + } else if (hold_IO_node) { + /* it used the whole range */ + hold_IO_node->next = func->io_head; + func->io_head = hold_IO_node; + } + + /* If we have memory space available and there is some left, + * return the unused portion + */ + if (hold_mem_node && temp_resources.mem_head) { + mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L); + + /* Check if we were able to split something off */ + if (mem_node) { + hold_mem_node->base = mem_node->base + mem_node->length; + + RES_CHECK(hold_mem_node->base, 16); + temp_word = (u32)((hold_mem_node->base) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word); + + return_resource(&(resources->mem_head), mem_node); + } + + mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L); + + /* Check if we were able to split something off */ + if (mem_node) { + /* First use the temporary node to store information for the board */ + hold_mem_node->length = mem_node->base - hold_mem_node->base; + + if (hold_mem_node->length) { + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + + /* configure end address */ + RES_CHECK(mem_node->base - 1, 16); + temp_word = (u32)((mem_node->base - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + /* Return unused resources to the pool */ + return_resource(&(resources->mem_head), mem_node); + } else { + /* it doesn't need any Mem */ + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->mem_head), mem_node); + kfree(hold_mem_node); + } + } else { + /* It used most of the range */ + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + } else if (hold_mem_node) { + /* It used the whole range */ + hold_mem_node->next = func->mem_head; + func->mem_head = hold_mem_node; + } + + /* If we have prefetchable memory space available and there is some + * left at the end, return the unused portion + */ + if (hold_p_mem_node && temp_resources.p_mem_head) { + p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), + &hold_p_mem_node, 0x100000L); + + /* Check if we were able to split something off */ + if (p_mem_node) { + hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; + + RES_CHECK(hold_p_mem_node->base, 16); + temp_word = (u32)((hold_p_mem_node->base) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } + + p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L); + + /* Check if we were able to split something off */ + if (p_mem_node) { + /* First use the temporary node to store information for the board */ + hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; + + /* If we used any, add it to the board's list */ + if (hold_p_mem_node->length) { + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + + RES_CHECK(p_mem_node->base - 1, 16); + temp_word = (u32)((p_mem_node->base - 1) >> 16); + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + } else { + /* It doesn't need any PMem */ + temp_word = 0x0000; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); + + return_resource(&(resources->p_mem_head), p_mem_node); + kfree(hold_p_mem_node); + } + } else { + /* It used the most of the range */ + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + } else if (hold_p_mem_node) { + /* It used the whole range */ + hold_p_mem_node->next = func->p_mem_head; + func->p_mem_head = hold_p_mem_node; + } + + /* We should be configuring an IRQ and the bridge's base address + * registers if it needs them. Although we have never seen such + * a device + */ + + shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE); + + dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, + func->bus, func->device, func->function); + } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + /* Standard device */ + u64 base64; + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + + if (class_code == PCI_BASE_CLASS_DISPLAY) + return (DEVICE_TYPE_NOT_SUPPORTED); + + /* Figure out IO and memory needs */ + for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { + temp_register = 0xFFFFFFFF; + + rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); + dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register, + func->bus, func->device, func->function); + + if (!temp_register) + continue; + + base64 = 0L; + if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) { + /* Map IO */ + + /* Set base = amount of IO space */ + base = temp_register & 0xFFFFFFFC; + base = ~base + 1; + + dbg("NEED IO length(0x%x)\n", base); + io_node = get_io_resource(&(resources->io_head),(ulong)base); + + /* Allocate the resource to the board */ + if (io_node) { + dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length); + base = (u32)io_node->base; + io_node->next = func->io_head; + func->io_head = io_node; + } else { + err("Got NO IO resource(length=0x%x)\n", base); + return -ENOMEM; + } + } else { /* Map MEM */ + int prefetchable = 1; + struct pci_resource **res_node = &func->p_mem_head; + char *res_type_str = "PMEM"; + u32 temp_register2; + + if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) { + prefetchable = 0; + res_node = &func->mem_head; + res_type_str++; + } + + base = temp_register & 0xFFFFFFF0; + base = ~base + 1; + + switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { + case PCI_BASE_ADDRESS_MEM_TYPE_32: + dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base); + + if (prefetchable && resources->p_mem_head) + mem_node=get_resource(&(resources->p_mem_head), (ulong)base); + else { + if (prefetchable) + dbg("using MEM for PMEM\n"); + mem_node=get_resource(&(resources->mem_head), (ulong)base); + } + + /* Allocate the resource to the board */ + if (mem_node) { + base = (u32)mem_node->base; + mem_node->next = *res_node; + *res_node = mem_node; + dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base, mem_node->length); + } else { + err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base); + return -ENOMEM; + } + break; + case PCI_BASE_ADDRESS_MEM_TYPE_64: + rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); + dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2, temp_register, base); + + if (prefetchable && resources->p_mem_head) + mem_node = get_resource(&(resources->p_mem_head), (ulong)base); + else { + if (prefetchable) + dbg("using MEM for PMEM\n"); + mem_node = get_resource(&(resources->mem_head), (ulong)base); + } + + /* Allocate the resource to the board */ + if (mem_node) { + base64 = mem_node->base; + mem_node->next = *res_node; + *res_node = mem_node; + dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32), (u32)base64, mem_node->length); + } else { + err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base); + return -ENOMEM; + } + break; + default: + dbg("reserved BAR type=0x%x\n", temp_register); + break; + } + + } + + if (base64) { + rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); + cloop += 4; + base64 >>= 32; + + if (base64) { + dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64); + base64 = 0x0L; + } + + rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64); + } else { + rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); + } + } /* End of base register loop */ + +#if defined(CONFIG_X86_64) + /* Figure out which interrupt pin this function uses */ + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte); + + /* If this function needs an interrupt and we are behind a bridge + and the pin is tied to something that's alread mapped, + set this one the same + */ + if (temp_byte && resources->irqs && + (resources->irqs->valid_INT & + (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { + /* We have to share with something already set up */ + IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03]; + } else { + /* Program IRQ based on card type */ + rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); + + if (class_code == PCI_BASE_CLASS_STORAGE) { + IRQ = shpchp_disk_irq; + } else { + IRQ = shpchp_nic_irq; + } + } + + /* IRQ Line */ + rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); + + if (!behind_bridge) { + rc = shpchp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); + if (rc) + return(1); + } else { + /* TBD - this code may also belong in the other clause of this If statement */ + resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; + resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; + } +#endif + /* Disable ROM base Address */ + temp_word = 0x00L; + rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word); + + /* Set HP parameters (Cache Line Size, Latency Timer) */ + rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL); + if (rc) + return rc; + + shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL); + + dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function); + } /* End of Not-A-Bridge else */ + else { + /* It's some strange type of PCI adapter (Cardbus?) */ + return(DEVICE_TYPE_NOT_SUPPORTED); + } + + func->configured = 1; + + return 0; +} + diff -urN linux-2.4.26/drivers/hotplug/shpchp_hpc.c linux-2.4.27/drivers/hotplug/shpchp_hpc.c --- linux-2.4.26/drivers/hotplug/shpchp_hpc.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchp_hpc.c 2004-08-07 16:26:04.769351503 -0700 @@ -0,0 +1,1615 @@ +/* + * Standard PCI Hot Plug Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "shpchp.h" + +#ifdef DEBUG +#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */ +#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */ +#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */ +#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */ +#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT) +#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE) +/* Redefine this flagword to set debug level */ +#define DEBUG_LEVEL DBG_K_STANDARD + +#define DEFINE_DBG_BUFFER char __dbg_str_buf[256]; + +#define DBG_PRINT( dbg_flags, args... ) \ + do { \ + if ( DEBUG_LEVEL & ( dbg_flags ) ) \ + { \ + int len; \ + len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \ + __FILE__, __LINE__, __FUNCTION__ ); \ + sprintf( __dbg_str_buf + len, args ); \ + printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \ + } \ + } while (0) + +#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]"); +#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]"); +#else +#define DEFINE_DBG_BUFFER +#define DBG_ENTER_ROUTINE +#define DBG_LEAVE_ROUTINE +#endif /* DEBUG */ + +/* Slot Available Register I field definition */ +#define SLOT_33MHZ 0x0000001f +#define SLOT_66MHZ_PCIX 0x00001f00 +#define SLOT_100MHZ_PCIX 0x001f0000 +#define SLOT_133MHZ_PCIX 0x1f000000 + +/* Slot Available Register II field definition */ +#define SLOT_66MHZ 0x0000001f +#define SLOT_66MHZ_PCIX_266 0x00000f00 +#define SLOT_100MHZ_PCIX_266 0x0000f000 +#define SLOT_133MHZ_PCIX_266 0x000f0000 +#define SLOT_66MHZ_PCIX_533 0x00f00000 +#define SLOT_100MHZ_PCIX_533 0x0f000000 +#define SLOT_133MHZ_PCIX_533 0xf0000000 + +/* Secondary Bus Configuration Register */ +/* For PI = 1, Bits 0 to 2 have been encoded as follows to show current bus speed/mode */ +#define PCI_33MHZ 0x0 +#define PCI_66MHZ 0x1 +#define PCIX_66MHZ 0x2 +#define PCIX_100MHZ 0x3 +#define PCIX_133MHZ 0x4 + +/* For PI = 2, Bits 0 to 3 have been encoded as follows to show current bus speed/mode */ +#define PCI_33MHZ 0x0 +#define PCI_66MHZ 0x1 +#define PCIX_66MHZ 0x2 +#define PCIX_100MHZ 0x3 +#define PCIX_133MHZ 0x4 +#define PCIX_66MHZ_ECC 0x5 +#define PCIX_100MHZ_ECC 0x6 +#define PCIX_133MHZ_ECC 0x7 +#define PCIX_66MHZ_266 0x9 +#define PCIX_100MHZ_266 0x0a +#define PCIX_133MHZ_266 0x0b +#define PCIX_66MHZ_533 0x11 +#define PCIX_100MHZ_533 0x12 +#define PCIX_133MHZ_533 0x13 + +/* Slot Configuration */ +#define SLOT_NUM 0x0000001F +#define FIRST_DEV_NUM 0x00001F00 +#define PSN 0x07FF0000 +#define UPDOWN 0x20000000 +#define MRLSENSOR 0x40000000 +#define ATTN_BUTTON 0x80000000 + +/* Slot Status Field Definitions */ +/* Slot State */ +#define PWR_ONLY 0x0001 +#define ENABLED 0x0002 +#define DISABLED 0x0003 + +/* Power Indicator State */ +#define PWR_LED_ON 0x0004 +#define PWR_LED_BLINK 0x0008 +#define PWR_LED_OFF 0x000c + +/* Attention Indicator State */ +#define ATTEN_LED_ON 0x0010 +#define ATTEN_LED_BLINK 0x0020 +#define ATTEN_LED_OFF 0x0030 + +/* Power Fault */ +#define pwr_fault 0x0040 + +/* Attention Button */ +#define ATTEN_BUTTON 0x0080 + +/* MRL Sensor */ +#define MRL_SENSOR 0x0100 + +/* 66 MHz Capable */ +#define IS_66MHZ_CAP 0x0200 + +/* PRSNT1#/PRSNT2# */ +#define SLOT_EMP 0x0c00 + +/* PCI-X Capability */ +#define NON_PCIX 0x0000 +#define PCIX_66 0x1000 +#define PCIX_133 0x3000 +#define PCIX_266 0x4000 /* For PI = 2 only */ +#define PCIX_533 0x5000 /* For PI = 2 only */ + +/* SHPC 'write' operations/commands */ + +/* Slot operation - 0x00h to 0x3Fh */ + +#define NO_CHANGE 0x00 + +/* Slot state - Bits 0 & 1 of controller command register */ +#define SET_SLOT_PWR 0x01 +#define SET_SLOT_ENABLE 0x02 +#define SET_SLOT_DISABLE 0x03 + +/* Power indicator state - Bits 2 & 3 of controller command register*/ +#define SET_PWR_ON 0x04 +#define SET_PWR_BLINK 0x08 +#define SET_PWR_OFF 0x0C + +/* Attention indicator state - Bits 4 & 5 of controller command register*/ +#define SET_ATTN_ON 0x010 +#define SET_ATTN_BLINK 0x020 +#define SET_ATTN_OFF 0x030 + +/* Set bus speed/mode A - 0x40h to 0x47h */ +#define SETA_PCI_33MHZ 0x40 +#define SETA_PCI_66MHZ 0x41 +#define SETA_PCIX_66MHZ 0x42 +#define SETA_PCIX_100MHZ 0x43 +#define SETA_PCIX_133MHZ 0x44 +#define RESERV_1 0x45 +#define RESERV_2 0x46 +#define RESERV_3 0x47 + +/* Set bus speed/mode B - 0x50h to 0x5fh */ +#define SETB_PCI_33MHZ 0x50 +#define SETB_PCI_66MHZ 0x51 +#define SETB_PCIX_66MHZ_PM 0x52 +#define SETB_PCIX_100MHZ_PM 0x53 +#define SETB_PCIX_133MHZ_PM 0x54 +#define SETB_PCIX_66MHZ_EM 0x55 +#define SETB_PCIX_100MHZ_EM 0x56 +#define SETB_PCIX_133MHZ_EM 0x57 +#define SETB_PCIX_66MHZ_266 0x58 +#define SETB_PCIX_100MHZ_266 0x59 +#define SETB_PCIX_133MHZ_266 0x5a +#define SETB_PCIX_66MHZ_533 0x5b +#define SETB_PCIX_100MHZ_533 0x5c +#define SETB_PCIX_133MHZ_533 0x5d + +/* Power-on all slots - 0x48h */ +#define SET_PWR_ON_ALL 0x48 + +/* Enable all slots - 0x49h */ +#define SET_ENABLE_ALL 0x49 + +/* SHPC controller command error code */ +#define SWITCH_OPEN 0x1 +#define INVALID_CMD 0x2 +#define INVALID_SPEED_MODE 0x4 + +/* For accessing SHPC Working Register Set */ +#define DWORD_SELECT 0x2 +#define DWORD_DATA 0x4 +#define BASE_OFFSET 0x0 + +/* Field Offset in Logical Slot Register - byte boundary */ +#define SLOT_EVENT_LATCH 0x2 +#define SLOT_SERR_INT_MASK 0x3 + +static spinlock_t hpc_event_lock; + +DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */ +static struct php_ctlr_state_s *php_ctlr_list_head = 0; /* HPC state linked list */ +static int ctlr_seq_num = 0; /* Controller sequenc # */ + +static spinlock_t list_lock; + +static void shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs); + +static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds); + +/* This is the interrupt polling timeout function. */ +static void int_poll_timeout(unsigned long lphp_ctlr) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr; + + DBG_ENTER_ROUTINE + + if ( !php_ctlr ) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return; + } + + /* Poll for interrupt events. regs == NULL => polling */ + shpc_isr( 0, (void *)php_ctlr, NULL ); + + init_timer(&php_ctlr->int_poll_timer); + if (!shpchp_poll_time) + shpchp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/ + + start_int_poll_timer(php_ctlr, shpchp_poll_time); + + return; +} + +/* This function starts the interrupt polling timer. */ +static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds) +{ + if (!php_ctlr) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return; + } + + if ( ( seconds <= 0 ) || ( seconds > 60 ) ) + seconds = 2; /* Clamp to sane value */ + + php_ctlr->int_poll_timer.function = &int_poll_timeout; + php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */ + php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ; + add_timer(&php_ctlr->int_poll_timer); + + return; +} + +static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 cmd_status; + int retval = 0; + u16 temp_word; + int i; + + DBG_ENTER_ROUTINE + + if (!php_ctlr) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + for (i = 0; i < 10; i++) { + cmd_status = readw(php_ctlr->creg + CMD_STATUS); + + if (!(cmd_status & 0x1)) + break; + /* Check every 0.1 sec for a total of 1 sec*/ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + } + + cmd_status = readw(php_ctlr->creg + CMD_STATUS); + + if (cmd_status & 0x1) { + /* After 1 sec and and the controller is still busy */ + err("%s : Controller is still busy after 1 sec.\n", __FUNCTION__); + return -1; + } + + ++t_slot; + temp_word = (t_slot << 8) | (cmd & 0xFF); + dbg("%s : t_slot %x cmd %x\n", __FUNCTION__, t_slot, cmd); + + /* To make sure the Controller Busy bit is 0 before we send out the + * command. + */ + writew(temp_word, php_ctlr->creg + CMD); + dbg("%s : temp_word written %x\n", __FUNCTION__, temp_word); + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_check_cmd_status(struct controller *ctrl) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; + u16 cmd_status; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + cmd_status = readw(php_ctlr->creg + CMD_STATUS) & 0x000F; + + switch (cmd_status >> 1) { + case 0: + retval = 0; + break; + case 1: + retval = SWITCH_OPEN; + err("%s: Switch opened!\n", __FUNCTION__); + break; + case 2: + retval = INVALID_CMD; + err("%s: Invalid HPC command!\n", __FUNCTION__); + break; + case 4: + retval = INVALID_SPEED_MODE; + err("%s: Invalid bus speed/mode!\n", __FUNCTION__); + break; + default: + retval = cmd_status; + } + + DBG_LEAVE_ROUTINE + return retval; +} + + +static int hpc_get_attention_status(struct slot *slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u32 slot_reg; + u16 slot_status; + u8 atten_led_state; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); + slot_status = (u16) slot_reg; + atten_led_state = (slot_status & 0x0030) >> 4; + + switch (atten_led_state) { + case 0: + *status = 0xFF; /* Reserved */ + break; + case 1: + *status = 1; /* On */ + break; + case 2: + *status = 2; /* Blink */ + break; + case 3: + *status = 0; /* Off */ + break; + default: + *status = 0xFF; + break; + } + + DBG_LEAVE_ROUTINE + return 0; +} + +static int hpc_get_power_status(struct slot * slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u32 slot_reg; + u16 slot_status; + u8 slot_state; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); + slot_status = (u16) slot_reg; + slot_state = (slot_status & 0x0003); + + switch (slot_state) { + case 0: + *status = 0xFF; + break; + case 1: + *status = 2; /* Powered only */ + break; + case 2: + *status = 1; /* Enabled */ + break; + case 3: + *status = 0; /* Disabled */ + break; + default: + *status = 0xFF; + break; + } + + DBG_LEAVE_ROUTINE + return retval; +} + + +static int hpc_get_latch_status(struct slot *slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u32 slot_reg; + u16 slot_status; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); + slot_status = (u16)slot_reg; + + *status = ((slot_status & 0x0100) == 0) ? 0 : 1; /* 0 -> close; 1 -> open */ + + DBG_LEAVE_ROUTINE + return 0; +} + +static int hpc_get_adapter_status(struct slot *slot, u8 *status) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u32 slot_reg; + u16 slot_status; + u8 card_state; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); + slot_status = (u16)slot_reg; + card_state = (u8)((slot_status & 0x0C00) >> 10); + *status = (card_state != 0x3) ? 1 : 0; + + DBG_LEAVE_ROUTINE + return 0; +} + +static int hpc_get_prog_int(struct slot *slot, u8 *prog_int) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + *prog_int = readb(php_ctlr->creg + PROG_INTERFACE); + + DBG_LEAVE_ROUTINE + return 0; +} + +static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u32 slot_reg; + u16 slot_status, sec_bus_status; + u8 m66_cap, pcix_cap, pi; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + pi = readb(php_ctlr->creg + PROG_INTERFACE); + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); + dbg("%s: pi = %d, slot_reg = %x\n", __FUNCTION__, pi, slot_reg); + slot_status = (u16) slot_reg; + dbg("%s: slot_status = %x\n", __FUNCTION__, slot_status); + sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG); + + pcix_cap = (u8) ((slot_status & 0x3000) >> 12); + dbg("%s: pcix_cap = %x\n", __FUNCTION__, pcix_cap); + m66_cap = (u8) ((slot_status & 0x0200) >> 9); + dbg("%s: m66_cap = %x\n", __FUNCTION__, m66_cap); + + if (pi == 2) { + switch (pcix_cap) { + case 0: + *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; + break; + case 1: + *value = PCI_SPEED_66MHz_PCIX; + break; + case 3: + *value = PCI_SPEED_133MHz_PCIX; + break; + case 4: + *value = PCI_SPEED_133MHz_PCIX_266; + break; + case 5: + *value = PCI_SPEED_133MHz_PCIX_533; + break; + case 2: /* Reserved */ + default: + *value = PCI_SPEED_UNKNOWN; + retval = -ENODEV; + break; + } + } else { + switch (pcix_cap) { + case 0: + *value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; + break; + case 1: + *value = PCI_SPEED_66MHz_PCIX; + break; + case 3: + *value = PCI_SPEED_133MHz_PCIX; + break; + case 2: /* Reserved */ + default: + *value = PCI_SPEED_UNKNOWN; + retval = -ENODEV; + break; + } + } + + dbg("Adapter speed = %d\n", *value); + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u16 sec_bus_status; + u8 pi; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + pi = readb(php_ctlr->creg + PROG_INTERFACE); + sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG); + + if (pi == 2) { + *mode = (sec_bus_status & 0x0100) >> 7; + } else { + retval = -1; + } + + dbg("Mode 1 ECC cap = %d\n", *mode); + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_query_power_fault(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u32 slot_reg; + u16 slot_status; + u8 pwr_fault_state, status; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot)); + slot_status = (u16) slot_reg; + pwr_fault_state = (slot_status & 0x0040) >> 7; + status = (pwr_fault_state == 1) ? 0 : 1; + + DBG_LEAVE_ROUTINE + /* Note: Logic 0 => fault */ + return status; +} + +static int hpc_set_attention_status(struct slot *slot, u8 value) +{ + struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd = 0; + int rc = 0; + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + switch (value) { + case 0 : + slot_cmd = 0x30; /* OFF */ + break; + case 1: + slot_cmd = 0x10; /* ON */ + break; + case 2: + slot_cmd = 0x20; /* BLINK */ + break; + default: + return -1; + } + + shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + return rc; +} + + +static void hpc_set_green_led_on(struct slot *slot) +{ + struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd; + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return ; + } + + slot_cmd = 0x04; + + shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + return; +} + +static void hpc_set_green_led_off(struct slot *slot) +{ + struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd; + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return ; + } + + slot_cmd = 0x0C; + + shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + return; +} + +static void hpc_set_green_led_blink(struct slot *slot) +{ + struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd; + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return ; + } + + slot_cmd = 0x08; + + shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + return; +} + +int shpc_get_ctlr_slot_config(struct controller *ctrl, + int *num_ctlr_slots, /* number of slots in this HPC */ + int *first_device_num, /* PCI dev num of the first slot in this SHPC */ + int *physical_slot_num, /* phy slot num of the first slot in this SHPC */ + int *updown, /* physical_slot_num increament: 1 or -1 */ + int *flags) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; + + DBG_ENTER_ROUTINE + + if (!ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + *first_device_num = php_ctlr->slot_device_offset; /* Obtained in shpc_init() */ + *num_ctlr_slots = php_ctlr->num_slots; /* Obtained in shpc_init() */ + + *physical_slot_num = (readl(php_ctlr->creg + SLOT_CONFIG) & PSN) >> 16; + dbg("%s: physical_slot_num = %x\n", __FUNCTION__, *physical_slot_num); + *updown = ((readl(php_ctlr->creg + SLOT_CONFIG) & UPDOWN ) >> 29) ? 1 : -1; + + DBG_LEAVE_ROUTINE + return 0; +} + +static void hpc_release_ctlr(struct controller *ctrl) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle; + struct php_ctlr_state_s *p, *p_prev; + + DBG_ENTER_ROUTINE + + if (!ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return ; + } + + if (shpchp_poll_mode) { + del_timer(&php_ctlr->int_poll_timer); + } else { + if (php_ctlr->irq) { + free_irq(php_ctlr->irq, ctrl); + php_ctlr->irq = 0; + } + } + if (php_ctlr->pci_dev) { + dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__); + iounmap(php_ctlr->creg); + release_mem_region(pci_resource_start(php_ctlr->pci_dev, 0), pci_resource_len(php_ctlr->pci_dev, 0)); + dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__); + php_ctlr->pci_dev = 0; + } + + spin_lock(&list_lock); + p = php_ctlr_list_head; + p_prev = NULL; + while (p) { + if (p == php_ctlr) { + if (p_prev) + p_prev->pnext = p->pnext; + else + php_ctlr_list_head = p->pnext; + break; + } else { + p_prev = p; + p = p->pnext; + } + } + spin_unlock(&list_lock); + + kfree(php_ctlr); + + DBG_LEAVE_ROUTINE + +} + +static int hpc_power_on_slot(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + slot_cmd = 0x01; + + retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + + DBG_LEAVE_ROUTINE + + return retval; +} + +static int hpc_slot_enable(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + /* 3A => Slot - Enable, Power Indicator - Blink, Attention Indicator - Off */ + slot_cmd = 0x3A; + + retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_slot_disable(struct slot * slot) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + u8 slot_cmd; + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + /* 1F => Slot - Disable, Power Indicator - Off, Attention Indicator - On */ + slot_cmd = 0x1F; + + retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd); + + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_enable_all_slots( struct slot *slot ) +{ + int retval = 0; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + retval = shpc_write_cmd(slot, 0, SET_ENABLE_ALL); + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + + DBG_LEAVE_ROUTINE + + return retval; +} + +static int hpc_pwr_on_all_slots(struct slot *slot) +{ + int retval = 0; + + DBG_ENTER_ROUTINE + + retval = shpc_write_cmd(slot, 0, SET_PWR_ON_ALL); + + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value) +{ + u8 slot_cmd; + u8 pi; + int retval = 0; + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + pi = readb(php_ctlr->creg + PROG_INTERFACE); + + if (pi == 1) { + switch (value) { + case 0: + slot_cmd = SETA_PCI_33MHZ; + break; + case 1: + slot_cmd = SETA_PCI_66MHZ; + break; + case 2: + slot_cmd = SETA_PCIX_66MHZ; + break; + case 3: + slot_cmd = SETA_PCIX_100MHZ; + break; + case 4: + slot_cmd = SETA_PCIX_133MHZ; + break; + default: + slot_cmd = PCI_SPEED_UNKNOWN; + retval = -ENODEV; + return retval; + } + } else { + switch (value) { + case 0: + slot_cmd = SETB_PCI_33MHZ; + break; + case 1: + slot_cmd = SETB_PCI_66MHZ; + break; + case 2: + slot_cmd = SETB_PCIX_66MHZ_PM; + break; + case 3: + slot_cmd = SETB_PCIX_100MHZ_PM; + break; + case 4: + slot_cmd = SETB_PCIX_133MHZ_PM; + break; + case 5: + slot_cmd = SETB_PCIX_66MHZ_EM; + break; + case 6: + slot_cmd = SETB_PCIX_100MHZ_EM; + break; + case 7: + slot_cmd = SETB_PCIX_133MHZ_EM; + break; + case 8: + slot_cmd = SETB_PCIX_66MHZ_266; + break; + case 9: + slot_cmd = SETB_PCIX_100MHZ_266; + break; + case 0xa: + slot_cmd = SETB_PCIX_133MHZ_266; + break; + case 0xb: + slot_cmd = SETB_PCIX_66MHZ_533; + break; + case 0xc: + slot_cmd = SETB_PCIX_100MHZ_533; + break; + case 0xd: + slot_cmd = SETB_PCIX_133MHZ_533; + break; + default: + slot_cmd = PCI_SPEED_UNKNOWN; + retval = -ENODEV; + return retval; + } + + } + retval = shpc_write_cmd(slot, 0, slot_cmd); + if (retval) { + err("%s: Write command failed!\n", __FUNCTION__); + return -1; + } + + DBG_LEAVE_ROUTINE + return retval; +} + +static void shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs) +{ + struct controller *ctrl = NULL; + struct php_ctlr_state_s *php_ctlr; + u8 schedule_flag = 0; + u8 temp_byte; + u32 temp_dword, intr_loc, intr_loc2; + int hp_slot; + + if (!dev_id) + return; + + if (!shpchp_poll_mode) { + ctrl = (struct controller *)dev_id; + php_ctlr = ctrl->hpc_ctlr_handle; + } else { + php_ctlr = (struct php_ctlr_state_s *) dev_id; + ctrl = (struct controller *)php_ctlr->callback_instance_id; + } + + if (!ctrl) + return; + + if (!php_ctlr || !php_ctlr->creg) + return; + + /* Check to see if it was our interrupt */ + intr_loc = readl(php_ctlr->creg + INTR_LOC); + + if (!intr_loc) + return; + + dbg("%s: intr_loc = %x\n",__FUNCTION__, intr_loc); + + if (!shpchp_poll_mode) { + /* Mask Global Interrupt Mask - see implementation note on p. 139 */ + /* of SHPC spec rev 1.0*/ + temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: Before masking global interrupt, temp_dword = %x\n", + __FUNCTION__, temp_dword); + temp_dword |= 0x00000001; + dbg("%s: After masking global interrupt, temp_dword = %x\n", + __FUNCTION__, temp_dword); + writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); + + intr_loc2 = readl(php_ctlr->creg + INTR_LOC); + dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2); + } + + if (intr_loc & 0x0001) { + /* + * Command Complete Interrupt Pending + * RO only - clear by writing 0 to the Command Completion + * Detect bit in Controller SERR-INT register + */ + temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: Before clearing CCIP, temp_dword = %x\n", + __FUNCTION__, temp_dword); + temp_dword &= 0xfffeffff; + dbg("%s: After clearing CCIP, temp_dword = %x\n", + __FUNCTION__, temp_dword); + writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); + + wake_up_interruptible(&ctrl->queue); + } + + if ((intr_loc = (intr_loc >> 1)) == 0) { + /* Unmask Global Interrupt Mask */ + temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: 1-Before unmasking global interrupt, temp_dword = %x\n", + __FUNCTION__, temp_dword); + temp_dword &= 0xfffffffe; + dbg("%s: 1-After unmasking global interrupt, temp_dword = %x\n", + __FUNCTION__, temp_dword); + writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); + return; + } + + for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) { + /* To find out which slot has interrupt pending */ + if ((intr_loc >> hp_slot) & 0x01) { + temp_dword = readl(php_ctlr->creg + SLOT1 + (4*hp_slot)); + dbg("%s: Slot %x with intr, temp_dword = %x\n", + __FUNCTION__, hp_slot, temp_dword); + temp_byte = (temp_dword >> 16) & 0xFF; + dbg("%s: Slot with intr, temp_byte = %x\n", + __FUNCTION__, temp_byte); + if ((php_ctlr->switch_change_callback) && (temp_byte & 0x08)) + schedule_flag += php_ctlr->switch_change_callback( + hp_slot, php_ctlr->callback_instance_id); + if ((php_ctlr->attention_button_callback) && (temp_byte & 0x04)) + schedule_flag += php_ctlr->attention_button_callback( + hp_slot, php_ctlr->callback_instance_id); + if ((php_ctlr->presence_change_callback) && (temp_byte & 0x01)) + schedule_flag += php_ctlr->presence_change_callback( + hp_slot , php_ctlr->callback_instance_id); + if ((php_ctlr->power_fault_callback) && (temp_byte & 0x12)) + schedule_flag += php_ctlr->power_fault_callback( + hp_slot, php_ctlr->callback_instance_id); + + /* Clear all slot events */ + temp_dword = 0xe01fffff; + dbg("%s: Clearing slot events, temp_dword = %x\n", + __FUNCTION__, temp_dword); + writel(temp_dword, php_ctlr->creg + SLOT1 + (4*hp_slot)); + + intr_loc2 = readl(php_ctlr->creg + INTR_LOC); + dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2); + } + } + + if (!shpchp_poll_mode) { + /* Unmask Global Interrupt Mask */ + temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: 2-Before unmasking global interrupt, temp_dword = %x\n", + __FUNCTION__, temp_dword); + temp_dword &= 0xfffffffe; + dbg("%s: 2-After unmasking global interrupt, temp_dword = %x\n", + __FUNCTION__, temp_dword); + writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE); + } + + return; +} + +static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; + int retval = 0; + u8 pi; + u32 slot_avail1, slot_avail2; + int slot_num; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + pi = readb(php_ctlr->creg + PROG_INTERFACE); + slot_avail1 = readl(php_ctlr->creg + SLOT_AVAIL1); + slot_avail2 = readl(php_ctlr->creg + SLOT_AVAIL2); + + if (pi == 2) { + if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_533) >> 27) ) != 0 ) + bus_speed = PCIX_133MHZ_533; + else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_533) >> 23) ) != 0 ) + bus_speed = PCIX_100MHZ_533; + else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_533) >> 19) ) != 0 ) + bus_speed = PCIX_66MHZ_533; + else if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_266) >> 15) ) != 0 ) + bus_speed = PCIX_133MHZ_266; + else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_266) >> 11) ) != 0 ) + bus_speed = PCIX_100MHZ_266; + else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_266) >> 7) ) != 0 ) + bus_speed = PCIX_66MHZ_266; + else if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 ) + bus_speed = PCIX_133MHZ; + else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 ) + bus_speed = PCIX_100MHZ; + else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 ) + bus_speed = PCIX_66MHZ; + else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 ) + bus_speed = PCI_66MHZ; + else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 ) + bus_speed = PCI_33MHZ; + else bus_speed = PCI_SPEED_UNKNOWN; + } else { + if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 ) + bus_speed = PCIX_133MHZ; + else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 ) + bus_speed = PCIX_100MHZ; + else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 ) + bus_speed = PCIX_66MHZ; + else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 ) + bus_speed = PCI_66MHZ; + else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 ) + bus_speed = PCI_33MHZ; + else bus_speed = PCI_SPEED_UNKNOWN; + } + + *value = bus_speed; + dbg("Max bus speed = %d\n", bus_speed); + DBG_LEAVE_ROUTINE + return retval; +} + +static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value) +{ + struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle; + enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN; + u16 sec_bus_status; + int retval = 0; + u8 pi; + + DBG_ENTER_ROUTINE + + if (!slot->ctrl->hpc_ctlr_handle) { + err("%s: Invalid HPC controller handle!\n", __FUNCTION__); + return -1; + } + + if (slot->hp_slot >= php_ctlr->num_slots) { + err("%s: Invalid HPC slot number!\n", __FUNCTION__); + return -1; + } + + pi = readb(php_ctlr->creg + PROG_INTERFACE); + sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG); + + if (pi == 2) { + switch (sec_bus_status & 0x000f) { + case 0: + bus_speed = PCI_SPEED_33MHz; + break; + case 1: + bus_speed = PCI_SPEED_66MHz; + break; + case 2: + bus_speed = PCI_SPEED_66MHz_PCIX; + break; + case 3: + bus_speed = PCI_SPEED_100MHz_PCIX; + break; + case 4: + bus_speed = PCI_SPEED_133MHz_PCIX; + break; + case 5: + bus_speed = PCI_SPEED_66MHz_PCIX_ECC; + break; + case 6: + bus_speed = PCI_SPEED_100MHz_PCIX_ECC; + break; + case 7: + bus_speed = PCI_SPEED_133MHz_PCIX_ECC; + break; + case 8: + bus_speed = PCI_SPEED_66MHz_PCIX_266; + break; + case 9: + bus_speed = PCI_SPEED_100MHz_PCIX_266; + break; + case 0xa: + bus_speed = PCI_SPEED_133MHz_PCIX_266; + break; + case 0xb: + bus_speed = PCI_SPEED_66MHz_PCIX_533; + break; + case 0xc: + bus_speed = PCI_SPEED_100MHz_PCIX_533; + break; + case 0xd: + bus_speed = PCI_SPEED_133MHz_PCIX_533; + break; + case 0xe: + case 0xf: + default: + bus_speed = PCI_SPEED_UNKNOWN; + break; + } + } else { + /* In the case where pi is undefined, default it to 1 */ + switch (sec_bus_status & 0x0007) { + case 0: + bus_speed = PCI_SPEED_33MHz; + break; + case 1: + bus_speed = PCI_SPEED_66MHz; + break; + case 2: + bus_speed = PCI_SPEED_66MHz_PCIX; + break; + case 3: + bus_speed = PCI_SPEED_100MHz_PCIX; + break; + case 4: + bus_speed = PCI_SPEED_133MHz_PCIX; + break; + case 5: + bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */ + break; + case 6: + bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */ + break; + case 7: + bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */ + break; + default: + bus_speed = PCI_SPEED_UNKNOWN; + break; + } + } + *value = bus_speed; + dbg("Current bus speed = %d\n", bus_speed); + DBG_LEAVE_ROUTINE + return retval; + +} + +static struct hpc_ops shpchp_hpc_ops = { + .power_on_slot = hpc_power_on_slot, + .slot_enable = hpc_slot_enable, + .slot_disable = hpc_slot_disable, + .enable_all_slots = hpc_enable_all_slots, + .pwr_on_all_slots = hpc_pwr_on_all_slots, + .set_bus_speed_mode = hpc_set_bus_speed_mode, + .set_attention_status = hpc_set_attention_status, + .get_power_status = hpc_get_power_status, + .get_attention_status = hpc_get_attention_status, + .get_latch_status = hpc_get_latch_status, + .get_adapter_status = hpc_get_adapter_status, + + .get_max_bus_speed = hpc_get_max_bus_speed, + .get_cur_bus_speed = hpc_get_cur_bus_speed, + .get_adapter_speed = hpc_get_adapter_speed, + .get_mode1_ECC_cap = hpc_get_mode1_ECC_cap, + .get_prog_int = hpc_get_prog_int, + + .query_power_fault = hpc_query_power_fault, + .green_led_on = hpc_set_green_led_on, + .green_led_off = hpc_set_green_led_off, + .green_led_blink = hpc_set_green_led_blink, + + .release_ctlr = hpc_release_ctlr, + .check_cmd_status = hpc_check_cmd_status, +}; + +int shpc_init(struct controller * ctrl, + struct pci_dev * pdev, + php_intr_callback_t attention_button_callback, + php_intr_callback_t switch_change_callback, + php_intr_callback_t presence_change_callback, + php_intr_callback_t power_fault_callback) +{ + struct php_ctlr_state_s *php_ctlr, *p; + void *instance_id = ctrl; + int rc; + u8 hp_slot; + static int first = 1; + u32 shpc_cap_offset, shpc_base_offset; + u32 tempdword, slot_reg; + u16 vendor_id, device_id; + u8 i; + + DBG_ENTER_ROUTINE + + spin_lock_init(&list_lock); + php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL); + + if (!php_ctlr) { /* allocate controller state data */ + err("%s: HPC controller memory allocation error!\n", __FUNCTION__); + goto abort; + } + + memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s)); + + php_ctlr->pci_dev = pdev; /* save pci_dev in context */ + + rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); + dbg("%s: Vendor ID: %x\n",__FUNCTION__, vendor_id); + if (rc) { + err("%s: Unable to read PCI COnfiguration data\n", __FUNCTION__); + goto abort_free_ctlr; + } + + rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); + dbg("%s: Device ID: %x\n",__FUNCTION__, device_id); + if (rc) { + err("%s: Unable to read PCI COnfiguration data\n", __FUNCTION__); + goto abort_free_ctlr; + } + + if ((vendor_id == PCI_VENDOR_ID_AMD) || (device_id == PCI_DEVICE_ID_AMD_GOLAM_7450)) { + shpc_base_offset = 0; /* amd shpc driver doesn't use this; assume 0 */ + } else { + if ((shpc_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC)) == 0) { + err("%s : shpc_cap_offset == 0\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: shpc_cap_offset = %x\n", __FUNCTION__, shpc_cap_offset); + + rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , BASE_OFFSET); + if (rc) { + err("%s : pci_word_config_byte failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + + rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &shpc_base_offset); + if (rc) { + err("%s : pci_read_config_dword failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + + for (i = 0; i <= 14; i++) { + rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , i); + if (rc) { + err("%s : pci_word_config_byte failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + + rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &tempdword); + if (rc) { + err("%s : pci_read_config_dword failed\n", __FUNCTION__); + goto abort_free_ctlr; + } + dbg("%s: offset %d: tempdword %x\n", __FUNCTION__,i, tempdword); + } + } + + if (first) { + spin_lock_init(&hpc_event_lock); + first = 0; + } + + dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn), pdev->irq); + for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) + if (pci_resource_len(pdev, rc) > 0) + dbg("pci resource[%d] start=0x%lx(len=0x%lx), shpc_base_offset %x\n", rc, + pci_resource_start(pdev, rc), pci_resource_len(pdev, rc), shpc_base_offset); + + info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + if (!request_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0), MY_NAME)) { + err("%s: cannot reserve MMIO region\n", __FUNCTION__); + goto abort_free_ctlr; + } + + php_ctlr->creg = (struct ctrl_reg *) + ioremap(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0)); + if (!php_ctlr->creg) { + err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__, pci_resource_len(pdev, 0), + pci_resource_start(pdev, 0) + shpc_base_offset); + release_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0)); + goto abort_free_ctlr; + } + dbg("%s: php_ctlr->creg %p\n", __FUNCTION__, php_ctlr->creg); + dbg("%s: physical addr %p\n", __FUNCTION__, (void*)pci_resource_start(pdev, 0)); + + init_MUTEX(&ctrl->crit_sect); + /* Ssetup wait queue */ + init_waitqueue_head(&ctrl->queue); + + /* Find the IRQ */ + php_ctlr->irq = pdev->irq; + dbg("HPC interrupt = %d\n", php_ctlr->irq); + + /* Save interrupt callback info */ + php_ctlr->attention_button_callback = attention_button_callback; + php_ctlr->switch_change_callback = switch_change_callback; + php_ctlr->presence_change_callback = presence_change_callback; + php_ctlr->power_fault_callback = power_fault_callback; + php_ctlr->callback_instance_id = instance_id; + + /* Return PCI Controller Info */ + php_ctlr->slot_device_offset = (readl(php_ctlr->creg + SLOT_CONFIG) & FIRST_DEV_NUM ) >> 8; + php_ctlr->num_slots = readl(php_ctlr->creg + SLOT_CONFIG) & SLOT_NUM; + dbg("%s: slot_device_offset %x\n", __FUNCTION__, php_ctlr->slot_device_offset); + dbg("%s: num_slots %x\n", __FUNCTION__, php_ctlr->num_slots); + + /* Mask Global Interrupt Mask & Command Complete Interrupt Mask */ + tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword); + tempdword = 0x0003000f; + writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE); + tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword); + + /* Mask the MRL sensor SERR Mask of individual slot in + * Slot SERR-INT Mask & clear all the existing event if any + */ + for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) { + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot ); + dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__, + hp_slot, slot_reg); + tempdword = 0xffffffff; + writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot)); + } + + if (shpchp_poll_mode) {/* Install interrupt polling code */ + /* Install and start the interrupt polling timer */ + init_timer(&php_ctlr->int_poll_timer); + start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */ + } else { + /* Installs the interrupt handler */ +#ifdef CONFIG_PCI_USE_VECTOR + rc = pci_enable_msi(pdev); + if (rc) { + info("Can't get msi for the hotplug controller\n"); + info("Use INTx for the hotplug controller\n"); + dbg("%s: rc = %x\n", __FUNCTION__, rc); + } else + php_ctlr->irq = pdev->irq; +#endif + rc = request_irq(php_ctlr->irq, shpc_isr, SA_SHIRQ, MY_NAME, (void *) ctrl); + dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, + ctlr_seq_num, rc); + if (rc) { + err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq); + goto abort_free_ctlr; + } + /* Execute OSHP method here */ + } + dbg("%s: Before adding HPC to HPC list\n", __FUNCTION__); + + /* Add this HPC instance into the HPC list */ + spin_lock(&list_lock); + if (php_ctlr_list_head == 0) { + php_ctlr_list_head = php_ctlr; + p = php_ctlr_list_head; + p->pnext = 0; + } else { + p = php_ctlr_list_head; + + while (p->pnext) + p = p->pnext; + + p->pnext = php_ctlr; + } + spin_unlock(&list_lock); + + ctlr_seq_num++; + ctrl->hpc_ctlr_handle = php_ctlr; + ctrl->hpc_ops = &shpchp_hpc_ops; + for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) { + slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot ); + dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__, + hp_slot, slot_reg); + tempdword = 0xe01fffff; + writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot)); + } + if (!shpchp_poll_mode) { + /* Unmask all general input interrupts and SERR */ + tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + tempdword = 0x0000000a; + writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE); + tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE); + dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword); + } + + dbg("%s: Leaving shpc_init\n", __FUNCTION__); + DBG_LEAVE_ROUTINE + return 0; + + /* We end up here for the many possible ways to fail this API. */ +abort_free_ctlr: + kfree(php_ctlr); +abort: + DBG_LEAVE_ROUTINE + return -1; +} diff -urN linux-2.4.26/drivers/hotplug/shpchp_pci.c linux-2.4.27/drivers/hotplug/shpchp_pci.c --- linux-2.4.26/drivers/hotplug/shpchp_pci.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchp_pci.c 2004-08-07 16:26:04.772351626 -0700 @@ -0,0 +1,1043 @@ +/* + * Standard Hot Plug Controller Driver + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "shpchp.h" +#ifndef CONFIG_IA64 +#include "../../arch/i386/kernel/pci-i386.h" /* horrible hack showing how processor dependant we are... */ +#endif + +static int is_pci_dev_in_use(struct pci_dev* dev) +{ + /* + * dev->driver will be set if the device is in use by a new-style + * driver -- otherwise, check the device's regions to see if any + * driver has claimed them + */ + + int i, inuse=0; + + if (dev->driver) return 1; /* Assume driver feels responsible */ + + for (i = 0; !dev->driver && !inuse && (i < 6); i++) { + if (!pci_resource_start(dev, i)) + continue; + + if (pci_resource_flags(dev, i) & IORESOURCE_IO) + inuse = check_region(pci_resource_start(dev, i), + pci_resource_len(dev, i)); + else if (pci_resource_flags(dev, i) & IORESOURCE_MEM) + inuse = check_mem_region(pci_resource_start(dev, i), + pci_resource_len(dev, i)); + } + + return inuse; + +} + + +static int pci_hp_remove_device(struct pci_dev *dev) +{ + if (is_pci_dev_in_use(dev)) { + err("***Cannot safely power down device -- " + "it appears to be in use***\n"); + return -EBUSY; + } + pci_remove_device(dev); + return 0; +} + + +static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_bus* bus = wrapped_bus->bus; + struct pci_dev* dev = wrapped_dev->dev; + struct pci_func *temp_func; + int i=0; + + dbg("%s: Enter\n", __FUNCTION__); + /* We need to fix up the hotplug function representation with the linux representation */ + do { + temp_func = shpchp_slot_find(dev->bus->number, dev->devfn >> 3, i++); + dbg("%s: i %d temp_func %p, bus %x dev %x\n", __FUNCTION__, i, temp_func, + dev->bus->number, dev->devfn >>3); + } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); + + if (temp_func) { + temp_func->pci_dev = dev; + dbg("%s: dev %p dev->irq %x\n", __FUNCTION__, dev, dev->irq); + } else { + /* We did not even find a hotplug rep of the function, create it + * This code might be taken out if we can guarantee the creation of functions + * in parallel (hotplug and Linux at the same time). + */ + dbg("@@@@@@@@@@@ shpchp_slot_create in %s\n", __FUNCTION__); + temp_func = shpchp_slot_create(bus->number); + if (temp_func == NULL) + return -ENOMEM; + temp_func->pci_dev = dev; + } + + /* Create /proc/bus/pci proc entry for this device and bus device is on */ + /* Notify the drivers of the change */ + if (temp_func->pci_dev) { + dbg("%s: PCI_ID=%04X:%04X\n", __FUNCTION__, temp_func->pci_dev->vendor, + temp_func->pci_dev->device); + dbg("%s: PCI BUS %x DEVFN %x\n", __FUNCTION__, temp_func->pci_dev->bus->number, + temp_func->pci_dev->devfn); + dbg("%s: PCI_SLOT_NAME %s\n", __FUNCTION__, + temp_func->pci_dev->slot_name); + pci_enable_device(temp_func->pci_dev); + pci_proc_attach_device(temp_func->pci_dev); + pci_announce_device_to_drivers(temp_func->pci_dev); + } + + return 0; +} + + +static int unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev* dev = wrapped_dev->dev; + + struct pci_func *temp_func; + int i=0; + + /* We need to remove the hotplug function representation with the linux representation */ + do { + temp_func = shpchp_slot_find(dev->bus->number, dev->devfn >> 3, i++); + if (temp_func) { + dbg("temp_func->function = %d\n", temp_func->function); + } + } while (temp_func && (temp_func->function != (dev->devfn & 0x07))); + + /* Now, remove the Linux Representation */ + if (dev) { + if (pci_hp_remove_device(dev) == 0) { + kfree(dev); /* Now, remove */ + } else { + return -1; /* problems while freeing, abort visitation */ + } + } + + if (temp_func) { + temp_func->pci_dev = NULL; + } else { + dbg("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn); + } + + return 0; +} + + +static int unconfigure_visit_pci_bus_phase2 (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev) +{ + struct pci_bus* bus = wrapped_bus->bus; + + /* The cleanup code for proc entries regarding buses should be in the kernel...*/ + if (bus->procdir) + dbg("detach_pci_bus %s\n", bus->procdir->name); + pci_proc_detach_bus(bus); + /* The cleanup code should live in the kernel... */ + bus->self->subordinate = NULL; + /* Unlink from parent bus */ + list_del(&bus->node); + + /* Now, remove */ + if (bus) + kfree(bus); + + return 0; +} + + +static int unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus) +{ + struct pci_dev* dev = wrapped_dev->dev; + int rc; + + dbg("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + /* Now, remove the Linux Driver Representation */ + if (dev->driver) { + if (dev->driver->remove) { + dev->driver->remove(dev); + dbg("driver was properly removed\n"); + } + dev->driver = NULL; + } + + rc = is_pci_dev_in_use(dev); + if (rc) + info("%s: device still in use\n", __FUNCTION__); + return rc; +} + + +static struct pci_visit configure_functions = { + .visit_pci_dev = configure_visit_pci_dev, +}; + + +static struct pci_visit unconfigure_functions_phase1 = { + .post_visit_pci_dev = unconfigure_visit_pci_dev_phase1 +}; + +static struct pci_visit unconfigure_functions_phase2 = { + .post_visit_pci_bus = unconfigure_visit_pci_bus_phase2, + .post_visit_pci_dev = unconfigure_visit_pci_dev_phase2 +}; + + +int shpchp_configure_device (struct controller* ctrl, struct pci_func* func) +{ + unsigned char bus; + struct pci_dev dev0; + struct pci_bus *child; + struct pci_dev* temp; + int rc = 0; + + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + + dbg("%s: Enter\n", __FUNCTION__); + memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); + + memset(&dev0, 0, sizeof(struct pci_dev)); + + dbg("%s: func->pci_dev %p\n", __FUNCTION__, func->pci_dev); + if (func->pci_dev != NULL) + dbg("%s: func->pci_dev->irq %x\n", __FUNCTION__, func->pci_dev->irq); + if (func->pci_dev == NULL) + func->pci_dev = pci_find_slot(func->bus, (func->device << 3) | (func->function & 0x7)); + dbg("%s: after pci_find_slot, func->pci_dev %p\n", __FUNCTION__, func->pci_dev); + if (func->pci_dev != NULL) + dbg("%s: after pci_find_slot, func->pci_dev->irq %x\n", __FUNCTION__, func->pci_dev->irq); + + /* Still NULL ? Well then scan for it ! */ + if (func->pci_dev == NULL) { + dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__); + dev0.bus = ctrl->pci_dev->subordinate; + dbg("%s: dev0.bus %p\n", __FUNCTION__, dev0.bus); + dev0.bus->number = func->bus; + dbg("%s: dev0.bus->number %x\n", __FUNCTION__, func->bus); + dev0.devfn = PCI_DEVFN(func->device, func->function); + dev0.sysdata = ctrl->pci_dev->sysdata; + + /* this will generate pci_dev structures for all functions, + * but we will only call this case when lookup fails + */ + dbg("%s: dev0.irq %x\n", __FUNCTION__, dev0.irq); + func->pci_dev = pci_scan_slot(&dev0); + dbg("%s: func->pci_dev->irq %x\n", __FUNCTION__, + func->pci_dev->irq); + if (func->pci_dev == NULL) { + dbg("ERROR: pci_dev still null\n"); + return 0; + } + } + + if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); + child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); + dbg("%s: calling pci_do_scan_bus\n", __FUNCTION__); + pci_do_scan_bus(child); + + } + + temp = func->pci_dev; + dbg("%s: func->pci_dev->irq %x\n", __FUNCTION__, func->pci_dev->irq); + + if (temp) { + wrapped_dev.dev = temp; + wrapped_bus.bus = temp->bus; + rc = pci_visit_dev(&configure_functions, &wrapped_dev, &wrapped_bus); + } + + dbg("%s: Exit\n", __FUNCTION__); + return rc; +} + + +int shpchp_unconfigure_device(struct pci_func* func) +{ + int rc = 0; + int j; + struct pci_dev_wrapped wrapped_dev; + struct pci_bus_wrapped wrapped_bus; + + memset(&wrapped_dev, 0, sizeof(struct pci_dev_wrapped)); + memset(&wrapped_bus, 0, sizeof(struct pci_bus_wrapped)); + + dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function); + + for (j=0; j<8 ; j++) { + struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j); + if (temp) { + wrapped_dev.dev = temp; + wrapped_bus.bus = temp->bus; + rc = pci_visit_dev(&unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus); + if (rc) + break; + + rc = pci_visit_dev(&unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus); + if (rc) + break; + } + } + return rc; +} + +/* + * shpchp_set_irq + * + * @bus_num: bus number of PCI device + * @dev_num: device number of PCI device + * @slot: pointer to u8 where slot number will be returned + */ +int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) +{ +#if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64) + int rc; + u16 temp_word; + struct pci_dev fakedev; + struct pci_bus fakebus; + + fakedev.devfn = dev_num << 3; + fakedev.bus = &fakebus; + fakebus.number = bus_num; + dbg("%s: dev %d, bus %d, pin %d, num %d\n", + __FUNCTION__, dev_num, bus_num, int_pin, irq_num); + rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); + dbg("%s: rc %d\n", __FUNCTION__, rc); + if (!rc) + return !rc; + + /* set the Edge Level Control Register (ELCR) */ + temp_word = inb(0x4d0); + temp_word |= inb(0x4d1) << 8; + + temp_word |= 0x01 << irq_num; + + /* This should only be for x86 as it sets the Edge Level Control Register */ + outb((u8) (temp_word & 0xFF), 0x4d0); + outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); +#endif + return 0; +} + +/* More PCI configuration routines; this time centered around hotplug controller */ + + +/* + * shpchp_save_config + * + * Reads configuration for all slots in a PCI bus and saves info. + * + * Note: For non-hot plug busses, the slot # saved is the device # + * + * returns 0 if success + */ +int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num) +{ + int rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + struct pci_func *new_slot; + int sub_bus; + int FirstSupported; + int LastSupported; + int max_functions; + int function; + u8 DevError; + int device = 0; + int cloop = 0; + int stop_it; + int index; + int is_hot_plug = num_ctlr_slots || first_device_num; + struct pci_bus lpci_bus, *pci_bus; + + dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, num_ctlr_slots, first_device_num); + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + + dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, num_ctlr_slots, first_device_num); + + /* Decide which slots are supported */ + if (is_hot_plug) { + /********************************* + * is_hot_plug is the slot mask + *********************************/ + FirstSupported = first_device_num; + LastSupported = FirstSupported + num_ctlr_slots - 1; + } else { + FirstSupported = 0; + LastSupported = 0x1F; + } + + dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, LastSupported); + + /* Save PCI configuration space for all devices in supported slots */ + pci_bus->number = busnumber; + for (device = FirstSupported; device <= LastSupported; device++) { + ID = 0xFFFFFFFF; + rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + + if (ID != 0xFFFFFFFF) { /* device in slot */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + dbg("class_code = %x, header_type = %x\n", class_code, header_type); + + /* If multi-function device, set max_functions to 8 */ + if (header_type & 0x80) + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + DevError = 0; + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* P-P Bridge */ + /* Recurse the subordinate bus + * get the subordinate bus number + */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); + if (rc) { + return rc; + } else { + sub_bus = (int) secondary_bus; + + /* Save secondary bus cfg spc with this recursive call. */ + rc = shpchp_save_config(ctrl, sub_bus, 0, 0); + if (rc) + return rc; + } + } + + index = 0; + new_slot = shpchp_slot_find(busnumber, device, index++); + + dbg("new_slot = %p\n", new_slot); + + while (new_slot && (new_slot->function != (u8) function)) { + new_slot = shpchp_slot_find(busnumber, device, index++); + dbg("new_slot = %p\n", new_slot); + } + if (!new_slot) { + /* Setup slot structure. */ + new_slot = shpchp_slot_create(busnumber); + dbg("new_slot = %p\n", new_slot); + + if (new_slot == NULL) + return(1); + } + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = (u8) function; + new_slot->is_a_board = 1; + new_slot->switch_save = 0x10; + /* In case of unsupported board */ + new_slot->status = DevError; + new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); + dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev); + + for (cloop = 0; cloop < 0x20; cloop++) { + rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot->config_space [cloop])); + dbg("%s: new_slot->config_space[%x] = %x\n", __FUNCTION__, cloop, new_slot->config_space[cloop]); + if (rc) + return rc; + } + + function++; + + stop_it = 0; + + /* This loop skips to the next present function + * reading in Class Code and Header type. + */ + + while ((function < max_functions)&&(!stop_it)) { + rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { /* nothing there. */ + function++; + dbg("Nothing there\n"); + } else { /* Something there */ + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); + if (rc) + return rc; + + rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; + + dbg("class_code = %x, header_type = %x\n", class_code, header_type); + stop_it++; + } + } + + } while (function < max_functions); + } /* End of IF (device in slot?) */ + else if (is_hot_plug) { + /* Setup slot structure with entry for empty slot */ + new_slot = shpchp_slot_create(busnumber); + + if (new_slot == NULL) { + return(1); + } + dbg("new_slot = %p\n", new_slot); + + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = 0; + new_slot->is_a_board = 0; + new_slot->presence_save = 0; + new_slot->switch_save = 0; + } + } /* End of FOR loop */ + + return(0); +} + + +/* + * shpchp_save_slot_config + * + * Saves configuration info for all PCI devices in a given slot + * including subordinate busses. + * + * returns 0 if success + */ +int shpchp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) +{ + int rc; + u8 class_code; + u8 header_type; + u32 ID; + u8 secondary_bus; + int sub_bus; + int max_functions; + int function; + int cloop = 0; + int stop_it; + struct pci_bus lpci_bus, *pci_bus; + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = new_slot->bus; + + ID = 0xFFFFFFFF; + + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); + + if (ID != 0xFFFFFFFF) { /* device in slot */ + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); + + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); + + if (header_type & 0x80) /* Multi-function device */ + max_functions = 8; + else + max_functions = 1; + + function = 0; + + do { + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + /* Recurse the subordinate bus */ + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); + + sub_bus = (int) secondary_bus; + + /* Save the config headers for the secondary bus. */ + rc = shpchp_save_config(ctrl, sub_bus, 0, 0); + + if (rc) + return(rc); + + } /* End of IF */ + + new_slot->status = 0; + + for (cloop = 0; cloop < 0x20; cloop++) { + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot->config_space [cloop])); + dbg("%s: new_slot->config_space[%x] = %x\n", __FUNCTION__,cloop, new_slot->config_space[cloop]); + } + + function++; + + stop_it = 0; + + /* this loop skips to the next present function + * reading in the Class Code and the Header type. + */ + + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { /* nothing there. */ + function++; + } else { /* Something there */ + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); + + pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); + + stop_it++; + } + } + + } while (function < max_functions); + } /* End of IF (device in slot?) */ + else { + return(2); + } + + return(0); +} + + +/* + * shpchp_save_used_resources + * + * Stores used resource information for existing boards. this is + * for boards that were in the system when this driver was loaded. + * this function is for hot plug ADD + * + * returns 0 if success + * if disable == 1(DISABLE_CARD), + * it loops for all functions of the slot and disables them. + * else, it just get resources of the function and return. + */ +int shpchp_save_used_resources (struct controller *ctrl, struct pci_func *func, int disable) +{ + u8 cloop; + u8 header_type; + u8 secondary_bus; + u8 temp_byte; + u16 command; + u16 save_command; + u16 w_base, w_length; + u32 temp_register; + u32 save_base; + u32 base, length; + u64 base64 = 0; + int index = 0; + unsigned int devfn; + struct pci_resource *mem_node = NULL; + struct pci_resource *p_mem_node = NULL; + struct pci_resource *t_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + struct pci_bus lpci_bus, *pci_bus; + + memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + + if (disable) + func = shpchp_slot_find(func->bus, func->device, index++); + + while ((func != NULL) && func->is_a_board) { + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + /* Save the command register */ + pci_bus_read_config_word (pci_bus, devfn, PCI_COMMAND, &save_command); + + if (disable) { + /* disable card */ + command = 0x00; + pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + } + + /* Check for Bridge */ + pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); + + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command); + if (disable) { + /* Clear Bridge Control Register */ + command = 0x00; + pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); + } + + pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); + pci_bus_read_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); + + bus_node =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!bus_node) + return -ENOMEM; + + bus_node->base = (ulong)secondary_bus; + bus_node->length = (ulong)(temp_byte - secondary_bus + 1); + + bus_node->next = func->bus_head; + func->bus_head = bus_node; + + /* Save IO base and Limit registers */ + pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_BASE, &temp_byte); + base = temp_byte; + pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); + length = temp_byte; + + if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; + io_node->length = (ulong)(length - base + 0x10) << 8; + + io_node->next = func->io_head; + func->io_head = io_node; + } + + /* Save memory base and Limit registers */ + pci_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_BASE, &w_base); + pci_bus_read_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = (ulong)w_base << 16; + mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + /* Save prefetchable memory base and Limit registers */ + pci_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); + pci_bus_read_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); + + if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = (ulong)w_base << 16; + p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } + } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { + dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command); + + /* Figure out IO and memory base lengths */ + for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { + pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); + + temp_register = 0xFFFFFFFF; + pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); + pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp_register); + + if (!disable) { + pci_bus_write_config_dword (pci_bus, devfn, cloop, save_base); + } + + if (!temp_register) + continue; + + base = temp_register; + + if ((base & PCI_BASE_ADDRESS_SPACE_IO) && (!disable || (save_command & PCI_COMMAND_IO))) { + /* IO base */ + /* set temp_register = amount of IO space requested */ + base = base & 0xFFFFFFFCL; + base = (~base) + 1; + + io_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; + io_node->length = (ulong)base; + dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", io_node->base, io_node->length); + + io_node->next = func->io_head; + func->io_head = io_node; + } else { /* map Memory */ + int prefetchable = 1; + /* struct pci_resources **res_node; */ + char *res_type_str = "PMEM"; + u32 temp_register2; + + t_mem_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL); + if (!t_mem_node) + return -ENOMEM; + + if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { + prefetchable = 0; + mem_node = t_mem_node; + res_type_str++; + } else + p_mem_node = t_mem_node; + + base = base & 0xFFFFFFF0L; + base = (~base) + 1; + + switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { + case PCI_BASE_ADDRESS_MEM_TYPE_32: + if (prefetchable) { + p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; + p_mem_node->length = (ulong)base; + dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str, p_mem_node->base, p_mem_node->length); + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else { + mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; + mem_node->length = (ulong)base; + dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str, mem_node->base, mem_node->length); + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + break; + case PCI_BASE_ADDRESS_MEM_TYPE_64: + pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); + base64 = temp_register2; + base64 = (base64 << 32) | save_base; + + if (temp_register2) { + dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", res_type_str, temp_register2, (u32)base64); + base64 &= 0x00000000FFFFFFFFL; + } + + if (prefetchable) { + p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; + p_mem_node->length = base; + dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str, p_mem_node->base, p_mem_node->length); + + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } else { + mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; + mem_node->length = base; + dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str, mem_node->base, mem_node->length); + + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + cloop += 4; + break; + default: + dbg("asur: reserved BAR type=0x%x\n", temp_register); + break; + } + } + } /* End of base register loop */ + } else { /* Some other unknown header type */ + dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", func->bus, func->device); + } + + /* find the next device in this slot */ + if (!disable) + break; + func = shpchp_slot_find(func->bus, func->device, index++); + } + + return(0); +} + + +/* + * shpchp_return_board_resources + * + * this routine returns all resources allocated to a board to + * the available pool. + * + * returns 0 if success + */ +int shpchp_return_board_resources(struct pci_func * func, struct resource_lists * resources) +{ + int rc = 0; + struct pci_resource *node; + struct pci_resource *t_node; + dbg("%s\n", __FUNCTION__); + + if (!func) + return(1); + + node = func->io_head; + func->io_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->io_head), node); + node = t_node; + } + + node = func->mem_head; + func->mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->mem_head), node); + node = t_node; + } + + node = func->p_mem_head; + func->p_mem_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->p_mem_head), node); + node = t_node; + } + + node = func->bus_head; + func->bus_head = NULL; + while (node) { + t_node = node->next; + return_resource(&(resources->bus_head), node); + node = t_node; + } + + rc |= shpchp_resource_sort_and_combine(&(resources->mem_head)); + rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head)); + rc |= shpchp_resource_sort_and_combine(&(resources->io_head)); + rc |= shpchp_resource_sort_and_combine(&(resources->bus_head)); + + return(rc); +} + + +/* + * shpchp_destroy_resource_list + * + * Puts node back in the resource list pointed to by head + */ +void shpchp_destroy_resource_list (struct resource_lists * resources) +{ + struct pci_resource *res, *tres; + + res = resources->io_head; + resources->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->mem_head; + resources->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->p_mem_head; + resources->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = resources->bus_head; + resources->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + + +/* + * shpchp_destroy_board_resources + * + * Puts node back in the resource list pointed to by head + */ +void shpchp_destroy_board_resources (struct pci_func * func) +{ + struct pci_resource *res, *tres; + + dbg("%s: \n", __FUNCTION__); + + res = func->io_head; + func->io_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->mem_head; + func->mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->p_mem_head; + func->p_mem_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } + + res = func->bus_head; + func->bus_head = NULL; + + while (res) { + tres = res; + res = res->next; + kfree(tres); + } +} + diff -urN linux-2.4.26/drivers/hotplug/shpchprm.h linux-2.4.27/drivers/hotplug/shpchprm.h --- linux-2.4.26/drivers/hotplug/shpchprm.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchprm.h 2004-08-07 16:26:04.772351626 -0700 @@ -0,0 +1,57 @@ +/* + * SHPCHPRM : SHPCHP Resource Manager for ACPI/non-ACPI platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#ifndef _SHPCHPRM_H_ +#define _SHPCHPRM_H_ + +#ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY +#include "shpchprm_legacy.h" +#else +#include "shpchprm_nonacpi.h" +#endif + +int shpchprm_init(enum php_ctlr_type ct); +void shpchprm_cleanup(void); +int shpchprm_print_pirt(void); +void *shpchprm_get_slot(struct slot *slot); +int shpchprm_find_available_resources(struct controller *ctrl); +int shpchprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type); +void shpchprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type); +int shpchprm_get_interrupt(int seg, int bus, int device, int pin, int *irq); +int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum); + +#ifdef DEBUG +#define RES_CHECK(this, bits) \ + { if (((this) & (bits - 1))) \ + printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); } +#else +#define RES_CHECK(this, bits) +#endif + +#endif /* _SHPCHPRM_H_ */ diff -urN linux-2.4.26/drivers/hotplug/shpchprm_acpi.c linux-2.4.27/drivers/hotplug/shpchprm_acpi.c --- linux-2.4.26/drivers/hotplug/shpchprm_acpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchprm_acpi.c 2004-08-07 16:26:04.775351749 -0700 @@ -0,0 +1,1696 @@ +/* + * SHPCHPRM ACPI: PHP Resource Manager for ACPI platform + * + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_IA64 +#include +#endif +#include +#include +#include +#include "shpchp.h" +#include "shpchprm.h" + +#define PCI_MAX_BUS 0x100 +#define ACPI_STA_DEVICE_PRESENT 0x01 + +#define METHOD_NAME__SUN "_SUN" +#define METHOD_NAME__HPP "_HPP" +#define METHOD_NAME_OSHP "OSHP" + +#define PHP_RES_BUS 0xA0 +#define PHP_RES_IO 0xA1 +#define PHP_RES_MEM 0xA2 +#define PHP_RES_PMEM 0xA3 + +#define BRIDGE_TYPE_P2P 0x00 +#define BRIDGE_TYPE_HOST 0x01 + +/* This should go to drivers/acpi/include/ */ +struct acpi__hpp { + u8 cache_line_size; + u8 latency_timer; + u8 enable_serr; + u8 enable_perr; +}; + +struct acpi_php_slot { + struct acpi_php_slot *next; + struct acpi_bridge *bridge; + acpi_handle handle; + int seg; + int bus; + int dev; + int fun; + u32 sun; + struct pci_resource *mem_head; + struct pci_resource *p_mem_head; + struct pci_resource *io_head; + struct pci_resource *bus_head; + void *slot_ops; /* _STA, _EJx, etc */ + struct slot *slot; +}; /* per func */ + +struct acpi_bridge { + struct acpi_bridge *parent; + struct acpi_bridge *next; + struct acpi_bridge *child; + acpi_handle handle; + int seg; + int pbus; /* pdev->bus->number */ + int pdevice; /* PCI_SLOT(pdev->devfn) */ + int pfunction; /* PCI_DEVFN(pdev->devfn) */ + int bus; /* pdev->subordinate->number */ + struct acpi__hpp *_hpp; + struct acpi_php_slot *slots; + struct pci_resource *tmem_head; /* total from crs */ + struct pci_resource *tp_mem_head; /* total from crs */ + struct pci_resource *tio_head; /* total from crs */ + struct pci_resource *tbus_head; /* total from crs */ + struct pci_resource *mem_head; /* available */ + struct pci_resource *p_mem_head; /* available */ + struct pci_resource *io_head; /* available */ + struct pci_resource *bus_head; /* available */ + int scanned; + int type; +}; + +static struct acpi_bridge *acpi_bridges_head; + +static u8 * acpi_path_name( acpi_handle handle) +{ + acpi_status status; + static u8 path_name[ACPI_PATHNAME_MAX]; + struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name }; + + memset(path_name, 0, sizeof (path_name)); + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf); + + if (ACPI_FAILURE(status)) + return NULL; + else + return path_name; +} + +static void acpi_get__hpp ( struct acpi_bridge *ab); +static void acpi_run_oshp ( struct acpi_bridge *ab); + +static int acpi_add_slot_to_php_slots( + struct acpi_bridge *ab, + int bus_num, + acpi_handle handle, + u32 adr, + u32 sun + ) +{ + struct acpi_php_slot *aps; + static long samesun = -1; + + aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL); + if (!aps) { + err ("acpi_shpchprm: alloc for aps fail\n"); + return -1; + } + memset(aps, 0, sizeof(struct acpi_php_slot)); + + aps->handle = handle; + aps->bus = bus_num; + aps->dev = (adr >> 16) & 0xffff; + aps->fun = adr & 0xffff; + aps->sun = sun; + + aps->next = ab->slots; /* cling to the bridge */ + aps->bridge = ab; + ab->slots = aps; + + ab->scanned += 1; + if (!ab->_hpp) + acpi_get__hpp(ab); + + acpi_run_oshp(ab); + if (sun != samesun) { + info("acpi_shpchprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", + aps->sun, ab->seg, aps->bus, aps->dev, aps->fun); + samesun = sun; + } + return 0; +} + +static void acpi_get__hpp ( struct acpi_bridge *ab) +{ + acpi_status status; + u8 nui[4]; + struct acpi_buffer ret_buf = { 0, NULL}; + union acpi_object *ext_obj, *package; + u8 *path_name = acpi_path_name(ab->handle); + int i, len = 0; + + /* get _hpp */ + status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); + switch (status) { + case AE_BUFFER_OVERFLOW: + ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); + if (!ret_buf.pointer) { + err ("acpi_shpchprm:%s alloc for _HPP fail\n", path_name); + return; + } + status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf); + if (ACPI_SUCCESS(status)) + break; + default: + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s _HPP fail=0x%x\n", path_name, status); + return; + } + } + + ext_obj = (union acpi_object *) ret_buf.pointer; + if (ext_obj->type != ACPI_TYPE_PACKAGE) { + err ("acpi_shpchprm:%s _HPP obj not a package\n", path_name); + goto free_and_return; + } + + len = ext_obj->package.count; + package = (union acpi_object *) ret_buf.pointer; + for ( i = 0; (i < len) || (i < 4); i++) { + ext_obj = (union acpi_object *) &package->package.elements[i]; + switch (ext_obj->type) { + case ACPI_TYPE_INTEGER: + nui[i] = (u8)ext_obj->integer.value; + break; + default: + err ("acpi_shpchprm:%s _HPP obj type incorrect\n", path_name); + goto free_and_return; + } + } + + ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL); + memset(ab->_hpp, 0, sizeof(struct acpi__hpp)); + + ab->_hpp->cache_line_size = nui[0]; + ab->_hpp->latency_timer = nui[1]; + ab->_hpp->enable_serr = nui[2]; + ab->_hpp->enable_perr = nui[3]; + + dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); + dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); + dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); + dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); + +free_and_return: + kfree(ret_buf.pointer); +} + +static void acpi_run_oshp ( struct acpi_bridge *ab) +{ + acpi_status status; + u8 *path_name = acpi_path_name(ab->handle); + struct acpi_buffer ret_buf = { 0, NULL}; + + /* run OSHP */ + status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, &ret_buf); + if (ACPI_FAILURE(status)) { + err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status); + } else + dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status); + return; +} + +static acpi_status acpi_evaluate_crs( + acpi_handle handle, + struct acpi_resource **retbuf + ) +{ + acpi_status status; + struct acpi_buffer crsbuf; + u8 *path_name = acpi_path_name(handle); + + crsbuf.length = 0; + crsbuf.pointer = NULL; + + status = acpi_get_current_resources (handle, &crsbuf); + + switch (status) { + case AE_BUFFER_OVERFLOW: + break; /* found */ + case AE_NOT_FOUND: + dbg("acpi_shpchprm:%s _CRS not found\n", path_name); + return status; + default: + err ("acpi_shpchprm:%s _CRS fail=0x%x\n", path_name, status); + return status; + } + + crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL); + if (!crsbuf.pointer) { + err ("acpi_shpchprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name); + return AE_NO_MEMORY; + } + + status = acpi_get_current_resources (handle, &crsbuf); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm: %s _CRS fail=0x%x.\n", path_name, status); + kfree(crsbuf.pointer); + return status; + } + + *retbuf = crsbuf.pointer; + + return status; +} + +static void free_pci_resource ( struct pci_resource *aprh) +{ + struct pci_resource *res, *next; + + for (res = aprh; res; res = next) { + next = res->next; + kfree(res); + } +} + +static void print_pci_resource ( struct pci_resource *aprh) +{ + struct pci_resource *res; + + for (res = aprh; res; res = res->next) + dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); +} + +static void print_slot_resources( struct acpi_php_slot *aps) +{ + if (aps->bus_head) { + dbg(" BUS Resources:\n"); + print_pci_resource (aps->bus_head); + } + + if (aps->io_head) { + dbg(" IO Resources:\n"); + print_pci_resource (aps->io_head); + } + + if (aps->mem_head) { + dbg(" MEM Resources:\n"); + print_pci_resource (aps->mem_head); + } + + if (aps->p_mem_head) { + dbg(" PMEM Resources:\n"); + print_pci_resource (aps->p_mem_head); + } +} + +static void print_pci_resources( struct acpi_bridge *ab) +{ + if (ab->tbus_head) { + dbg(" Total BUS Resources:\n"); + print_pci_resource (ab->tbus_head); + } + if (ab->bus_head) { + dbg(" BUS Resources:\n"); + print_pci_resource (ab->bus_head); + } + + if (ab->tio_head) { + dbg(" Total IO Resources:\n"); + print_pci_resource (ab->tio_head); + } + if (ab->io_head) { + dbg(" IO Resources:\n"); + print_pci_resource (ab->io_head); + } + + if (ab->tmem_head) { + dbg(" Total MEM Resources:\n"); + print_pci_resource (ab->tmem_head); + } + if (ab->mem_head) { + dbg(" MEM Resources:\n"); + print_pci_resource (ab->mem_head); + } + + if (ab->tp_mem_head) { + dbg(" Total PMEM Resources:\n"); + print_pci_resource (ab->tp_mem_head); + } + if (ab->p_mem_head) { + dbg(" PMEM Resources:\n"); + print_pci_resource (ab->p_mem_head); + } + if (ab->_hpp) { + dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size); + dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer); + dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr); + dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr); + } +} + +static int shpchprm_delete_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + struct pci_resource *prevnode; + struct pci_resource *split_node; + ulong tbase; + + shpchp_resource_sort_and_combine(aprh); + + for (res = *aprh; res; res = res->next) { + if (res->base > base) + continue; + + if ((res->base + res->length) < (base + size)) + continue; + + if (res->base < base) { + tbase = base; + + if ((res->length - (tbase - res->base)) < size) + continue; + + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base; + split_node->length = tbase - res->base; + res->base = tbase; + res->length -= split_node->length; + + split_node->next = res->next; + res->next = split_node; + } + + if (res->length >= size) { + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base + size; + split_node->length = res->length - size; + res->length = size; + + split_node->next = res->next; + res->next = split_node; + } + + if (*aprh == res) { + *aprh = res->next; + } else { + prevnode = *aprh; + while (prevnode->next != res) + prevnode = prevnode->next; + + prevnode->next = res->next; + } + res->next = NULL; + kfree(res); + break; + } + + return 0; +} + +static int shpchprm_delete_resources( + struct pci_resource **aprh, + struct pci_resource *this + ) +{ + struct pci_resource *res; + + for (res = this; res; res = res->next) + shpchprm_delete_resource(aprh, res->base, res->length); + + return 0; +} + +static int shpchprm_add_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + + for (res = *aprh; res; res = res->next) { + if ((res->base + res->length) == base) { + res->length += size; + size = 0L; + break; + } + if (res->next == *aprh) + break; + } + + if (size) { + res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!res) { + err ("acpi_shpchprm: alloc for res fail\n"); + return -ENOMEM; + } + memset(res, 0, sizeof (struct pci_resource)); + + res->base = base; + res->length = size; + res->next = *aprh; + *aprh = res; + } + + return 0; +} + +static int shpchprm_add_resources( + struct pci_resource **aprh, + struct pci_resource *this + ) +{ + struct pci_resource *res; + int rc = 0; + + for (res = this; res && !rc; res = res->next) + rc = shpchprm_add_resource(aprh, res->base, res->length); + + return rc; +} + +static void acpi_parse_io ( + struct acpi_bridge *ab, + union acpi_resource_data *data + ) +{ + struct acpi_resource_io *dataio; + dataio = (struct acpi_resource_io *) data; + + dbg("Io Resource\n"); + dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10); + dbg(" Range minimum base: %08X\n", dataio->min_base_address); + dbg(" Range maximum base: %08X\n", dataio->max_base_address); + dbg(" Alignment: %08X\n", dataio->alignment); + dbg(" Range Length: %08X\n", dataio->range_length); +} + +static void acpi_parse_fixed_io ( + struct acpi_bridge *ab, + union acpi_resource_data *data + ) +{ + struct acpi_resource_fixed_io *datafio; + datafio = (struct acpi_resource_fixed_io *) data; + + dbg("Fixed Io Resource\n"); + dbg(" Range base address: %08X", datafio->base_address); + dbg(" Range length: %08X", datafio->range_length); +} + +static void acpi_parse_address16_32 ( + struct acpi_bridge *ab, + union acpi_resource_data *data, + acpi_resource_type id + ) +{ + /* + * acpi_resource_address16 == acpi_resource_address32 + * acpi_resource_address16 *data16 = (acpi_resource_address16 *) data; + */ + struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data; + struct pci_resource **aprh, **tprh; + + if (id == ACPI_RSTYPE_ADDRESS16) + dbg("acpi_shpchprm:16-Bit Address Space Resource\n"); + else + dbg("acpi_shpchprm:32-Bit Address Space Resource\n"); + + switch (data32->resource_type) { + case ACPI_MEMORY_RANGE: + dbg(" Resource Type: Memory Range\n"); + aprh = &ab->mem_head; + tprh = &ab->tmem_head; + + switch (data32->attribute.memory.cache_attribute) { + case ACPI_NON_CACHEABLE_MEMORY: + dbg(" Type Specific: Noncacheable memory\n"); + break; + case ACPI_CACHABLE_MEMORY: + dbg(" Type Specific: Cacheable memory\n"); + break; + case ACPI_WRITE_COMBINING_MEMORY: + dbg(" Type Specific: Write-combining memory\n"); + break; + case ACPI_PREFETCHABLE_MEMORY: + aprh = &ab->p_mem_head; + dbg(" Type Specific: Prefetchable memory\n"); + break; + default: + dbg(" Type Specific: Invalid cache attribute\n"); + break; + } + + dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only"); + break; + + case ACPI_IO_RANGE: + dbg(" Resource Type: I/O Range\n"); + aprh = &ab->io_head; + tprh = &ab->tio_head; + + switch (data32->attribute.io.range_attribute) { + case ACPI_NON_ISA_ONLY_RANGES: + dbg(" Type Specific: Non-ISA Io Addresses\n"); + break; + case ACPI_ISA_ONLY_RANGES: + dbg(" Type Specific: ISA Io Addresses\n"); + break; + case ACPI_ENTIRE_RANGE: + dbg(" Type Specific: ISA and non-ISA Io Addresses\n"); + break; + default: + dbg(" Type Specific: Invalid range attribute\n"); + break; + } + break; + + case ACPI_BUS_NUMBER_RANGE: + dbg(" Resource Type: Bus Number Range(fixed)\n"); + /* Fixup to be compatible with the rest of php driver */ + data32->min_address_range++; + data32->address_length--; + aprh = &ab->bus_head; + tprh = &ab->tbus_head; + break; + default: + dbg(" Resource Type: Invalid resource type. Exiting.\n"); + return; + } + + dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer"); + dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive"); + dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not"); + dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not"); + dbg(" Granularity: %08X\n", data32->granularity); + dbg(" Address range min: %08X\n", data32->min_address_range); + dbg(" Address range max: %08X\n", data32->max_address_range); + dbg(" Address translation offset: %08X\n", data32->address_translation_offset); + dbg(" Address Length: %08X\n", data32->address_length); + + if (0xFF != data32->resource_source.index) { + dbg(" Resource Source Index: %X\n", data32->resource_source.index); + /* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */ + } + + shpchprm_add_resource(aprh, data32->min_address_range, data32->address_length); +} + +static acpi_status acpi_parse_crs( + struct acpi_bridge *ab, + struct acpi_resource *crsbuf + ) +{ + acpi_status status = AE_OK; + struct acpi_resource *resource = crsbuf; + u8 count = 0; + u8 done = 0; + + while (!done) { + dbg("acpi_shpchprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++); + switch (resource->id) { + case ACPI_RSTYPE_IRQ: + dbg("Irq -------- Resource\n"); + break; + case ACPI_RSTYPE_DMA: + dbg("DMA -------- Resource\n"); + break; + case ACPI_RSTYPE_START_DPF: + dbg("Start DPF -------- Resource\n"); + break; + case ACPI_RSTYPE_END_DPF: + dbg("End DPF -------- Resource\n"); + break; + case ACPI_RSTYPE_IO: + acpi_parse_io (ab, &resource->data); + break; + case ACPI_RSTYPE_FIXED_IO: + acpi_parse_fixed_io (ab, &resource->data); + break; + case ACPI_RSTYPE_VENDOR: + dbg("Vendor -------- Resource\n"); + break; + case ACPI_RSTYPE_END_TAG: + dbg("End_tag -------- Resource\n"); + done = 1; + break; + case ACPI_RSTYPE_MEM24: + dbg("Mem24 -------- Resource\n"); + break; + case ACPI_RSTYPE_MEM32: + dbg("Mem32 -------- Resource\n"); + break; + case ACPI_RSTYPE_FIXED_MEM32: + dbg("Fixed Mem32 -------- Resource\n"); + break; + case ACPI_RSTYPE_ADDRESS16: + acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16); + break; + case ACPI_RSTYPE_ADDRESS32: + acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32); + break; + case ACPI_RSTYPE_ADDRESS64: + info("Address64 -------- Resource unparsed\n"); + break; + case ACPI_RSTYPE_EXT_IRQ: + dbg("Ext Irq -------- Resource\n"); + break; + default: + dbg("Invalid -------- resource type 0x%x\n", resource->id); + break; + } + + resource = (struct acpi_resource *) ((char *)resource + resource->length); + } + + return status; +} + +static acpi_status acpi_get_crs( struct acpi_bridge *ab) +{ + acpi_status status; + struct acpi_resource *crsbuf; + + status = acpi_evaluate_crs(ab->handle, &crsbuf); + if (ACPI_SUCCESS(status)) { + status = acpi_parse_crs(ab, crsbuf); + kfree(crsbuf); + + shpchp_resource_sort_and_combine(&ab->bus_head); + shpchp_resource_sort_and_combine(&ab->io_head); + shpchp_resource_sort_and_combine(&ab->mem_head); + shpchp_resource_sort_and_combine(&ab->p_mem_head); + + shpchprm_add_resources (&ab->tbus_head, ab->bus_head); + shpchprm_add_resources (&ab->tio_head, ab->io_head); + shpchprm_add_resources (&ab->tmem_head, ab->mem_head); + shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); + } + + return status; +} + +/* Find acpi_bridge downword from ab. */ +static struct acpi_bridge * +find_acpi_bridge_by_bus( + struct acpi_bridge *ab, + int seg, + int bus /* pdev->subordinate->number */ + ) +{ + struct acpi_bridge *lab = NULL; + + if (!ab) + return NULL; + + if ((ab->bus == bus) && (ab->seg == seg)) + return ab; + + if (ab->child) + lab = find_acpi_bridge_by_bus(ab->child, seg, bus); + + if (!lab) + if (ab->next) + lab = find_acpi_bridge_by_bus(ab->next, seg, bus); + + return lab; +} + +/* + * Build a device tree of ACPI PCI Bridges + */ +static void shpchprm_acpi_register_a_bridge ( + struct acpi_bridge **head, + struct acpi_bridge *pab, /* parent bridge to which child bridge is added */ + struct acpi_bridge *cab /* child bridge to add */ + ) +{ + struct acpi_bridge *lpab; + struct acpi_bridge *lcab; + + lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus); + if (!lpab) { + if (!(pab->type & BRIDGE_TYPE_HOST)) + warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus); + pab->next = *head; + *head = pab; + lpab = pab; + } + + if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab)) + return; + + lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus); + if (lcab) { + if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus)) + err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus); + return; + } else + lcab = cab; + + lcab->parent = lpab; + lcab->next = lpab->child; + lpab->child = lcab; +} + +static acpi_status shpchprm_acpi_build_php_slots_callback( + acpi_handle handle, + u32 Level, + void *context, + void **retval + ) +{ + ulong bus_num; + ulong seg_num; + ulong sun, adr; + ulong padr = 0; + acpi_handle phandle = NULL; + struct acpi_bridge *pab = (struct acpi_bridge *)context; + struct acpi_bridge *lab; + acpi_status status; + u8 *path_name = acpi_path_name(handle); + + /* Get _SUN */ + status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun); + switch(status) { + case AE_NOT_FOUND: + return AE_OK; + default: + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s _SUN fail=0x%x\n", path_name, status); + return status; + } + } + + /* Get _ADR. _ADR must exist if _SUN exists */ + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); + return status; + } + + dbg("acpi_shpchprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr); + + status = acpi_get_parent(handle, &phandle); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s get_parent fail=0x%x\n", path_name, status); + return (status); + } + + bus_num = pab->bus; + seg_num = pab->seg; + + if (pab->bus == bus_num) { + lab = pab; + } else { + dbg("WARN: pab is not parent\n"); + lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num); + if (!lab) { + dbg("acpi_shpchprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); + lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL); + if (!lab) { + err("acpi_shpchprm: alloc for ab fail\n"); + return AE_NO_MEMORY; + } + memset(lab, 0, sizeof(struct acpi_bridge)); + + lab->handle = phandle; + lab->pbus = pab->bus; + lab->pdevice = (int)(padr >> 16) & 0xffff; + lab->pfunction = (int)(padr & 0xffff); + lab->bus = (int)bus_num; + lab->scanned = 0; + lab->type = BRIDGE_TYPE_P2P; + + shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab); + } else + dbg("acpi_shpchprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun); + } + + acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun); + return (status); +} + +static int shpchprm_acpi_build_php_slots( + struct acpi_bridge *ab, + u32 depth + ) +{ + acpi_status status; + u8 *path_name = acpi_path_name(ab->handle); + + /* Walk down this pci bridge to get _SUNs if any behind P2P */ + status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, + ab->handle, + depth, + shpchprm_acpi_build_php_slots_callback, + ab, + NULL ); + if (ACPI_FAILURE(status)) { + dbg("acpi_shpchprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status); + return -1; + } + + return 0; +} + +static void build_a_bridge( + struct acpi_bridge *pab, + struct acpi_bridge *ab + ) +{ + u8 *path_name = acpi_path_name(ab->handle); + + shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab); + + switch (ab->type) { + case BRIDGE_TYPE_HOST: + dbg("acpi_shpchprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", + ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); + break; + case BRIDGE_TYPE_P2P: + dbg("acpi_shpchprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n", + ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name); + break; + }; + + /* any immediate PHP slots under this pci bridge */ + shpchprm_acpi_build_php_slots(ab, 1); +} + +static struct acpi_bridge * add_p2p_bridge( + acpi_handle handle, + struct acpi_bridge *pab, /* parent */ + ulong adr + ) +{ + struct acpi_bridge *ab; + struct pci_dev *pdev; + ulong devnum, funcnum; + u8 *path_name = acpi_path_name(handle); + + ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); + if (!ab) { + err("acpi_shpchprm: alloc for ab fail\n"); + return NULL; + } + memset(ab, 0, sizeof(struct acpi_bridge)); + + devnum = (adr >> 16) & 0xffff; + funcnum = adr & 0xffff; + + pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); + if (!pdev || !pdev->subordinate) { + err("acpi_shpchprm:%s is not a P2P Bridge\n", path_name); + kfree(ab); + return NULL; + } + + ab->handle = handle; + ab->seg = pab->seg; + ab->pbus = pab->bus; /* or pdev->bus->number */ + ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */ + ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */ + ab->bus = pdev->subordinate->number; + ab->scanned = 0; + ab->type = BRIDGE_TYPE_P2P; + + dbg("acpi_shpchprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n", + pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), + pab->bus, (u32)devnum, (u32)funcnum, path_name); + + build_a_bridge(pab, ab); + + return ab; +} + +static acpi_status scan_p2p_bridge( + acpi_handle handle, + u32 Level, + void *context, + void **retval + ) +{ + struct acpi_bridge *pab = (struct acpi_bridge *)context; + struct acpi_bridge *ab; + acpi_status status; + ulong adr = 0; + u8 *path_name = acpi_path_name(handle); + ulong devnum, funcnum; + struct pci_dev *pdev; + + /* Get device, function */ + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) + err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); + return AE_OK; + } + + devnum = (adr >> 16) & 0xffff; + funcnum = adr & 0xffff; + + pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum)); + if (!pdev) + return AE_OK; + if (!pdev->subordinate) + return AE_OK; + + ab = add_p2p_bridge(handle, pab, adr); + if (ab) { + status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, + handle, + (u32)1, + scan_p2p_bridge, + ab, + NULL); + if (ACPI_FAILURE(status)) + dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status); + } + + return AE_OK; +} + +static struct acpi_bridge * add_host_bridge( + acpi_handle handle, + ulong segnum, + ulong busnum + ) +{ + ulong adr = 0; + acpi_status status; + struct acpi_bridge *ab; + u8 *path_name = acpi_path_name(handle); + + /* Get device, function: host br adr is always 0000 though. */ + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status); + return NULL; + } + dbg("acpi_shpchprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, (u32)busnum, (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name); + + ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL); + if (!ab) { + err("acpi_shpchprm: alloc for ab fail\n"); + return NULL; + } + memset(ab, 0, sizeof(struct acpi_bridge)); + + ab->handle = handle; + ab->seg = (int)segnum; + ab->bus = ab->pbus = (int)busnum; + ab->pdevice = (int)(adr >> 16) & 0xffff; + ab->pfunction = (int)(adr & 0xffff); + ab->scanned = 0; + ab->type = BRIDGE_TYPE_HOST; + + /* Get root pci bridge's current resources */ + status = acpi_get_crs(ab); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s evaluate _CRS fail=0x%x\n", path_name, status); + kfree(ab); + return NULL; + } + build_a_bridge(ab, ab); + + return ab; +} + +static acpi_status acpi_scan_from_root_pci_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval + ) +{ + ulong segnum = 0; + ulong busnum = 0; + acpi_status status; + struct acpi_bridge *ab; + u8 *path_name = acpi_path_name(handle); + + /* Get bus number of this pci root bridge */ + status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + err("acpi_shpchprm:%s evaluate _SEG fail=0x%x\n", path_name, status); + return status; + } + segnum = 0; + } + + /* Get bus number of this pci root bridge */ + status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:%s evaluate _BBN fail=0x%x\n", path_name, status); + return (status); + } + + ab = add_host_bridge(handle, segnum, busnum); + if (ab) { + status = acpi_walk_namespace ( ACPI_TYPE_DEVICE, + handle, + 1, + scan_p2p_bridge, + ab, + NULL); + if (ACPI_FAILURE(status)) + dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status); + } + + return AE_OK; +} + +static int shpchprm_acpi_scan_pci (void) +{ + acpi_status status; + + /* + * TBD: traverse LDM device tree with the help of + * unified ACPI augmented for php device population. + */ + status = acpi_get_devices ( PCI_ROOT_HID_STRING, + acpi_scan_from_root_pci_callback, + NULL, + NULL ); + if (ACPI_FAILURE(status)) { + err("acpi_shpchprm:get_device PCI ROOT HID fail=0x%x\n", status); + return -1; + } + + return 0; +} + +int shpchprm_init(enum php_ctlr_type ctlr_type) +{ + int rc; + + if (ctlr_type != PCI) + return -ENODEV; + + dbg("shpchprm ACPI init \n"); + acpi_bridges_head = NULL; + + /* construct PCI bus:device tree of acpi_handles */ + rc = shpchprm_acpi_scan_pci(); + if (rc) + return rc; + + dbg("shpchprm ACPI init %s\n", (rc)?"fail":"success"); + return rc; +} + +static void free_a_slot(struct acpi_php_slot *aps) +{ + dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun); + + free_pci_resource (aps->io_head); + free_pci_resource (aps->bus_head); + free_pci_resource (aps->mem_head); + free_pci_resource (aps->p_mem_head); + + kfree(aps); +} + +static void free_a_bridge( struct acpi_bridge *ab) +{ + struct acpi_php_slot *aps, *next; + + switch (ab->type) { + case BRIDGE_TYPE_HOST: + dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", + ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); + break; + case BRIDGE_TYPE_P2P: + dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n", + ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction); + break; + }; + + /* free slots first */ + for (aps = ab->slots; aps; aps = next) { + next = aps->next; + free_a_slot(aps); + } + + free_pci_resource (ab->io_head); + free_pci_resource (ab->tio_head); + free_pci_resource (ab->bus_head); + free_pci_resource (ab->tbus_head); + free_pci_resource (ab->mem_head); + free_pci_resource (ab->tmem_head); + free_pci_resource (ab->p_mem_head); + free_pci_resource (ab->tp_mem_head); + + kfree(ab); +} + +static void shpchprm_free_bridges ( struct acpi_bridge *ab) +{ + if (!ab) + return; + + if (ab->child) + shpchprm_free_bridges (ab->child); + + if (ab->next) + shpchprm_free_bridges (ab->next); + + free_a_bridge(ab); +} + +void shpchprm_cleanup(void) +{ + shpchprm_free_bridges (acpi_bridges_head); +} + +static int get_number_of_slots ( + struct acpi_bridge *ab, + int selfonly + ) +{ + struct acpi_php_slot *aps; + int prev_slot = -1; + int slot_num = 0; + + for ( aps = ab->slots; aps; aps = aps->next) + if (aps->dev != prev_slot) { + prev_slot = aps->dev; + slot_num++; + } + + if (ab->child) + slot_num += get_number_of_slots (ab->child, 0); + + if (selfonly) + return slot_num; + + if (ab->next) + slot_num += get_number_of_slots (ab->next, 0); + + return slot_num; +} + +static int print_acpi_resources (struct acpi_bridge *ab) +{ + struct acpi_php_slot *aps; + int i; + + switch (ab->type) { + case BRIDGE_TYPE_HOST: + dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle)); + break; + case BRIDGE_TYPE_P2P: + dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle)); + break; + }; + + print_pci_resources (ab); + + for ( i = -1, aps = ab->slots; aps; aps = aps->next) { + if (aps->dev == i) + continue; + dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); + print_slot_resources(aps); + i = aps->dev; + } + + if (ab->child) + print_acpi_resources (ab->child); + + if (ab->next) + print_acpi_resources (ab->next); + + return 0; +} + +int shpchprm_print_pirt(void) +{ + dbg("SHPCHPRM ACPI Slots\n"); + if (acpi_bridges_head) + print_acpi_resources (acpi_bridges_head); + return 0; +} + +static struct acpi_php_slot * get_acpi_slot ( + struct acpi_bridge *ab, + u32 sun + ) +{ + struct acpi_php_slot *aps = NULL; + + for ( aps = ab->slots; aps; aps = aps->next) + if (aps->sun == sun) + return aps; + + if (!aps && ab->child) { + aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun); + if (aps) + return aps; + } + + if (!aps && ab->next) { + aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun); + if (aps) + return aps; + } + + return aps; + +} + +void * shpchprm_get_slot(struct slot *slot) +{ + struct acpi_bridge *ab = acpi_bridges_head; + struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number); + + aps->slot = slot; + + dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun); + + return (void *)aps; +} + +static void shpchprm_dump_func_res( struct pci_func *fun) +{ + struct pci_func *func = fun; + + if (func->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (func->bus_head); + } + if (func->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (func->io_head); + } + if (func->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (func->mem_head); + } + if (func->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (func->p_mem_head); + } +} + +static void shpchprm_dump_ctrl_res( struct controller *ctlr) +{ + struct controller *ctrl = ctlr; + + if (ctrl->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (ctrl->bus_head); + } + if (ctrl->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (ctrl->io_head); + } + if (ctrl->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (ctrl->mem_head); + } + if (ctrl->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (ctrl->p_mem_head); + } +} + +static int shpchprm_get_used_resources ( + struct controller *ctrl, + struct pci_func *func + ) +{ + return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD); +} + +static int configure_existing_function( + struct controller *ctrl, + struct pci_func *func + ) +{ + int rc; + + /* see how much resources the func has used. */ + rc = shpchprm_get_used_resources (ctrl, func); + + if (!rc) { + /* subtract the resources used by the func from ctrl resources */ + rc = shpchprm_delete_resources (&ctrl->bus_head, func->bus_head); + rc |= shpchprm_delete_resources (&ctrl->io_head, func->io_head); + rc |= shpchprm_delete_resources (&ctrl->mem_head, func->mem_head); + rc |= shpchprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); + if (rc) + warn("aCEF: cannot del used resources\n"); + } else + err("aCEF: cannot get used resources\n"); + + return rc; +} + +static int bind_pci_resources_to_slots ( struct controller *ctrl) +{ + struct pci_func *func; + int busn = ctrl->bus; + int devn, funn; + u32 vid; + + for (devn = 0; devn < 32; devn++) { + for (funn = 0; funn < 8; funn++) { + if (devn == ctrl->device && funn == ctrl->function) + continue; + /* find out if this entry is for an occupied slot */ + vid = 0xFFFFFFFF; + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); + + if (vid != 0xFFFFFFFF) { + func = shpchp_slot_find(busn, devn, funn); + if (!func) + continue; + configure_existing_function(ctrl, func); + dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); + shpchprm_dump_func_res(func); + } + } + } + + return 0; +} + +static int bind_pci_resources( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int status = 0; + + if (ab->bus_head) { + dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus); + status = shpchprm_add_resources (&ctrl->bus_head, ab->bus_head); + if (shpchprm_delete_resources (&ab->bus_head, ctrl->bus_head)) + warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus); + + if (ab->io_head) { + dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus); + status = shpchprm_add_resources (&ctrl->io_head, ab->io_head); + if (shpchprm_delete_resources (&ab->io_head, ctrl->io_head)) + warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus); + + if (ab->mem_head) { + dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus); + status = shpchprm_add_resources (&ctrl->mem_head, ab->mem_head); + if (shpchprm_delete_resources (&ab->mem_head, ctrl->mem_head)) + warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus); + + if (ab->p_mem_head) { + dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus); + status = shpchprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head); + if (shpchprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head)) + warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus); + if (status) { + err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status); + return status; + } + } else + info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus); + + return status; +} + +static int no_pci_resources( struct acpi_bridge *ab) +{ + return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head); +} + +static int find_pci_bridge_resources ( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int rc = 0; + struct pci_func func; + + memset(&func, 0, sizeof(struct pci_func)); + + func.bus = ab->pbus; + func.device = ab->pdevice; + func.function = ab->pfunction; + func.is_a_board = 1; + + /* Get used resources for this PCI bridge */ + rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD); + + ab->io_head = func.io_head; + ab->mem_head = func.mem_head; + ab->p_mem_head = func.p_mem_head; + ab->bus_head = func.bus_head; + if (ab->bus_head) + shpchprm_delete_resource(&ab->bus_head, ctrl->bus, 1); + + return rc; +} + +static int get_pci_resources_from_bridge( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int rc = 0; + + dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus); + + rc = find_pci_bridge_resources (ctrl, ab); + + shpchp_resource_sort_and_combine(&ab->bus_head); + shpchp_resource_sort_and_combine(&ab->io_head); + shpchp_resource_sort_and_combine(&ab->mem_head); + shpchp_resource_sort_and_combine(&ab->p_mem_head); + + shpchprm_add_resources (&ab->tbus_head, ab->bus_head); + shpchprm_add_resources (&ab->tio_head, ab->io_head); + shpchprm_add_resources (&ab->tmem_head, ab->mem_head); + shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head); + + return rc; +} + +static int get_pci_resources( + struct controller *ctrl, + struct acpi_bridge *ab + ) +{ + int rc = 0; + + if (no_pci_resources(ab)) { + dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus); + rc = get_pci_resources_from_bridge(ctrl, ab); + } + + return rc; +} + +int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) +{ + int offset = devnum - ctrl->slot_device_offset; + + dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset); + *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc *offset); + return 0; +} + +/* + * Get resources for this ctrl. + * 1. get total resources from ACPI _CRS or bridge (this ctrl) + * 2. find used resources of existing adapters + * 3. subtract used resources from total resources + */ +int shpchprm_find_available_resources( struct controller *ctrl) +{ + int rc = 0; + struct acpi_bridge *ab; + + ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number); + if (!ab) { + err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); + return -1; + } + if (no_pci_resources(ab)) { + rc = get_pci_resources(ctrl, ab); + if (rc) { + err("pfar:cannot get pci resources of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number); + return -1; + } + } + + rc = bind_pci_resources(ctrl, ab); + dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); + shpchprm_dump_ctrl_res(ctrl); + + bind_pci_resources_to_slots (ctrl); + + dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number); + shpchprm_dump_ctrl_res(ctrl); + + return rc; +} + +int shpchprm_set_hpp( + struct controller *ctrl, + struct pci_func *func, + u8 card_type + ) +{ + struct acpi_bridge *ab; + struct pci_bus lpci_bus, *pci_bus; + int rc = 0; + unsigned int devfn; + u8 cls= 0x08; /* default cache line size */ + u8 lt = 0x40; /* default latency timer */ + u8 ep = 0; + u8 es = 0; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); + + if (ab) { + if (ab->_hpp) { + lt = (u8)ab->_hpp->latency_timer; + cls = (u8)ab->_hpp->cache_line_size; + ep = (u8)ab->_hpp->enable_perr; + es = (u8)ab->_hpp->enable_serr; + } else + dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); + } else + dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function); + + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + /* Set subordinate Latency Timer */ + rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt); + } + + /* set base Latency Timer */ + rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt); + dbg(" set latency timer =0x%02x: %x\n", lt, rc); + + rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls); + dbg(" set cache_line_size=0x%02x: %x\n", cls, rc); + + return rc; +} + +void shpchprm_enable_card( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u16 command, cmd, bcommand, bcmd; + struct pci_bus lpci_bus, *pci_bus; + struct acpi_bridge *ab; + unsigned int devfn; + int rc; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); + } + + cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA; + + ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus); + if (ab) { + if (ab->_hpp) { + if (ab->_hpp->enable_perr) { + command |= PCI_COMMAND_PARITY; + bcommand |= PCI_BRIDGE_CTL_PARITY; + } else { + command &= ~PCI_COMMAND_PARITY; + bcommand &= ~PCI_BRIDGE_CTL_PARITY; + } + if (ab->_hpp->enable_serr) { + command |= PCI_COMMAND_SERR; + bcommand |= PCI_BRIDGE_CTL_SERR; + } else { + command &= ~PCI_COMMAND_SERR; + bcommand &= ~PCI_BRIDGE_CTL_SERR; + } + } else + dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); + } else + dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function); + + if (command != cmd) { + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + } + if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) { + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); + } +} + diff -urN linux-2.4.26/drivers/hotplug/shpchprm_legacy.c linux-2.4.27/drivers/hotplug/shpchprm_legacy.c --- linux-2.4.26/drivers/hotplug/shpchprm_legacy.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchprm_legacy.c 2004-08-07 16:26:04.777351832 -0700 @@ -0,0 +1,444 @@ +/* + * SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_IA64 +#include +#endif +#include "shpchp.h" +#include "shpchprm.h" +#include "shpchprm_legacy.h" + +static void *shpchp_rom_start; +static u16 unused_IRQ; + +void shpchprm_cleanup() +{ + if (shpchp_rom_start) + iounmap(shpchp_rom_start); +} + + +int shpchprm_print_pirt() +{ + return 0; +} + +void * shpchprm_get_slot(struct slot *slot) +{ + return NULL; +} + +int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) +{ + int offset = devnum - ctrl->slot_device_offset; + + *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset); + return 0; +} + +/* find the Hot Plug Resource Table in the specified region of memory */ +static void *detect_HRT_floating_pointer(void *begin, void *end) +{ + void *fp; + void *endp; + u8 temp1, temp2, temp3, temp4; + int status = 0; + + endp = (end - sizeof(struct hrt) + 1); + + for (fp = begin; fp <= endp; fp += 16) { + temp1 = readb(fp + SIG0); + temp2 = readb(fp + SIG1); + temp3 = readb(fp + SIG2); + temp4 = readb(fp + SIG3); + if (temp1 == '$' && temp2 == 'H' && temp3 == 'R' && temp4 == 'T') { + status = 1; + break; + } + } + + if (!status) + fp = NULL; + + dbg("Discovered Hotplug Resource Table at %p\n", fp); + return fp; +} + +/* + * shpchprm_find_available_resources + * + * Finds available memory, IO, and IRQ resources for programming + * devices which may be added to the system + * this function is for hot plug ADD! + * + * returns 0 if success + */ +int shpchprm_find_available_resources(struct controller *ctrl) +{ + u8 populated_slot; + u8 bridged_slot; + void *one_slot; + struct pci_func *func = NULL; + int i = 10, index = 0; + u32 temp_dword, rc; + ulong temp_ulong; + struct pci_resource *mem_node; + struct pci_resource *p_mem_node; + struct pci_resource *io_node; + struct pci_resource *bus_node; + void *rom_resource_table; + struct pci_bus lpci_bus, *pci_bus; + u8 cfgspc_irq, temp; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + rom_resource_table = detect_HRT_floating_pointer(shpchp_rom_start, shpchp_rom_start + 0xffff); + dbg("rom_resource_table = %p\n", rom_resource_table); + if (rom_resource_table == NULL) + return -ENODEV; + + /* Sum all resources and setup resource maps */ + unused_IRQ = readl(rom_resource_table + UNUSED_IRQ); + dbg("unused_IRQ = %x\n", unused_IRQ); + + temp = 0; + while (unused_IRQ) { + if (unused_IRQ & 1) { + shpchp_disk_irq = temp; + break; + } + unused_IRQ = unused_IRQ >> 1; + temp++; + } + + dbg("shpchp_disk_irq= %d\n", shpchp_disk_irq); + unused_IRQ = unused_IRQ >> 1; + temp++; + + while (unused_IRQ) { + if (unused_IRQ & 1) { + shpchp_nic_irq = temp; + break; + } + unused_IRQ = unused_IRQ >> 1; + temp++; + } + + dbg("shpchp_nic_irq= %d\n", shpchp_nic_irq); + unused_IRQ = readl(rom_resource_table + PCIIRQ); + + temp = 0; + + pci_read_config_byte(ctrl->pci_dev, PCI_INTERRUPT_LINE, &cfgspc_irq); + + if (!shpchp_nic_irq) { + shpchp_nic_irq = cfgspc_irq; + } + + if (!shpchp_disk_irq) { + shpchp_disk_irq = cfgspc_irq; + } + + dbg("shpchp_disk_irq, shpchp_nic_irq= %d, %d\n", shpchp_disk_irq, shpchp_nic_irq); + + one_slot = rom_resource_table + sizeof(struct hrt); + + i = readb(rom_resource_table + NUMBER_OF_ENTRIES); + dbg("number_of_entries = %d\n", i); + + if (!readb(one_slot + SECONDARY_BUS)) + return (1); + + dbg("dev|IO base|length|MEMbase|length|PM base|length|PB SB MB\n"); + + while (i && readb(one_slot + SECONDARY_BUS)) { + u8 dev_func = readb(one_slot + DEV_FUNC); + u8 primary_bus = readb(one_slot + PRIMARY_BUS); + u8 secondary_bus = readb(one_slot + SECONDARY_BUS); + u8 max_bus = readb(one_slot + MAX_BUS); + u16 io_base = readw(one_slot + IO_BASE); + u16 io_length = readw(one_slot + IO_LENGTH); + u16 mem_base = readw(one_slot + MEM_BASE); + u16 mem_length = readw(one_slot + MEM_LENGTH); + u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); + u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); + + dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", + dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, + primary_bus, secondary_bus, max_bus); + + /* If this entry isn't for our controller's bus, ignore it */ + if (primary_bus != ctrl->slot_bus) { + i--; + one_slot += sizeof(struct slot_rt); + continue; + } + /* Find out if this entry is for an occupied slot */ + temp_dword = 0xFFFFFFFF; + pci_bus->number = primary_bus; + pci_bus_read_config_dword(pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); + + dbg("temp_D_word = %x\n", temp_dword); + + if (temp_dword != 0xFFFFFFFF) { + index = 0; + func = shpchp_slot_find(primary_bus, dev_func >> 3, 0); + + while (func && (func->function != (dev_func & 0x07))) { + dbg("func = %p b:d:f(%x:%x:%x)\n", func, primary_bus, dev_func >> 3, index); + func = shpchp_slot_find(primary_bus, dev_func >> 3, index++); + } + + /* If we can't find a match, skip this table entry */ + if (!func) { + i--; + one_slot += sizeof(struct slot_rt); + continue; + } + /* This may not work and shouldn't be used */ + if (secondary_bus != primary_bus) + bridged_slot = 1; + else + bridged_slot = 0; + + populated_slot = 1; + } else { + populated_slot = 0; + bridged_slot = 0; + } + dbg("slot populated =%s \n", populated_slot?"yes":"no"); + + /* If we've got a valid IO base, use it */ + + temp_ulong = io_base + io_length; + + if ((io_base) && (temp_ulong <= 0x10000)) { + io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!io_node) + return -ENOMEM; + + io_node->base = (ulong)io_base; + io_node->length = (ulong)io_length; + dbg("found io_node(base, length) = %x, %x\n", io_node->base, io_node->length); + + if (!populated_slot) { + io_node->next = ctrl->io_head; + ctrl->io_head = io_node; + } else { + io_node->next = func->io_head; + func->io_head = io_node; + } + } + + /* If we've got a valid memory base, use it */ + temp_ulong = mem_base + mem_length; + if ((mem_base) && (temp_ulong <= 0x10000)) { + mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!mem_node) + return -ENOMEM; + + mem_node->base = (ulong)mem_base << 16; + mem_node->length = (ulong)(mem_length << 16); + dbg("found mem_node(base, length) = %x, %x\n", mem_node->base, mem_node->length); + + if (!populated_slot) { + mem_node->next = ctrl->mem_head; + ctrl->mem_head = mem_node; + } else { + mem_node->next = func->mem_head; + func->mem_head = mem_node; + } + } + + /* + * If we've got a valid prefetchable memory base, and + * the base + length isn't greater than 0xFFFF + */ + temp_ulong = pre_mem_base + pre_mem_length; + if ((pre_mem_base) && (temp_ulong <= 0x10000)) { + p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!p_mem_node) + return -ENOMEM; + + p_mem_node->base = (ulong)pre_mem_base << 16; + p_mem_node->length = (ulong)pre_mem_length << 16; + dbg("found p_mem_node(base, length) = %x, %x\n", p_mem_node->base, p_mem_node->length); + + if (!populated_slot) { + p_mem_node->next = ctrl->p_mem_head; + ctrl->p_mem_head = p_mem_node; + } else { + p_mem_node->next = func->p_mem_head; + func->p_mem_head = p_mem_node; + } + } + + /* + * If we've got a valid bus number, use it + * The second condition is to ignore bus numbers on + * populated slots that don't have PCI-PCI bridges + */ + if (secondary_bus && (secondary_bus != primary_bus)) { + bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!bus_node) + return -ENOMEM; + + bus_node->base = (ulong)secondary_bus; + bus_node->length = (ulong)(max_bus - secondary_bus + 1); + dbg("found bus_node(base, length) = %x, %x\n", bus_node->base, bus_node->length); + + if (!populated_slot) { + bus_node->next = ctrl->bus_head; + ctrl->bus_head = bus_node; + } else { + bus_node->next = func->bus_head; + func->bus_head = bus_node; + } + } + + i--; + one_slot += sizeof(struct slot_rt); + } + + /* If all of the following fail, we don't have any resources for hot plug add */ + rc = 1; + rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head)); + rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); + rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head)); + rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head)); + + return (rc); +} + +int shpchprm_set_hpp( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u32 rc; + u8 temp_byte; + struct pci_bus lpci_bus, *pci_bus; + unsigned int devfn; + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + temp_byte = 0x40; /* Hard coded value for LT */ + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + /* Set subordinate Latency Timer */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); + if (rc) { + dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + return rc; + } + } + + /* Set base Latency Timer */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + if (rc) { + dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + return rc; + } + + /* Set Cache Line size */ + temp_byte = 0x08; /* hard coded value for CLS */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + if (rc) { + dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + } + + /* Set enable_perr */ + /* Set enable_serr */ + + return rc; +} + +void shpchprm_enable_card( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u16 command, bcommand; + struct pci_bus lpci_bus, *pci_bus; + unsigned int devfn; + int rc; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); + command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR + | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); + bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR + | PCI_BRIDGE_CTL_NO_ISA; + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); + } +} + +static int legacy_shpchprm_init_pci(void) +{ + shpchp_rom_start = (u8 *) ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); + if (!shpchp_rom_start) { + err("Could not ioremap memory region for ROM\n"); + return -EIO; + } + + return 0; +} + +int shpchprm_init(enum php_ctlr_type ctrl_type) +{ + int retval; + + switch (ctrl_type) { + case PCI: + retval = legacy_shpchprm_init_pci(); + break; + default: + retval = -ENODEV; + break; + } + + return retval; +} diff -urN linux-2.4.26/drivers/hotplug/shpchprm_legacy.h linux-2.4.27/drivers/hotplug/shpchprm_legacy.h --- linux-2.4.26/drivers/hotplug/shpchprm_legacy.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchprm_legacy.h 2004-08-07 16:26:04.777351832 -0700 @@ -0,0 +1,113 @@ +/* + * SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform using HRT + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#ifndef _SHPCHPRM_LEGACY_H_ +#define _SHPCHPRM_LEGACY_H_ + +#define ROM_PHY_ADDR 0x0F0000 +#define ROM_PHY_LEN 0x00FFFF + +struct slot_rt { + u8 dev_func; + u8 primary_bus; + u8 secondary_bus; + u8 max_bus; + u16 io_base; + u16 io_length; + u16 mem_base; + u16 mem_length; + u16 pre_mem_base; + u16 pre_mem_length; +} __attribute__ ((packed)); + +/* offsets to the hotplug slot resource table registers based on the above structure layout */ +enum slot_rt_offsets { + DEV_FUNC = offsetof(struct slot_rt, dev_func), + PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), + SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), + MAX_BUS = offsetof(struct slot_rt, max_bus), + IO_BASE = offsetof(struct slot_rt, io_base), + IO_LENGTH = offsetof(struct slot_rt, io_length), + MEM_BASE = offsetof(struct slot_rt, mem_base), + MEM_LENGTH = offsetof(struct slot_rt, mem_length), + PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), + PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), +}; + +struct hrt { + char sig0; + char sig1; + char sig2; + char sig3; + u16 unused_IRQ; + u16 PCIIRQ; + u8 number_of_entries; + u8 revision; + u16 reserved1; + u32 reserved2; +} __attribute__ ((packed)); + +/* offsets to the hotplug resource table registers based on the above structure layout */ +enum hrt_offsets { + SIG0 = offsetof(struct hrt, sig0), + SIG1 = offsetof(struct hrt, sig1), + SIG2 = offsetof(struct hrt, sig2), + SIG3 = offsetof(struct hrt, sig3), + UNUSED_IRQ = offsetof(struct hrt, unused_IRQ), + PCIIRQ = offsetof(struct hrt, PCIIRQ), + NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries), + REVISION = offsetof(struct hrt, revision), + HRT_RESERVED1 = offsetof(struct hrt, reserved1), + HRT_RESERVED2 = offsetof(struct hrt, reserved2), +}; + +struct irq_info { + u8 bus, devfn; /* bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__ ((packed)) irq[4]; + u8 slot; /* slot number, 0=onboard */ + u8 rfu; +} __attribute__ ((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__ ((packed)); + +#endif /* _SHPCHPRM_LEGACY_H_ */ diff -urN linux-2.4.26/drivers/hotplug/shpchprm_nonacpi.c linux-2.4.27/drivers/hotplug/shpchprm_nonacpi.c --- linux-2.4.26/drivers/hotplug/shpchprm_nonacpi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchprm_nonacpi.c 2004-08-07 16:26:04.778351873 -0700 @@ -0,0 +1,430 @@ +/* + * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_IA64 +#include +#endif +#include "shpchp.h" +#include "shpchprm.h" +#include "shpchprm_nonacpi.h" + +void shpchprm_cleanup(void) +{ + return; +} + +int shpchprm_print_pirt(void) +{ + return 0; +} + +void * shpchprm_get_slot(struct slot *slot) +{ + return NULL; +} + +int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum) +{ + int offset = devnum - ctrl->slot_device_offset; + + dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset); + *sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset); + return 0; +} + +static void print_pci_resource ( struct pci_resource *aprh) +{ + struct pci_resource *res; + + for (res = aprh; res; res = res->next) + dbg(" base= 0x%x length= 0x%x\n", res->base, res->length); +} + + +static void phprm_dump_func_res( struct pci_func *fun) +{ + struct pci_func *func = fun; + + if (func->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (func->bus_head); + } + if (func->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (func->io_head); + } + if (func->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (func->mem_head); + } + if (func->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (func->p_mem_head); + } +} + +static int phprm_get_used_resources ( + struct controller *ctrl, + struct pci_func *func + ) +{ + return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD); +} + +static int phprm_delete_resource( + struct pci_resource **aprh, + ulong base, + ulong size) +{ + struct pci_resource *res; + struct pci_resource *prevnode; + struct pci_resource *split_node; + ulong tbase; + + shpchp_resource_sort_and_combine(aprh); + + for (res = *aprh; res; res = res->next) { + if (res->base > base) + continue; + + if ((res->base + res->length) < (base + size)) + continue; + + if (res->base < base) { + tbase = base; + + if ((res->length - (tbase - res->base)) < size) + continue; + + split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base; + split_node->length = tbase - res->base; + res->base = tbase; + res->length -= split_node->length; + + split_node->next = res->next; + res->next = split_node; + } + + if (res->length >= size) { + split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL); + if (!split_node) + return -ENOMEM; + + split_node->base = res->base + size; + split_node->length = res->length - size; + res->length = size; + + split_node->next = res->next; + res->next = split_node; + } + + if (*aprh == res) { + *aprh = res->next; + } else { + prevnode = *aprh; + while (prevnode->next != res) + prevnode = prevnode->next; + + prevnode->next = res->next; + } + res->next = NULL; + kfree(res); + break; + } + + return 0; +} + + +static int phprm_delete_resources( + struct pci_resource **aprh, + struct pci_resource *this + ) +{ + struct pci_resource *res; + + for (res = this; res; res = res->next) + phprm_delete_resource(aprh, res->base, res->length); + + return 0; +} + + +static int configure_existing_function( + struct controller *ctrl, + struct pci_func *func + ) +{ + int rc; + + /* see how much resources the func has used. */ + rc = phprm_get_used_resources (ctrl, func); + + if (!rc) { + /* subtract the resources used by the func from ctrl resources */ + rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head); + rc |= phprm_delete_resources (&ctrl->io_head, func->io_head); + rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head); + rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head); + if (rc) + warn("aCEF: cannot del used resources\n"); + } else + err("aCEF: cannot get used resources\n"); + + return rc; +} + +static int bind_pci_resources_to_slots ( struct controller *ctrl) +{ + struct pci_func *func; + int busn = ctrl->slot_bus; + int devn, funn; + u32 vid; + + for (devn = 0; devn < 32; devn++) { + for (funn = 0; funn < 8; funn++) { + /* if (devn == ctrl->device && funn == ctrl->function) + continue; + */ + /* find out if this entry is for an occupied slot */ + vid = 0xFFFFFFFF; + + pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid); + + if (vid != 0xFFFFFFFF) { + func = shpchp_slot_find(busn, devn, funn); + if (!func) + continue; + configure_existing_function(ctrl, func); + dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus); + phprm_dump_func_res(func); + } + } + } + + return 0; +} + +static void phprm_dump_ctrl_res( struct controller *ctlr) +{ + struct controller *ctrl = ctlr; + + if (ctrl->bus_head) { + dbg(": BUS Resources:\n"); + print_pci_resource (ctrl->bus_head); + } + if (ctrl->io_head) { + dbg(": IO Resources:\n"); + print_pci_resource (ctrl->io_head); + } + if (ctrl->mem_head) { + dbg(": MEM Resources:\n"); + print_pci_resource (ctrl->mem_head); + } + if (ctrl->p_mem_head) { + dbg(": PMEM Resources:\n"); + print_pci_resource (ctrl->p_mem_head); + } +} + +/* + * phprm_find_available_resources + * + * Finds available memory, IO, and IRQ resources for programming + * devices which may be added to the system + * this function is for hot plug ADD! + * + * returns 0 if success + */ +int shpchprm_find_available_resources(struct controller *ctrl) +{ + struct pci_func func; + u32 rc; + + memset(&func, 0, sizeof(struct pci_func)); + + func.bus = ctrl->bus; + func.device = ctrl->device; + func.function = ctrl->function; + func.is_a_board = 1; + + /* Get resources for this PCI bridge */ + rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD); + dbg("%s: shpchp_save_used_resources rc = %d\n", __FUNCTION__, rc); + + if (func.mem_head) + func.mem_head->next = ctrl->mem_head; + ctrl->mem_head = func.mem_head; + + if (func.p_mem_head) + func.p_mem_head->next = ctrl->p_mem_head; + ctrl->p_mem_head = func.p_mem_head; + + if (func.io_head) + func.io_head->next = ctrl->io_head; + ctrl->io_head = func.io_head; + + if(func.bus_head) + func.bus_head->next = ctrl->bus_head; + ctrl->bus_head = func.bus_head; + if (ctrl->bus_head) + phprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1); + + dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); + phprm_dump_ctrl_res(ctrl); + + bind_pci_resources_to_slots (ctrl); + + dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus); + phprm_dump_ctrl_res(ctrl); + + + /* If all of the following fail, we don't have any resources for hot plug add */ + rc = 1; + rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head)); + rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head)); + rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head)); + rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head)); + + return (rc); +} + +int shpchprm_set_hpp( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u32 rc; + u8 temp_byte; + struct pci_bus lpci_bus, *pci_bus; + unsigned int devfn; + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + temp_byte = 0x40; /* hard coded value for LT */ + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + /* set subordinate Latency Timer */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); + + if (rc) { + dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + return rc; + } + } + + /* set base Latency Timer */ + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); + + if (rc) { + dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + return rc; + } + + /* set Cache Line size */ + temp_byte = 0x08; /* hard coded value for CLS */ + + rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); + + if (rc) { + dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function); + } + + /* set enable_perr */ + /* set enable_serr */ + + return rc; +} + +void shpchprm_enable_card( + struct controller *ctrl, + struct pci_func *func, + u8 card_type) +{ + u16 command, bcommand; + struct pci_bus lpci_bus, *pci_bus; + unsigned int devfn; + int rc; + + memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus)); + pci_bus = &lpci_bus; + pci_bus->number = func->bus; + devfn = PCI_DEVFN(func->device, func->function); + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command); + + command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR + | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE + | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; + + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); + + if (card_type == PCI_HEADER_TYPE_BRIDGE) { + + rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand); + + bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR + | PCI_BRIDGE_CTL_NO_ISA; + + rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand); + } +} + +static int legacy_shpchprm_init_pci(void) +{ + return 0; +} + +int shpchprm_init(enum php_ctlr_type ctrl_type) +{ + int retval; + + switch (ctrl_type) { + case PCI: + retval = legacy_shpchprm_init_pci(); + break; + default: + retval = -ENODEV; + break; + } + + return retval; +} diff -urN linux-2.4.26/drivers/hotplug/shpchprm_nonacpi.h linux-2.4.27/drivers/hotplug/shpchprm_nonacpi.h --- linux-2.4.26/drivers/hotplug/shpchprm_nonacpi.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/hotplug/shpchprm_nonacpi.h 2004-08-07 16:26:04.779351914 -0700 @@ -0,0 +1,56 @@ +/* + * SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform + * + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * + * All rights reserved. + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Send feedback to , + * + */ + +#ifndef _SHPCHPRM_NONACPI_H_ +#define _SHPCHPRM_NONACPI_H_ + +struct irq_info { + u8 bus, devfn; /* bus, device and function */ + struct { + u8 link; /* IRQ line ID, chipset dependent, 0=not routed */ + u16 bitmap; /* Available IRQs */ + } __attribute__ ((packed)) irq[4]; + u8 slot; /* slot number, 0=onboard */ + u8 rfu; +} __attribute__ ((packed)); + +struct irq_routing_table { + u32 signature; /* PIRQ_SIGNATURE should be here */ + u16 version; /* PIRQ_VERSION */ + u16 size; /* Table size in bytes */ + u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */ + u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */ + u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */ + u32 miniport_data; /* Crap */ + u8 rfu[11]; + u8 checksum; /* Modulo 256 checksum must give zero */ + struct irq_info slots[0]; +} __attribute__ ((packed)); + +#endif /* _SHPCHPRM_NONACPI_H_ */ diff -urN linux-2.4.26/drivers/ide/Config.in linux-2.4.27/drivers/ide/Config.in --- linux-2.4.26/drivers/ide/Config.in 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/ide/Config.in 2004-08-07 16:26:04.780351955 -0700 @@ -63,11 +63,11 @@ fi dep_tristate ' NS87415 chipset support' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI dep_tristate ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL - dep_tristate ' PROMISE PDC202{46|62|65|67} support' CONFIG_BLK_DEV_PDC202XX_OLD $CONFIG_BLK_DEV_IDEDMA_PCI - dep_mbool ' Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX_OLD $CONFIG_BLK_DEV_IDEDMA_PCI - dep_tristate ' PROMISE PDC202{68|69|70|71|75|76|77} support' CONFIG_BLK_DEV_PDC202XX_NEW $CONFIG_BLK_DEV_IDEDMA_PCI + dep_tristate ' Promise PDC202{46|62|65|67} support' CONFIG_BLK_DEV_PDC202XX_OLD $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' Force (U)DMA burst transfers' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX_OLD + dep_tristate ' Promise PDC202{68|69|70|71|75|76|77} support' CONFIG_BLK_DEV_PDC202XX_NEW $CONFIG_BLK_DEV_IDEDMA_PCI if [ "$CONFIG_BLK_DEV_PDC202XX_OLD" = "y" -o "$CONFIG_BLK_DEV_PDC202XX_OLD" = "m" -o "$CONFIG_BLK_DEV_PDC202XX_NEW" = "y" -o "$CONFIG_BLK_DEV_PDC202XX_NEW" = "m" ]; then - bool ' Special FastTrak Feature' CONFIG_PDC202XX_FORCE + bool ' Ignore BIOS port disabled setting on FastTrak' CONFIG_PDC202XX_FORCE fi dep_tristate ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86 dep_tristate ' SCx200 chipset support' CONFIG_BLK_DEV_SC1200 $CONFIG_BLK_DEV_IDEDMA_PCI diff -urN linux-2.4.26/drivers/ide/ide-disk.c linux-2.4.27/drivers/ide/ide-disk.c --- linux-2.4.26/drivers/ide/ide-disk.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/ide/ide-disk.c 2004-08-07 16:26:04.781351996 -0700 @@ -1161,15 +1161,15 @@ { struct hd_driveid *id = drive->id; unsigned long capacity = drive->cyl * drive->head * drive->sect; - unsigned long set_max = idedisk_read_native_max_address(drive); + int have_setmax = idedisk_supports_host_protected_area(drive); + unsigned long set_max = + (have_setmax ? idedisk_read_native_max_address(drive) : 0); unsigned long long capacity_2 = capacity; unsigned long long set_max_ext; drive->capacity48 = 0; drive->select.b.lba = 0; - (void) idedisk_supports_host_protected_area(drive); - if (id->cfs_enable_2 & 0x0400) { capacity_2 = id->lba_capacity_2; drive->head = drive->bios_head = 255; diff -urN linux-2.4.26/drivers/ide/ide.c linux-2.4.27/drivers/ide/ide.c --- linux-2.4.26/drivers/ide/ide.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/ide/ide.c 2004-08-07 16:26:04.783352078 -0700 @@ -2993,7 +2993,24 @@ /* set the drive to standby */ printk("%s ", drive->name); +#ifdef CONFIG_ALPHA + /* On Alpha, halt(8) doesn't actually turn the machine + off, it puts you into the sort of firmware monitor. + Typically, it's used to boot another kernel image, + so it's not much different from reboot(8). + Therefore, we don't need to spin down the disk in + this case, especially since Alpha firmware doesn't + handle disks in standby mode properly. + On the other hand, it's reasonably safe to turn + the power off when the shutdown process reaches + the firmware prompt, as the firmware initialization + takes rather long time - at least 10 seconds, + which should be sufficient for the disk to expire + its write cache. */ + if (event == SYS_POWER_OFF) +#else if (event != SYS_RESTART) +#endif if (DRIVER(drive)->standby(drive)) continue; diff -urN linux-2.4.26/drivers/ide/pci/aec62xx.c linux-2.4.27/drivers/ide/pci/aec62xx.c --- linux-2.4.26/drivers/ide/pci/aec62xx.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/aec62xx.c 2004-08-07 16:26:04.784352119 -0700 @@ -356,13 +356,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: aec62xx_tune_drive(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } static int aec62xx_irq_timeout (ide_drive_t *drive) diff -urN linux-2.4.26/drivers/ide/pci/amd74xx.c linux-2.4.27/drivers/ide/pci/amd74xx.c --- linux-2.4.26/drivers/ide/pci/amd74xx.c 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/amd74xx.c 2004-08-07 16:26:04.785352160 -0700 @@ -1,7 +1,8 @@ /* * Version 2.13 * - * AMD 755/756/766/8111 and nVidia nForce/2/2s/3/3s IDE driver for Linux. + * AMD 755/756/766/8111 and nVidia nForce/2/2s/3/3s/CK804/MCP04 + * IDE driver for Linux. * * Copyright (c) 2000-2002 Vojtech Pavlik * @@ -68,6 +69,12 @@ { PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, 0x50, AMD_UDMA_133 }, { PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, 0x50, AMD_UDMA_133 }, { PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, 0x50, AMD_UDMA_133 }, + { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, 0x50, AMD_UDMA_133 }, { 0 } }; @@ -464,6 +471,12 @@ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 12 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 13 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 14 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 17 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18 }, { 0, }, }; diff -urN linux-2.4.26/drivers/ide/pci/amd74xx.h linux-2.4.27/drivers/ide/pci/amd74xx.h --- linux-2.4.26/drivers/ide/pci/amd74xx.h 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/amd74xx.h 2004-08-07 16:26:04.786352201 -0700 @@ -112,7 +112,7 @@ { /* 7 */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE, - .name = "NFORCE2S", + .name = "NFORCE2-U400R", .init_chipset = init_chipset_amd74xx, .init_hwif = init_hwif_amd74xx, .channels = 2, @@ -123,7 +123,7 @@ { /* 8 */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA, - .name = "NFORCE2S-SATA", + .name = "NFORCE2-U400R-SATA", .init_chipset = init_chipset_amd74xx, .init_hwif = init_hwif_amd74xx, .channels = 2, @@ -134,7 +134,7 @@ { /* 9 */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE, - .name = "NFORCE3", + .name = "NFORCE3-150", .init_chipset = init_chipset_amd74xx, .init_hwif = init_hwif_amd74xx, .channels = 2, @@ -145,7 +145,7 @@ { /* 10 */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE, - .name = "NFORCE3S", + .name = "NFORCE3-250", .init_chipset = init_chipset_amd74xx, .init_hwif = init_hwif_amd74xx, .channels = 2, @@ -156,7 +156,7 @@ { /* 11 */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA, - .name = "NFORCE3S-SATA", + .name = "NFORCE3-250-SATA", .init_chipset = init_chipset_amd74xx, .init_hwif = init_hwif_amd74xx, .channels = 2, @@ -167,7 +167,73 @@ { /* 12 */ .vendor = PCI_VENDOR_ID_NVIDIA, .device = PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2, - .name = "NFORCE3S-SATA2", + .name = "NFORCE3-250-SATA2", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 13 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE, + .name = "NFORCE-CK804", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 14 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA, + .name = "NFORCE-CK804-SATA", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 15 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_SATA2, + .name = "NFORCE-CK804-SATA2", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 16 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE, + .name = "NFORCE-MCP04", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 17 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA, + .name = "NFORCE-MCP04-SATA", + .init_chipset = init_chipset_amd74xx, + .init_hwif = init_hwif_amd74xx, + .channels = 2, + .autodma = AUTODMA, + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, + .bootable = ON_BOARD, + }, + { /* 18 */ + .vendor = PCI_VENDOR_ID_NVIDIA, + .device = PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, + .name = "NFORCE-MCP04-SATA2", .init_chipset = init_chipset_amd74xx, .init_hwif = init_hwif_amd74xx, .channels = 2, diff -urN linux-2.4.26/drivers/ide/pci/cmd64x.c linux-2.4.27/drivers/ide/pci/cmd64x.c --- linux-2.4.26/drivers/ide/pci/cmd64x.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/cmd64x.c 2004-08-07 16:26:04.786352201 -0700 @@ -485,13 +485,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: config_chipset_for_pio(drive, 1); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } static int cmd64x_alt_dma_status (struct pci_dev *dev) diff -urN linux-2.4.26/drivers/ide/pci/generic.c linux-2.4.27/drivers/ide/pci/generic.c --- linux-2.4.26/drivers/ide/pci/generic.c 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/generic.c 2004-08-07 16:26:04.787352242 -0700 @@ -141,6 +141,8 @@ { PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 7}, { PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8}, { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 9}, + { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 10}, + { PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 11}, { 0, }, }; diff -urN linux-2.4.26/drivers/ide/pci/generic.h linux-2.4.27/drivers/ide/pci/generic.h --- linux-2.4.26/drivers/ide/pci/generic.h 2004-04-14 06:05:29.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/generic.h 2004-08-07 16:26:04.787352242 -0700 @@ -130,16 +130,33 @@ },{ /* 9 */ .vendor = PCI_VENDOR_ID_TOSHIBA, .device = PCI_DEVICE_ID_TOSHIBA_PICCOLO, - .name = "Piccolo", + .name = "Piccolo0102", + .init_chipset = init_chipset_generic, + .init_hwif = init_hwif_generic, + .init_dma = init_dma_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 10 */ + .vendor = PCI_VENDOR_ID_TOSHIBA, + .device = PCI_DEVICE_ID_TOSHIBA_PICCOLO_1, + .name = "Piccolo0103", + .init_chipset = init_chipset_generic, + .init_hwif = init_hwif_generic, + .init_dma = init_dma_generic, + .channels = 2, + .autodma = NOAUTODMA, + .bootable = ON_BOARD, + },{ /* 11 */ + .vendor = PCI_VENDOR_ID_TOSHIBA, + .device = PCI_DEVICE_ID_TOSHIBA_PICCOLO_2, + .name = "Piccolo0105", .init_chipset = init_chipset_generic, - .init_iops = NULL, .init_hwif = init_hwif_generic, .init_dma = init_dma_generic, .channels = 2, .autodma = NOAUTODMA, - .enablebits = {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, .bootable = ON_BOARD, - .extra = 0, },{ .vendor = 0, .device = 0, diff -urN linux-2.4.26/drivers/ide/pci/hpt34x.c linux-2.4.27/drivers/ide/pci/hpt34x.c --- linux-2.4.26/drivers/ide/pci/hpt34x.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/hpt34x.c 2004-08-07 16:26:04.788352284 -0700 @@ -216,17 +216,19 @@ } else { goto fast_ata_pio; } +#ifndef CONFIG_HPT34X_AUTODMA + return hwif->ide_dma_off_quietly(drive); +#else + return hwif->ide_dma_on(drive); +#endif } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: hpt34x_tune_drive(drive, 255); return hwif->ide_dma_off_quietly(drive); } - -#ifndef CONFIG_HPT34X_AUTODMA - return hwif->ide_dma_off_quietly(drive); -#endif /* CONFIG_HPT34X_AUTODMA */ - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } /* diff -urN linux-2.4.26/drivers/ide/pci/hpt366.c linux-2.4.27/drivers/ide/pci/hpt366.c --- linux-2.4.26/drivers/ide/pci/hpt366.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/hpt366.c 2004-08-07 16:26:04.841354461 -0700 @@ -123,7 +123,7 @@ " %s\n", (c0 & 0x80) ? "no" : "yes", (c1 & 0x80) ? "no" : "yes"); - +#if 0 if (hpt_minimum_revision(dev, 3)) { u8 cbl; cbl = inb(iobase + 0x7b); @@ -136,7 +136,7 @@ (cbl & 0x01) ? 33 : 66); p += sprintf(p, "\n"); } - +#endif p += sprintf(p, "--------------- drive0 --------- drive1 " "------- drive0 ---------- drive1 -------\n"); p += sprintf(p, "DMA capable: %s %s" @@ -567,13 +567,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: hpt3xx_tune_drive(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } /* diff -urN linux-2.4.26/drivers/ide/pci/it8172.c linux-2.4.27/drivers/ide/pci/it8172.c --- linux-2.4.26/drivers/ide/pci/it8172.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/it8172.c 2004-08-07 16:26:04.842354502 -0700 @@ -228,13 +228,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: it8172_tune_drive(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } static unsigned int __init init_chipset_it8172 (struct pci_dev *dev, const char *name) diff -urN linux-2.4.26/drivers/ide/pci/pdc202xx_new.c linux-2.4.27/drivers/ide/pci/pdc202xx_new.c --- linux-2.4.26/drivers/ide/pci/pdc202xx_new.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/pdc202xx_new.c 2004-08-07 16:26:04.843354543 -0700 @@ -415,13 +415,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: hwif->tuneproc(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } static int pdcnew_quirkproc (ide_drive_t *drive) diff -urN linux-2.4.26/drivers/ide/pci/pdc202xx_old.c linux-2.4.27/drivers/ide/pci/pdc202xx_old.c --- linux-2.4.26/drivers/ide/pci/pdc202xx_old.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/ide/pci/pdc202xx_old.c 2004-08-07 16:26:04.844354585 -0700 @@ -516,13 +516,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: hwif->tuneproc(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } static int pdc202xx_quirkproc (ide_drive_t *drive) diff -urN linux-2.4.26/drivers/ide/pci/piix.c linux-2.4.27/drivers/ide/pci/piix.c --- linux-2.4.26/drivers/ide/pci/piix.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/piix.c 2004-08-07 16:26:04.845354626 -0700 @@ -593,13 +593,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: hwif->tuneproc(drive, 255); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } /** diff -urN linux-2.4.26/drivers/ide/pci/serverworks.c linux-2.4.27/drivers/ide/pci/serverworks.c --- linux-2.4.26/drivers/ide/pci/serverworks.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/ide/pci/serverworks.c 2004-08-07 16:26:04.845354626 -0700 @@ -473,7 +473,9 @@ int dma = config_chipset_for_dma(drive); if ((id->field_valid & 2) && !dma) goto try_dma_modes; - } + } else + /* UDMA disabled by mask, try other DMA modes */ + goto try_dma_modes; } else if (id->field_valid & 2) { try_dma_modes: if ((id->dma_mword & hwif->mwdma_mask) || @@ -490,6 +492,7 @@ } else { goto no_dma_set; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: @@ -497,7 +500,8 @@ // hwif->tuneproc(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } /* This can go soon */ diff -urN linux-2.4.26/drivers/ide/pci/siimage.c linux-2.4.27/drivers/ide/pci/siimage.c --- linux-2.4.26/drivers/ide/pci/siimage.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/siimage.c 2004-08-07 16:26:04.846354667 -0700 @@ -516,13 +516,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: config_chipset_for_pio(drive, 1); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } /* returns 1 if dma irq issued, 0 otherwise */ diff -urN linux-2.4.26/drivers/ide/pci/sis5513.c linux-2.4.27/drivers/ide/pci/sis5513.c --- linux-2.4.26/drivers/ide/pci/sis5513.c 2003-08-25 04:44:41.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/sis5513.c 2004-08-07 16:26:04.847354708 -0700 @@ -697,13 +697,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: sis5513_tune_drive(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } /* initiates/aborts (U)DMA read/write operations on a drive. */ diff -urN linux-2.4.26/drivers/ide/pci/slc90e66.c linux-2.4.27/drivers/ide/pci/slc90e66.c --- linux-2.4.26/drivers/ide/pci/slc90e66.c 2003-06-13 07:51:33.000000000 -0700 +++ linux-2.4.27/drivers/ide/pci/slc90e66.c 2004-08-07 16:26:04.848354749 -0700 @@ -302,13 +302,15 @@ } else { goto fast_ata_pio; } + return hwif->ide_dma_on(drive); } else if ((id->capability & 8) || (id->field_valid & 2)) { fast_ata_pio: no_dma_set: hwif->tuneproc(drive, 5); return hwif->ide_dma_off_quietly(drive); } - return hwif->ide_dma_on(drive); + /* IORDY not supported */ + return 0; } #endif /* CONFIG_BLK_DEV_IDEDMA */ diff -urN linux-2.4.26/drivers/ieee1394/pcilynx.c linux-2.4.27/drivers/ieee1394/pcilynx.c --- linux-2.4.26/drivers/ieee1394/pcilynx.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/ieee1394/pcilynx.c 2004-08-07 16:26:04.850354831 -0700 @@ -1065,12 +1065,15 @@ ssize_t retval; void *membase; - if ((off + count) > PCILYNX_MAX_MEMORY+1) { - count = PCILYNX_MAX_MEMORY+1 - off; - } - if (count == 0 || off > PCILYNX_MAX_MEMORY) { + if (!count) + return 0; + if (off < 0) + return -EINVAL; + if (off > PCILYNX_MAX_MEMORY) return -ENOSPC; - } + + if (count > PCILYNX_MAX_MEMORY + 1 - off) + count = PCILYNX_MAX_MEMORY + 1 - off; switch (md->type) { case rom: @@ -1091,6 +1094,7 @@ if (count < mem_mindma) { memcpy_fromio(md->lynx->mem_dma_buffer, membase+off, count); + off += count; goto out; } @@ -1121,6 +1125,7 @@ if (bcount) { memcpy_fromio(md->lynx->mem_dma_buffer + count - bcount, membase+off, bcount); + off += bcount; } out: @@ -1128,7 +1133,7 @@ up(&md->lynx->mem_dma_mutex); if (retval) return -EFAULT; - *offset += count; + *offset = off; return count; } @@ -1137,32 +1142,36 @@ loff_t *offset) { struct memdata *md = (struct memdata *)file->private_data; + loff_t off = *offset; - if (((*offset) + count) > PCILYNX_MAX_MEMORY+1) { - count = PCILYNX_MAX_MEMORY+1 - *offset; - } - if (count == 0 || *offset > PCILYNX_MAX_MEMORY) { - return -ENOSPC; - } + if (!count) + return 0; + if (off < 0) + return -EINVAL; + if (off > PCILYNX_MAX_MEMORY) + return -ENOSPC; + + if (count > PCILYNX_MAX_MEMORY + 1 - off) + count = PCILYNX_MAX_MEMORY + 1 - off; /* FIXME: dereferencing pointers to PCI mem doesn't work everywhere */ switch (md->type) { case aux: - if (copy_from_user(md->lynx->aux_port+(*offset), buffer, count)) + if (copy_from_user(md->lynx->aux_port+off, buffer, count)) return -EFAULT; break; case ram: - if (copy_from_user(md->lynx->local_ram+(*offset), buffer, count)) + if (copy_from_user(md->lynx->local_ram+off, buffer, count)) return -EFAULT; break; case rom: /* the ROM may be writeable */ - if (copy_from_user(md->lynx->local_rom+(*offset), buffer, count)) + if (copy_from_user(md->lynx->local_rom+off, buffer, count)) return -EFAULT; break; } - file->f_pos += count; + *offset = off + count; return count; } #endif /* CONFIG_IEEE1394_PCILYNX_PORTS */ @@ -1464,7 +1473,7 @@ reg_write(lynx, PCI_INT_ENABLE, 0); free_irq(lynx->dev->irq, lynx); - /* Disable IRM Contender */ + /* Disable IRM Contender and LCtrl */ if (lynx->phyic.reg_1394a) set_phy_reg(lynx, 4, ~0xc0 & get_phy_reg(lynx, 4)); @@ -1792,12 +1801,12 @@ reg_set_bits(lynx, GPIO_CTRL_A, 0x1); reg_write(lynx, GPIO_DATA_BASE + 0x3c, 0x1); } else { - /* set the contender bit in the extended PHY register + /* set the contender and LCtrl bit in the extended PHY register * set. (Should check that bis 0,1,2 (=0xE0) is set * in register 2?) */ i = get_phy_reg(lynx, 4); - if (i != -1) set_phy_reg(lynx, 4, i | 0x40); + if (i != -1) set_phy_reg(lynx, 4, i | 0xc0); } diff -urN linux-2.4.26/drivers/ieee1394/sbp2.c linux-2.4.27/drivers/ieee1394/sbp2.c --- linux-2.4.26/drivers/ieee1394/sbp2.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/ieee1394/sbp2.c 2004-08-07 16:26:04.852354913 -0700 @@ -113,8 +113,8 @@ * badly behaved sbp2 devices. */ MODULE_PARM(sbp2_serialize_io,"i"); -MODULE_PARM_DESC(sbp2_serialize_io, "Serialize all I/O coming down from the scsi drivers (default = 0)"); -static int sbp2_serialize_io = 0; /* serialize I/O - available for debugging purposes */ +MODULE_PARM_DESC(sbp2_serialize_io, "Serialize all I/O coming down from the scsi drivers (default = 1)"); +static int sbp2_serialize_io = 1; /* serialize I/O - available for debugging purposes */ /* * Bump up sbp2_max_sectors if you'd like to support very large sized diff -urN linux-2.4.26/drivers/ieee1394/video1394.c linux-2.4.27/drivers/ieee1394/video1394.c --- linux-2.4.26/drivers/ieee1394/video1394.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/ieee1394/video1394.c 2004-08-07 16:26:04.858355160 -0700 @@ -21,6 +21,9 @@ /* jds -- add private data to file to keep track of iso contexts associated with each open -- so release won't kill all iso transfers */ +/* Damien Douxchamps: Fix failure when the number of DMA pages per frame is + one */ + #include #include #include @@ -405,33 +408,43 @@ (unsigned long)d->dma.kvirt)); ir_prg[0].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, 1 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - - /* the second descriptor will read PAGE_SIZE-4 bytes */ - ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | (PAGE_SIZE-4)); - ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) - - (unsigned long)d->dma.kvirt)); - ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); + /* if there is *not* only one DMA page per frame (hence, d->nb_cmd==2) */ + if (d->nb_cmd>2) { + /* the second descriptor will read PAGE_SIZE-4 bytes */ + ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_BRANCH | (PAGE_SIZE-4)); + ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf + 4) - + (unsigned long)d->dma.kvirt)); + ir_prg[1].branchAddress = cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, + 2 * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); - for (i=2;inb_cmd-1;i++) { + for (i=2;inb_cmd-1;i++) { + ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_BRANCH | PAGE_SIZE); + ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf+(i-1)*PAGE_SIZE) - + (unsigned long)d->dma.kvirt)); + + ir_prg[i].branchAddress = + cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, + (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); + } + + /* the last descriptor will generate an interrupt */ ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_BRANCH | PAGE_SIZE); + DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); - - ir_prg[i].branchAddress = - cpu_to_le32((dma_prog_region_offset_to_bus(ir_reg, - (i + 1) * sizeof(struct dma_cmd)) & 0xfffffff0) | 0x1); + (unsigned long)d->dma.kvirt)); + } + else { + /* only one DMA page is used. Read d->left_size immediately and */ + /* generate an interrupt as this is also the last page */ + ir_prg[1].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | + DMA_CTL_IRQ | DMA_CTL_BRANCH | (d->left_size-4)); + ir_prg[1].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, + (buf + 4) - (unsigned long)d->dma.kvirt)); } - - /* the last descriptor will generate an interrupt */ - ir_prg[i].control = cpu_to_le32(DMA_CTL_INPUT_MORE | DMA_CTL_UPDATE | - DMA_CTL_IRQ | DMA_CTL_BRANCH | d->left_size); - ir_prg[i].address = cpu_to_le32(dma_region_offset_to_bus(&d->dma, - (buf+(i-1)*PAGE_SIZE) - - (unsigned long)d->dma.kvirt)); } static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag, int flags) diff -urN linux-2.4.26/drivers/isdn/divert/divert_procfs.c linux-2.4.27/drivers/isdn/divert/divert_procfs.c --- linux-2.4.26/drivers/isdn/divert/divert_procfs.c 2001-12-21 09:41:54.000000000 -0800 +++ linux-2.4.27/drivers/isdn/divert/divert_procfs.c 2004-08-07 16:26:04.858355160 -0700 @@ -80,6 +80,7 @@ isdn_divert_read(struct file *file, char *buf, size_t count, loff_t * off) { struct divert_info *inf; + loff_t pos = *off; int len; if (!*((struct divert_info **) file->private_data)) { @@ -95,7 +96,7 @@ if ((len = strlen(inf->info_start)) <= count) { if (copy_to_user(buf, inf->info_start, len)) return -EFAULT; - file->f_pos += len; + *off = pos + len; return (len); } return (0); diff -urN linux-2.4.26/drivers/isdn/hisax/nj_s.c linux-2.4.27/drivers/isdn/hisax/nj_s.c --- linux-2.4.26/drivers/isdn/hisax/nj_s.c 2002-11-28 15:53:13.000000000 -0800 +++ linux-2.4.27/drivers/isdn/hisax/nj_s.c 2004-08-07 16:26:04.859355201 -0700 @@ -130,6 +130,7 @@ release_io_netjet(cs); return(0); case CARD_INIT: + reset_netjet_s(cs); inittiger(cs); clear_pending_isac_ints(cs); initisac(cs); @@ -262,7 +263,6 @@ } else { request_region(cs->hw.njet.base, bytecnt, "netjet-s isdn"); } - reset_netjet_s(cs); cs->readisac = &NETjet_ReadIC; cs->writeisac = &NETjet_WriteIC; cs->readisacfifo = &NETjet_ReadICfifo; diff -urN linux-2.4.26/drivers/isdn/hysdn/hysdn_procconf.c linux-2.4.27/drivers/isdn/hysdn/hysdn_procconf.c --- linux-2.4.26/drivers/isdn/hysdn/hysdn_procconf.c 2001-12-21 09:41:54.000000000 -0800 +++ linux-2.4.27/drivers/isdn/hysdn/hysdn_procconf.c 2004-08-07 16:26:04.860355242 -0700 @@ -212,29 +212,27 @@ static ssize_t hysdn_conf_read(struct file *file, char *buf, size_t count, loff_t * off) { + loff_t pos = *off; char *cp; int i; if (off != &file->f_pos) /* fs error check */ return -ESPIPE; - if (file->f_mode & FMODE_READ) { - if (!(cp = file->private_data)) - return (-EFAULT); /* should never happen */ - i = strlen(cp); /* get total string length */ - if (*off < i) { - /* still bytes to transfer */ - cp += *off; /* point to desired data offset */ - i -= *off; /* remaining length */ - if (i > count) - i = count; /* limit length to transfer */ - if (copy_to_user(buf, cp, i)) - return (-EFAULT); /* copy error */ - *off += i; /* adjust offset */ - } else - return (0); + if (!(cp = file->private_data)) + return (-EFAULT); /* should never happen */ + i = strlen(cp); /* get total string length */ + if (pos == (unsigned)pos && pos < i) { + /* still bytes to transfer */ + cp += pos; /* point to desired data offset */ + i -= pos; /* remaining length */ + if (i > count) + i = count; /* limit length to transfer */ + if (copy_to_user(buf, cp, i)) + return (-EFAULT); /* copy error */ + *off = pos + i; /* adjust offset */ } else - return (-EPERM); /* no permission to read */ + return (0); return (i); } /* hysdn_conf_read */ diff -urN linux-2.4.26/drivers/isdn/hysdn/hysdn_proclog.c linux-2.4.27/drivers/isdn/hysdn/hysdn_proclog.c --- linux-2.4.26/drivers/isdn/hysdn/hysdn_proclog.c 2001-12-21 09:41:54.000000000 -0800 +++ linux-2.4.27/drivers/isdn/hysdn/hysdn_proclog.c 2004-08-07 16:26:04.860355242 -0700 @@ -210,6 +210,7 @@ word ino; struct procdata *pd = NULL; hysdn_card *card; + loff_t pos = *off; if (!*((struct log_data **) file->private_data)) { if (file->f_flags & O_NONBLOCK) @@ -238,7 +239,7 @@ if ((len = strlen(inf->log_start)) <= count) { if (copy_to_user(buf, inf->log_start, len)) return -EFAULT; - file->f_pos += len; + *off = pos + len; return (len); } return (0); diff -urN linux-2.4.26/drivers/isdn/isdn_common.c linux-2.4.27/drivers/isdn/isdn_common.c --- linux-2.4.26/drivers/isdn/isdn_common.c 2002-08-02 17:39:44.000000000 -0700 +++ linux-2.4.27/drivers/isdn/isdn_common.c 2004-08-07 16:26:04.862355324 -0700 @@ -976,10 +976,14 @@ int chidx; int retval; char *p; + loff_t pos = *off; if (off != &file->f_pos) return -ESPIPE; + if (pos != (unsigned) pos) + return -EINVAL; + lock_kernel(); if (minor == ISDN_MINOR_STATUS) { if (!file->private_data) { @@ -996,7 +1000,7 @@ retval = -EFAULT; goto out; } - *off += len; + *off = pos + len; retval = len; goto out; } @@ -1027,7 +1031,7 @@ cli(); len = isdn_readbchan(drvidx, chidx, p, 0, count, &dev->drv[drvidx]->rcv_waitq[chidx]); - *off += len; + *off = pos + len; restore_flags(flags); if (copy_to_user(buf,p,len)) len = -EFAULT; @@ -1064,7 +1068,7 @@ else dev->drv[drvidx]->stavail = 0; restore_flags(flags); - *off += len; + *off = pos + len; retval = len; goto out; } diff -urN linux-2.4.26/drivers/isdn/sc/command.c linux-2.4.27/drivers/isdn/sc/command.c --- linux-2.4.26/drivers/isdn/sc/command.c 2001-12-21 09:41:54.000000000 -0800 +++ linux-2.4.27/drivers/isdn/sc/command.c 2004-08-07 16:26:04.862355324 -0700 @@ -95,7 +95,7 @@ if(adapter[i]->driverId == driver) return i; } - return -NODEV; + return -ENODEV; } /* diff -urN linux-2.4.26/drivers/macintosh/ans-lcd.c linux-2.4.27/drivers/macintosh/ans-lcd.c --- linux-2.4.26/drivers/macintosh/ans-lcd.c 2002-02-25 11:37:58.000000000 -0800 +++ linux-2.4.27/drivers/macintosh/ans-lcd.c 2004-08-07 16:26:04.863355365 -0700 @@ -53,7 +53,6 @@ size_t count, loff_t *ppos ) { const char * p = buf; - int i; #ifdef DEBUG printk(KERN_DEBUG "LCD: write\n"); @@ -61,13 +60,13 @@ if ( verify_area(VERIFY_READ, buf, count) ) return -EFAULT; - for ( i = *ppos; count > 0; ++i, ++p, --count ) - { + while (count--) { char c; - __get_user(c, p); + if (__get_user(c, p++)) + return -EFAULT; anslcd_write_byte_data( c ); } - *ppos = i; + *ppos = p - buf; return p - buf; } diff -urN linux-2.4.26/drivers/macintosh/nvram.c linux-2.4.27/drivers/macintosh/nvram.c --- linux-2.4.26/drivers/macintosh/nvram.c 2003-06-13 07:51:34.000000000 -0700 +++ linux-2.4.27/drivers/macintosh/nvram.c 2004-08-07 16:26:04.863355365 -0700 @@ -37,14 +37,15 @@ static ssize_t read_nvram(struct file *file, char *buf, size_t count, loff_t *ppos) { - unsigned int i; + loff_t n = *ppos; + unsigned int i = n; char *p = buf; if (verify_area(VERIFY_WRITE, buf, count)) return -EFAULT; - if (*ppos >= NVRAM_SIZE) + if (i != n || i >= NVRAM_SIZE) return 0; - for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) + for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) if (__put_user(nvram_read_byte(i), p)) return -EFAULT; *ppos = i; @@ -54,15 +55,16 @@ static ssize_t write_nvram(struct file *file, const char *buf, size_t count, loff_t *ppos) { - unsigned int i; + loff_t n = *ppos; + unsigned int i = n; const char *p = buf; char c; if (verify_area(VERIFY_READ, buf, count)) return -EFAULT; - if (*ppos >= NVRAM_SIZE) + if (i != n || i >= NVRAM_SIZE) return 0; - for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { + for (; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { if (__get_user(c, p)) return -EFAULT; nvram_write_byte(c, i); diff -urN linux-2.4.26/drivers/md/raid5.c linux-2.4.27/drivers/md/raid5.c --- linux-2.4.26/drivers/md/raid5.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/md/raid5.c 2004-08-07 16:26:04.865355447 -0700 @@ -950,7 +950,7 @@ /* Now we might consider reading some blocks, either to check/generate * parity, or to satisfy requests */ - if (to_read || (syncing && (uptodate+failed < disks))) { + if (to_read || (syncing && (uptodate < disks))) { for (i=disks; i--;) { bh = sh->bh_cache[i]; if (!buffer_locked(bh) && !buffer_uptodate(bh) && diff -urN linux-2.4.26/drivers/media/video/cpia.c linux-2.4.27/drivers/media/video/cpia.c --- linux-2.4.26/drivers/media/video/cpia.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/media/video/cpia.c 2004-08-07 16:26:04.867355530 -0700 @@ -2443,7 +2443,7 @@ goto_high_power(cam); do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); if (goto_low_power(cam)) - return -NODEV; + return -ENODEV; } /* procedure described in developer's guide p3-28 */ diff -urN linux-2.4.26/drivers/media/video/videodev.c linux-2.4.27/drivers/media/video/videodev.c --- linux-2.4.26/drivers/media/video/videodev.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/media/video/videodev.c 2004-08-07 16:26:04.898356803 -0700 @@ -553,7 +553,14 @@ /* pick a minor number */ down(&videodev_lock); - if (-1 == nr) { + if (nr >= 0 && nr < end-base) { + /* use the one the driver asked for */ + i = base+nr; + if (NULL != video_device[i]) { + up(&videodev_lock); + return -ENFILE; + } + } else { /* use first free */ for(i=base;iminor=i; diff -urN linux-2.4.26/drivers/message/fusion/isense.c linux-2.4.27/drivers/message/fusion/isense.c --- linux-2.4.26/drivers/message/fusion/isense.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/isense.c 2004-08-07 16:26:04.899356845 -0700 @@ -66,7 +66,7 @@ #endif #define MODULEAUTHOR "Steven J. Ralston" -#define COPYRIGHT "Copyright (c) 2001-2002 " MODULEAUTHOR +#define COPYRIGHT "Copyright (c) 2001-2004 " MODULEAUTHOR #include "mptbase.h" #include "isense.h" diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi.h linux-2.4.27/drivers/message/fusion/lsi/mpi.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi.h 2004-08-07 16:26:04.900356886 -0700 @@ -2,11 +2,11 @@ * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI.H + * Name: mpi.h * Title: MPI Message independent structures and definitions * Creation Date: July 27, 2000 * - * MPI.H Version: 01.02.10 + * mpi.h Version: 01.05.xx * * Version History * --------------- @@ -66,7 +66,7 @@ *****************************************************************************/ #define MPI_VERSION_MAJOR (0x01) -#define MPI_VERSION_MINOR (0x02) +#define MPI_VERSION_MINOR (0x05) #define MPI_VERSION_MAJOR_MASK (0xFF00) #define MPI_VERSION_MAJOR_SHIFT (8) #define MPI_VERSION_MINOR_MASK (0x00FF) @@ -77,10 +77,12 @@ #define MPI_VERSION_01_00 (0x0100) #define MPI_VERSION_01_01 (0x0101) #define MPI_VERSION_01_02 (0x0102) +#define MPI_VERSION_01_03 (0x0103) +#define MPI_VERSION_01_05 (0x0105) /* Note: The major versions of 0xe0 through 0xff are reserved */ /* versioning for this MPI header set */ -#define MPI_HEADER_VERSION_UNIT (0x0C) +#define MPI_HEADER_VERSION_UNIT (0x00) #define MPI_HEADER_VERSION_DEV (0x00) #define MPI_HEADER_VERSION_UNIT_MASK (0xFF00) #define MPI_HEADER_VERSION_UNIT_SHIFT (8) @@ -175,6 +177,8 @@ #define MPI_REPLY_POST_FIFO_OFFSET (0x00000044) #define MPI_REPLY_FREE_FIFO_OFFSET (0x00000044) +#define MPI_HI_PRI_REQUEST_QUEUE_OFFSET (0x00000048) + /***************************************************************************** @@ -234,10 +238,6 @@ #define MPI_FUNCTION_TARGET_ASSIST (0x0B) #define MPI_FUNCTION_TARGET_STATUS_SEND (0x0C) #define MPI_FUNCTION_TARGET_MODE_ABORT (0x0D) -#define MPI_FUNCTION_TARGET_FC_BUF_POST_LINK_SRVC (0x0E) /* obsolete name */ -#define MPI_FUNCTION_TARGET_FC_RSP_LINK_SRVC (0x0F) /* obsolete name */ -#define MPI_FUNCTION_TARGET_FC_EX_SEND_LINK_SRVC (0x10) /* obsolete name */ -#define MPI_FUNCTION_TARGET_FC_ABORT (0x11) /* obsolete name */ #define MPI_FUNCTION_FC_LINK_SRVC_BUF_POST (0x0E) #define MPI_FUNCTION_FC_LINK_SRVC_RSP (0x0F) #define MPI_FUNCTION_FC_EX_LINK_SRVC_SEND (0x10) @@ -255,16 +255,46 @@ #define MPI_FUNCTION_MAILBOX (0x19) +#define MPI_FUNCTION_SMP_PASSTHROUGH (0x1A) +#define MPI_FUNCTION_SAS_IO_UNIT_CONTROL (0x1B) + +#define MPI_DIAG_BUFFER_POST (0x1D) +#define MPI_DIAG_RELEASE (0x1E) + +#define MPI_FUNCTION_SCSI_IO_32 (0x1F) + #define MPI_FUNCTION_LAN_SEND (0x20) #define MPI_FUNCTION_LAN_RECEIVE (0x21) #define MPI_FUNCTION_LAN_RESET (0x22) +#define MPI_FUNCTION_INBAND_BUFFER_POST (0x28) +#define MPI_FUNCTION_INBAND_SEND (0x29) +#define MPI_FUNCTION_INBAND_RSP (0x2A) +#define MPI_FUNCTION_INBAND_ABORT (0x2B) + #define MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) #define MPI_FUNCTION_IO_UNIT_RESET (0x41) #define MPI_FUNCTION_HANDSHAKE (0x42) #define MPI_FUNCTION_REPLY_FRAME_REMOVAL (0x43) +/* standard version format */ +typedef struct _MPI_VERSION_STRUCT +{ + U8 Dev; /* 00h */ + U8 Unit; /* 01h */ + U8 Minor; /* 02h */ + U8 Major; /* 03h */ +} MPI_VERSION_STRUCT, MPI_POINTER PTR_MPI_VERSION_STRUCT, + MpiVersionStruct_t, MPI_POINTER pMpiVersionStruct; + +typedef union _MPI_VERSION_FORMAT +{ + MPI_VERSION_STRUCT Struct; + U32 Word; +} MPI_VERSION_FORMAT, MPI_POINTER PTR_MPI_VERSION_FORMAT, + MpiVersionFormat_t, MPI_POINTER pMpiVersionFormat_t; + /***************************************************************************** * @@ -577,44 +607,54 @@ /* Common IOCStatus values for all replies */ /****************************************************************************/ -#define MPI_IOCSTATUS_SUCCESS (0x0000) -#define MPI_IOCSTATUS_INVALID_FUNCTION (0x0001) -#define MPI_IOCSTATUS_BUSY (0x0002) -#define MPI_IOCSTATUS_INVALID_SGL (0x0003) -#define MPI_IOCSTATUS_INTERNAL_ERROR (0x0004) -#define MPI_IOCSTATUS_RESERVED (0x0005) -#define MPI_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) -#define MPI_IOCSTATUS_INVALID_FIELD (0x0007) -#define MPI_IOCSTATUS_INVALID_STATE (0x0008) +#define MPI_IOCSTATUS_SUCCESS (0x0000) +#define MPI_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI_IOCSTATUS_BUSY (0x0002) +#define MPI_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI_IOCSTATUS_RESERVED (0x0005) +#define MPI_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI_IOCSTATUS_INVALID_STATE (0x0008) +#define MPI_IOCSTATUS_OP_STATE_NOT_SUPPORTED (0x0009) /****************************************************************************/ /* Config IOCStatus values */ /****************************************************************************/ -#define MPI_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) -#define MPI_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) -#define MPI_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) -#define MPI_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) -#define MPI_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) -#define MPI_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) +#define MPI_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) /****************************************************************************/ /* SCSIIO Reply (SPI & FCP) initiator values */ /****************************************************************************/ -#define MPI_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) -#define MPI_IOCSTATUS_SCSI_INVALID_BUS (0x0041) -#define MPI_IOCSTATUS_SCSI_INVALID_TARGETID (0x0042) -#define MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) -#define MPI_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) -#define MPI_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) -#define MPI_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) -#define MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) -#define MPI_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) -#define MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) -#define MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) -#define MPI_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) -#define MPI_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) +#define MPI_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI_IOCSTATUS_SCSI_INVALID_BUS (0x0041) +#define MPI_IOCSTATUS_SCSI_INVALID_TARGETID (0x0042) +#define MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPI_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPI_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) + +/****************************************************************************/ +/* For use by SCSI Initiator and SCSI Target end-to-end data protection */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_EEDP_CRC_ERROR (0x004D) +#define MPI_IOCSTATUS_EEDP_LBA_TAG_ERROR (0x004E) +#define MPI_IOCSTATUS_EEDP_APP_TAG_ERROR (0x004F) + /****************************************************************************/ /* SCSI (SPI & FCP) target values */ @@ -631,7 +671,7 @@ #define MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x006B) /****************************************************************************/ -/* Additional FCP target values */ +/* Additional FCP target values (obsolete) */ /****************************************************************************/ #define MPI_IOCSTATUS_TARGET_FC_ABORTED (0x0066) /* obsolete */ @@ -662,6 +702,25 @@ #define MPI_IOCSTATUS_LAN_PARTIAL_PACKET (0x0086) #define MPI_IOCSTATUS_LAN_CANCELED (0x0087) +/****************************************************************************/ +/* Serial Attached SCSI values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_SAS_SMP_REQUEST_FAILED (0x0090) + +/****************************************************************************/ +/* Inband values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_INBAND_ABORTED (0x0098) +#define MPI_IOCSTATUS_INBAND_NO_CONNECTION (0x0099) + +/****************************************************************************/ +/* Diagnostic Tools values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_DIAGNOSTIC_RELEASED (0x00A0) + /****************************************************************************/ /* IOCStatus flag to indicate that log info is available */ @@ -675,9 +734,12 @@ /****************************************************************************/ #define MPI_IOCLOGINFO_TYPE_MASK (0xF0000000) +#define MPI_IOCLOGINFO_TYPE_SHIFT (28) #define MPI_IOCLOGINFO_TYPE_NONE (0x0) #define MPI_IOCLOGINFO_TYPE_SCSI (0x1) #define MPI_IOCLOGINFO_TYPE_FC (0x2) +#define MPI_IOCLOGINFO_TYPE_SAS (0x3) +#define MPI_IOCLOGINFO_TYPE_ISCSI (0x4) #define MPI_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_cnfg.h linux-2.4.27/drivers/message/fusion/lsi/mpi_cnfg.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_cnfg.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_cnfg.h 2004-08-07 16:26:04.904357050 -0700 @@ -2,11 +2,11 @@ * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_CNFG.H + * Name: mpi_cnfg.h * Title: MPI Config message, structures, and Pages * Creation Date: July 27, 2000 * - * MPI_CNFG.H Version: 01.02.12 + * mpi_cnfg.h Version: 01.05.xx * * Version History * --------------- @@ -176,6 +176,19 @@ } ConfigPageHeaderUnion, MPI_POINTER pConfigPageHeaderUnion, fCONFIG_PAGE_HEADER_UNION, MPI_POINTER PTR_CONFIG_PAGE_HEADER_UNION; +typedef struct _CONFIG_EXTENDED_PAGE_HEADER +{ + U8 PageVersion; /* 00h */ + U8 Reserved1; /* 01h */ + U8 PageNumber; /* 02h */ + U8 PageType; /* 03h */ + U16 ExtPageLength; /* 04h */ + U8 ExtPageType; /* 06h */ + U8 Reserved2; /* 07h */ +} fCONFIG_EXTENDED_PAGE_HEADER, MPI_POINTER PTR_CONFIG_EXTENDED_PAGE_HEADER, + ConfigExtendedPageHeader_t, MPI_POINTER pConfigExtendedPageHeader_t; + + /**************************************************************************** * PageType field values @@ -197,12 +210,23 @@ #define MPI_CONFIG_PAGETYPE_RAID_VOLUME (0x08) #define MPI_CONFIG_PAGETYPE_MANUFACTURING (0x09) #define MPI_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A) +#define MPI_CONFIG_PAGETYPE_INBAND (0x0B) +#define MPI_CONFIG_PAGETYPE_EXTENDED (0x0F) #define MPI_CONFIG_PAGETYPE_MASK (0x0F) #define MPI_CONFIG_TYPENUM_MASK (0x0FFF) /**************************************************************************** +* ExtPageType field values +****************************************************************************/ +#define MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT (0x10) +#define MPI_CONFIG_EXTPAGETYPE_SAS_EXPANDER (0x11) +#define MPI_CONFIG_EXTPAGETYPE_SAS_DEVICE (0x12) +#define MPI_CONFIG_EXTPAGETYPE_SAS_PHY (0x13) + + +/**************************************************************************** * PageAddress field values ****************************************************************************/ #define MPI_SCSI_PORT_PGAD_PORT_MASK (0x000000FF) @@ -236,6 +260,24 @@ #define MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF) #define MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT (0) +#define MPI_SAS_DEVICE_PGAD_FORM_MASK (0xF0000000) +#define MPI_SAS_DEVICE_PGAD_FORM_SHIFT (28) +#define MPI_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE (0x00000000) +#define MPI_SAS_DEVICE_PGAD_FORM_BUS_TARGET_ID (0x00000001) +#define MPI_SAS_DEVICE_PGAD_FORM_HANDLE (0x00000002) +#define MPI_SAS_DEVICE_PGAD_GNH_HANDLE_MASK (0x0000FFFF) +#define MPI_SAS_DEVICE_PGAD_GNH_HANDLE_SHIFT (0) +#define MPI_SAS_DEVICE_PGAD_BT_BUS_MASK (0x0000FF00) +#define MPI_SAS_DEVICE_PGAD_BT_BUS_SHIFT (8) +#define MPI_SAS_DEVICE_PGAD_BT_TID_MASK (0x000000FF) +#define MPI_SAS_DEVICE_PGAD_BT_TID_SHIFT (0) +#define MPI_SAS_DEVICE_PGAD_H_HANDLE_MASK (0x0000FFFF) +#define MPI_SAS_DEVICE_PGAD_H_HANDLE_SHIFT (0) + +#define MPI_SAS_PHY_PGAD_PHY_NUMBER_MASK (0x00FF0000) +#define MPI_SAS_PHY_PGAD_PHY_NUMBER_SHIFT (16) +#define MPI_SAS_PHY_PGAD_DEVHANDLE_MASK (0x0000FFFF) +#define MPI_SAS_PHY_PGAD_DEVHANDLE_SHIFT (0) /**************************************************************************** @@ -247,7 +289,8 @@ U8 Reserved; /* 01h */ U8 ChainOffset; /* 02h */ U8 Function; /* 03h */ - U8 Reserved1[3]; /* 04h */ + U16 ExtPageLength; /* 04h */ + U8 ExtPageType; /* 06h */ U8 MsgFlags; /* 07h */ U32 MsgContext; /* 08h */ U8 Reserved2[8]; /* 0Ch */ @@ -277,7 +320,8 @@ U8 Reserved; /* 01h */ U8 MsgLength; /* 02h */ U8 Function; /* 03h */ - U8 Reserved1[3]; /* 04h */ + U16 ExtPageLength; /* 04h */ + U8 ExtPageType; /* 06h */ U8 MsgFlags; /* 07h */ U32 MsgContext; /* 08h */ U8 Reserved2[2]; /* 0Ch */ @@ -299,29 +343,21 @@ * Manufacturing Config pages ****************************************************************************/ #define MPI_MANUFACTPAGE_VENDORID_LSILOGIC (0x1000) -#define MPI_MANUFACTPAGE_VENDORID_TREBIA (0x1783) - +/* Fibre Channel */ #define MPI_MANUFACTPAGE_DEVICEID_FC909 (0x0621) #define MPI_MANUFACTPAGE_DEVICEID_FC919 (0x0624) #define MPI_MANUFACTPAGE_DEVICEID_FC929 (0x0622) #define MPI_MANUFACTPAGE_DEVICEID_FC919X (0x0628) #define MPI_MANUFACTPAGE_DEVICEID_FC929X (0x0626) - +/* SCSI */ #define MPI_MANUFACTPAGE_DEVID_53C1030 (0x0030) #define MPI_MANUFACTPAGE_DEVID_53C1030ZC (0x0031) #define MPI_MANUFACTPAGE_DEVID_1030_53C1035 (0x0032) #define MPI_MANUFACTPAGE_DEVID_1030ZC_53C1035 (0x0033) #define MPI_MANUFACTPAGE_DEVID_53C1035 (0x0040) #define MPI_MANUFACTPAGE_DEVID_53C1035ZC (0x0041) - -#define MPI_MANUFACTPAGE_DEVID_SA2010 (0x0804) -#define MPI_MANUFACTPAGE_DEVID_SA2010ZC (0x0805) -#define MPI_MANUFACTPAGE_DEVID_SA2020 (0x0806) -#define MPI_MANUFACTPAGE_DEVID_SA2020ZC (0x0807) - -#define MPI_MANUFACTPAGE_DEVID_SNP1000 (0x0010) -#define MPI_MANUFACTPAGE_DEVID_SNP500 (0x0020) - +/* SAS */ +#define MPI_MANUFACTPAGE_DEVID_SAS1064 (0x0050) typedef struct _CONFIG_PAGE_MANUFACTURING_0 @@ -405,8 +441,8 @@ U8 InfoOffset1; /* 0Ah */ U8 InfoSize1; /* 0Bh */ U8 InquirySize; /* 0Ch */ - U8 Reserved2; /* 0Dh */ - U16 Reserved3; /* 0Eh */ + U8 Flags; /* 0Dh */ + U16 Reserved2; /* 0Eh */ U8 InquiryData[56]; /* 10h */ U32 ISVolumeSettings; /* 48h */ U32 IMEVolumeSettings; /* 4Ch */ @@ -414,7 +450,30 @@ } fCONFIG_PAGE_MANUFACTURING_4, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_4, ManufacturingPage4_t, MPI_POINTER pManufacturingPage4_t; -#define MPI_MANUFACTURING4_PAGEVERSION (0x00) +#define MPI_MANUFACTURING4_PAGEVERSION (0x01) + +/* defines for the Flags field */ +#define MPI_MANPAGE4_IR_NO_MIX_SAS_SATA (0x01) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_5 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U64 BaseWWID; /* 04h */ +} fCONFIG_PAGE_MANUFACTURING_5, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_5, + ManufacturingPage5_t, MPI_POINTER pManufacturingPage5_t; + +#define MPI_MANUFACTURING5_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_6 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 ProductSpecificInfo;/* 04h */ +} fCONFIG_PAGE_MANUFACTURING_6, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_6, + ManufacturingPage6_t, MPI_POINTER pManufacturingPage6_t; + +#define MPI_MANUFACTURING6_PAGEVERSION (0x00) /**************************************************************************** @@ -438,17 +497,18 @@ } fCONFIG_PAGE_IO_UNIT_1, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_1, IOUnitPage1_t, MPI_POINTER pIOUnitPage1_t; -#define MPI_IOUNITPAGE1_PAGEVERSION (0x00) +#define MPI_IOUNITPAGE1_PAGEVERSION (0x01) /* IO Unit Page 1 Flags defines */ - #define MPI_IOUNITPAGE1_MULTI_FUNCTION (0x00000000) #define MPI_IOUNITPAGE1_SINGLE_FUNCTION (0x00000001) #define MPI_IOUNITPAGE1_MULTI_PATHING (0x00000002) #define MPI_IOUNITPAGE1_SINGLE_PATHING (0x00000000) #define MPI_IOUNITPAGE1_IR_USE_STATIC_VOLUME_ID (0x00000004) +#define MPI_IOUNITPAGE1_DISABLE_QUEUE_FULL_HANDLING (0x00000020) #define MPI_IOUNITPAGE1_DISABLE_IR (0x00000040) #define MPI_IOUNITPAGE1_FORCE_32 (0x00000080) +#define MPI_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE (0x00000100) typedef struct _MPI_ADAPTER_INFO @@ -478,6 +538,11 @@ #define MPI_IOUNITPAGE2_FLAGS_COLOR_VIDEO_DISABLE (0x00000008) #define MPI_IOUNITPAGE2_FLAGS_DONT_HOOK_INT_40 (0x00000010) +#define MPI_IOUNITPAGE2_FLAGS_DEV_LIST_DISPLAY_MASK (0x000000E0) +#define MPI_IOUNITPAGE2_FLAGS_INSTALLED_DEV_DISPLAY (0x00000000) +#define MPI_IOUNITPAGE2_FLAGS_ADAPTER_DISPLAY (0x00000020) +#define MPI_IOUNITPAGE2_FLAGS_ADAPTER_DEV_DISPLAY (0x00000040) + /* * Host code (drivers, BIOS, utilities, etc.) should leave this define set to @@ -540,6 +605,12 @@ #define MPI_IOCPAGE1_PAGEVERSION (0x01) +/* defines for the Flags field */ +#define MPI_IOCPAGE1_EEDP_HOST_SUPPORTS_DIF (0x08000000) +#define MPI_IOCPAGE1_EEDP_MODE_MASK (0x07000000) +#define MPI_IOCPAGE1_EEDP_MODE_OFF (0x00000000) +#define MPI_IOCPAGE1_EEDP_MODE_T10 (0x01000000) +#define MPI_IOCPAGE1_EEDP_MODE_LSI_1 (0x02000000) #define MPI_IOCPAGE1_REPLY_COALESCING (0x00000001) #define MPI_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF) @@ -680,7 +751,7 @@ typedef struct _CONFIG_PAGE_IOC_5 { - fCONFIG_PAGE_HEADER Header; /* 00h */ + fCONFIG_PAGE_HEADER Header; /* 00h */ U32 Reserved1; /* 04h */ U8 NumHotSpares; /* 08h */ U8 Reserved2; /* 09h */ @@ -692,6 +763,57 @@ #define MPI_IOCPAGE5_PAGEVERSION (0x00) +/**************************************************************************** +* BIOS Port Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_BIOS_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 BiosOptions; /* 04h */ + U32 IOCSettings; /* 08h */ + U32 Reserved1; /* 0Ch */ + U32 DeviceSettings; /* 10h */ + U16 NumberOfDevices; /* 14h */ + U16 Reserved2; /* 16h */ + U16 IOTimeoutBlockDevicesNonRM; /* 18h */ + U16 IOTimeoutSequential; /* 1Ah */ + U16 IOTimeoutOther; /* 1Ch */ + U16 IOTimeoutBlockDevicesRM; /* 1Eh */ +} fCONFIG_PAGE_BIOS_1, MPI_POINTER PTR_CONFIG_PAGE_BIOS_1, + BIOSPage1_t, MPI_POINTER pBIOSPage1_t; + +#define MPI_BIOSPAGE1_PAGEVERSION (0x00) + +/* values for the BiosOptions field */ +#define MPI_BIOSPAGE1_OPTIONS_SPI_ENABLE (0x00000400) +#define MPI_BIOSPAGE1_OPTIONS_FC_ENABLE (0x00000200) +#define MPI_BIOSPAGE1_OPTIONS_SAS_ENABLE (0x00000100) +#define MPI_BIOSPAGE1_OPTIONS_DISABLE_BIOS (0x00000001) + +/* values for the IOCSettings field */ +#define MPI_BIOSPAGE1_IOCSET_MASK_SPINUP_DELAY (0x00000F00) +#define MPI_BIOSPAGE1_IOCSET_SHIFT_SPINUP_DELAY (8) + +#define MPI_BIOSPAGE1_IOCSET_MASK_RM_SETTING (0x000000C0) +#define MPI_BIOSPAGE1_IOCSET_NONE_RM_SETTING (0x00000000) +#define MPI_BIOSPAGE1_IOCSET_BOOT_RM_SETTING (0x00000040) +#define MPI_BIOSPAGE1_IOCSET_MEDIA_RM_SETTING (0x00000080) + +#define MPI_BIOSPAGE1_IOCSET_MASK_ADAPTER_SUPPORT (0x00000030) +#define MPI_BIOSPAGE1_IOCSET_NO_SUPPORT (0x00000000) +#define MPI_BIOSPAGE1_IOCSET_BIOS_SUPPORT (0x00000010) +#define MPI_BIOSPAGE1_IOCSET_OS_SUPPORT (0x00000020) +#define MPI_BIOSPAGE1_IOCSET_ALL_SUPPORT (0x00000030) + +#define MPI_BIOSPAGE1_IOCSET_ALTERNATE_CHS (0x00000008) + +/* values for the DeviceSettings field */ +#define MPI_BIOSPAGE1_DEVSET_DISABLE_SEQ_LUN (0x00000008) +#define MPI_BIOSPAGE1_DEVSET_DISABLE_RM_LUN (0x00000004) +#define MPI_BIOSPAGE1_DEVSET_DISABLE_NON_RM_LUN (0x00000002) +#define MPI_BIOSPAGE1_DEVSET_DISABLE_OTHER_LUN (0x00000001) + /**************************************************************************** * SCSI Port Config Pages @@ -711,7 +833,27 @@ #define MPI_SCSIPORTPAGE0_CAP_DT (0x00000002) #define MPI_SCSIPORTPAGE0_CAP_QAS (0x00000004) #define MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIPORTPAGE0_SYNC_ASYNC (0x00) +#define MPI_SCSIPORTPAGE0_SYNC_5 (0x32) +#define MPI_SCSIPORTPAGE0_SYNC_10 (0x19) +#define MPI_SCSIPORTPAGE0_SYNC_20 (0x0C) +#define MPI_SCSIPORTPAGE0_SYNC_33_33 (0x0B) +#define MPI_SCSIPORTPAGE0_SYNC_40 (0x0A) +#define MPI_SCSIPORTPAGE0_SYNC_80 (0x09) +#define MPI_SCSIPORTPAGE0_SYNC_160 (0x08) +#define MPI_SCSIPORTPAGE0_SYNC_UNKNOWN (0xFF) + +#define MPI_SCSIPORTPAGE0_CAP_SHIFT_MIN_SYNC_PERIOD (8) +#define MPI_SCSIPORTPAGE0_CAP_GET_MIN_SYNC_PERIOD(Cap) \ + ( ((Cap) & MPI_SCSIPORTPAGE0_CAP_MASK_MIN_SYNC_PERIOD) \ + >> MPI_SCSIPORTPAGE0_CAP_SHIFT_MIN_SYNC_PERIOD \ + ) #define MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIPORTPAGE0_CAP_SHIFT_MAX_SYNC_OFFSET (16) +#define MPI_SCSIPORTPAGE0_CAP_GET_MAX_SYNC_OFFSET(Cap) \ + ( ((Cap) & MPI_SCSIPORTPAGE0_CAP_MASK_MAX_SYNC_OFFSET) \ + >> MPI_SCSIPORTPAGE0_CAP_SHIFT_MAX_SYNC_OFFSET \ + ) #define MPI_SCSIPORTPAGE0_CAP_WIDE (0x20000000) #define MPI_SCSIPORTPAGE0_CAP_AIP (0x80000000) @@ -736,11 +878,12 @@ } fCONFIG_PAGE_SCSI_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_1, SCSIPortPage1_t, MPI_POINTER pSCSIPortPage1_t; -#define MPI_SCSIPORTPAGE1_PAGEVERSION (0x02) +#define MPI_SCSIPORTPAGE1_PAGEVERSION (0x03) /* Configuration values */ #define MPI_SCSIPORTPAGE1_CFG_PORT_SCSI_ID_MASK (0x000000FF) #define MPI_SCSIPORTPAGE1_CFG_PORT_RESPONSE_ID_MASK (0xFFFF0000) +#define MPI_SCSIPORTPAGE1_CFG_SHIFT_PORT_RESPONSE_ID (16) /* TargetConfig values */ #define MPI_SCSIPORTPAGE1_TARGCONFIG_TARG_ONLY (0x01) @@ -777,6 +920,7 @@ #define MPI_SCSIPORTPAGE2_PORT_FLAGS_BASIC_DV_ONLY (0x00000020) #define MPI_SCSIPORTPAGE2_PORT_FLAGS_OFF_DV (0x00000060) + /* PortSettings values */ #define MPI_SCSIPORTPAGE2_PORT_HOST_ID_MASK (0x0000000F) #define MPI_SCSIPORTPAGE2_PORT_MASK_INIT_HBA (0x00000030) @@ -785,7 +929,11 @@ #define MPI_SCSIPORTPAGE2_PORT_OS_INIT_HBA (0x00000020) #define MPI_SCSIPORTPAGE2_PORT_BIOS_OS_INIT_HBA (0x00000030) #define MPI_SCSIPORTPAGE2_PORT_REMOVABLE_MEDIA (0x000000C0) +#define MPI_SCSIPORTPAGE2_PORT_RM_NONE (0x00000000) +#define MPI_SCSIPORTPAGE2_PORT_RM_BOOT_ONLY (0x00000040) +#define MPI_SCSIPORTPAGE2_PORT_RM_WITH_MEDIA (0x00000080) #define MPI_SCSIPORTPAGE2_PORT_SPINUP_DELAY_MASK (0x00000F00) +#define MPI_SCSIPORTPAGE2_PORT_SHIFT_SPINUP_DELAY (8) #define MPI_SCSIPORTPAGE2_PORT_MASK_NEGO_MASTER_SETTINGS (0x00003000) #define MPI_SCSIPORTPAGE2_PORT_NEGO_MASTER_SETTINGS (0x00000000) #define MPI_SCSIPORTPAGE2_PORT_NONE_MASTER_SETTINGS (0x00001000) @@ -822,7 +970,9 @@ #define MPI_SCSIDEVPAGE0_NP_RTI (0x00000040) #define MPI_SCSIDEVPAGE0_NP_PCOMP_EN (0x00000080) #define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIDEVPAGE0_NP_SHIFT_SYNC_PERIOD (8) #define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIDEVPAGE0_NP_SHIFT_SYNC_OFFSET (16) #define MPI_SCSIDEVPAGE0_NP_WIDE (0x20000000) #define MPI_SCSIDEVPAGE0_NP_AIP (0x80000000) @@ -852,7 +1002,9 @@ #define MPI_SCSIDEVPAGE1_RP_RTI (0x00000040) #define MPI_SCSIDEVPAGE1_RP_PCOMP_EN (0x00000080) #define MPI_SCSIDEVPAGE1_RP_MIN_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIDEVPAGE1_RP_SHIFT_MIN_SYNC_PERIOD (8) #define MPI_SCSIDEVPAGE1_RP_MAX_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIDEVPAGE1_RP_SHIFT_MAX_SYNC_OFFSET (16) #define MPI_SCSIDEVPAGE1_RP_WIDE (0x20000000) #define MPI_SCSIDEVPAGE1_RP_AIP (0x80000000) @@ -998,13 +1150,19 @@ #define MPI_FCPORTPAGE0_SUPPORT_CLASS_2 (0x00000002) #define MPI_FCPORTPAGE0_SUPPORT_CLASS_3 (0x00000004) -#define MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED (0x00000001) /* (SNIA)HBA_PORTSPEED_1GBIT 1 1 GBit/sec */ -#define MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED (0x00000002) /* (SNIA)HBA_PORTSPEED_2GBIT 2 2 GBit/sec */ -#define MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED (0x00000004) /* (SNIA)HBA_PORTSPEED_10GBIT 4 10 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_SPEED_UKNOWN (0x00000000) /* (SNIA)HBA_PORTSPEED_UNKNOWN 0 Unknown - transceiver incapable of reporting */ +#define MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED (0x00000001) /* (SNIA)HBA_PORTSPEED_1GBIT 1 1 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED (0x00000002) /* (SNIA)HBA_PORTSPEED_2GBIT 2 2 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED (0x00000004) /* (SNIA)HBA_PORTSPEED_10GBIT 4 10 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_4GBIT_SPEED (0x00000008) /* (SNIA)HBA_PORTSPEED_4GBIT 8 4 GBit/sec */ +#define MPI_FCPORTPAGE0_CURRENT_SPEED_UKNOWN MPI_FCPORTPAGE0_SUPPORT_SPEED_UKNOWN #define MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED #define MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED #define MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED +#define MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT MPI_FCPORTPAGE0_SUPPORT_4GBIT_SPEED +#define MPI_FCPORTPAGE0_CURRENT_SPEED_NOT_NEGOTIATED (0x00008000) /* (SNIA)HBA_PORTSPEED_NOT_NEGOTIATED (1<<15) Speed not established */ + typedef struct _CONFIG_PAGE_FC_PORT_1 @@ -1019,11 +1177,12 @@ U8 AltConnector; /* 1Bh */ U8 NumRequestedAliases; /* 1Ch */ U8 RR_TOV; /* 1Dh */ - U16 Reserved2; /* 1Eh */ + U8 InitiatorDeviceTimeout; /* 1Eh */ + U8 InitiatorIoPendTimeout; /* 1Fh */ } fCONFIG_PAGE_FC_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_1, FCPortPage1_t, MPI_POINTER pFCPortPage1_t; -#define MPI_FCPORTPAGE1_PAGEVERSION (0x05) +#define MPI_FCPORTPAGE1_PAGEVERSION (0x06) #define MPI_FCPORTPAGE1_FLAGS_EXT_FCP_STATUS_EN (0x08000000) #define MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY (0x04000000) @@ -1031,6 +1190,7 @@ #define MPI_FCPORTPAGE1_FLAGS_VERBOSE_RESCAN_EVENTS (0x01000000) #define MPI_FCPORTPAGE1_FLAGS_TARGET_MODE_OXID (0x00800000) #define MPI_FCPORTPAGE1_FLAGS_PORT_OFFLINE (0x00400000) +#define MPI_FCPORTPAGE1_FLAGS_SOFT_ALPA_FALLBACK (0x00200000) #define MPI_FCPORTPAGE1_FLAGS_MASK_RR_TOV_UNITS (0x00000070) #define MPI_FCPORTPAGE1_FLAGS_SUPPRESS_PROT_REG (0x00000008) #define MPI_FCPORTPAGE1_FLAGS_PLOGI_ON_LOGO (0x00000004) @@ -1066,6 +1226,8 @@ #define MPI_FCPORTPAGE1_ALT_CONN_UNKNOWN (0x00) +#define MPI_FCPORTPAGE1_INITIATOR_DEV_TIMEOUT_MASK (0x7F) + typedef struct _CONFIG_PAGE_FC_PORT_2 { @@ -1627,5 +1789,317 @@ #define MPI_LAN_PAGE1_DEV_STATE_RESET (0x00) #define MPI_LAN_PAGE1_DEV_STATE_OPERATIONAL (0x01) + +/**************************************************************************** +* Inband Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_INBAND_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + MPI_VERSION_FORMAT InbandVersion; /* 04h */ + U16 MaximumBuffers; /* 08h */ + U16 Reserved1; /* 0Ah */ +} fCONFIG_PAGE_INBAND_0, MPI_POINTER PTR_CONFIG_PAGE_INBAND_0, + InbandPage0_t, MPI_POINTER pInbandPage0_t; + +#define MPI_INBAND_PAGEVERSION (0x00) + + + +/**************************************************************************** +* SAS IO Unit Config Pages +****************************************************************************/ + +typedef struct _MPI_SAS_IO_UNIT0_PHY_DATA +{ + U8 Port; /* 00h */ + U8 PortFlags; /* 01h */ + U8 PhyFlags; /* 02h */ + U8 NegotiatedLinkRate; /* 03h */ + U32 ControllerPhyDeviceInfo;/* 04h */ + U16 AttachedDeviceHandle; /* 08h */ + U16 ControllerDevHandle; /* 0Ah */ + U32 Reserved2; /* 0Ch */ +} MPI_SAS_IO_UNIT0_PHY_DATA, MPI_POINTER PTR_MPI_SAS_IO_UNIT0_PHY_DATA, + SasIOUnit0PhyData, MPI_POINTER pSasIOUnit0PhyData; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_SAS_IOUNIT0_PHY_MAX +#define MPI_SAS_IOUNIT0_PHY_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_SAS_IO_UNIT_0 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U8 NumPhys; /* 0Ch */ + U8 Reserved2; /* 0Dh */ + U16 Reserved3; /* 0Eh */ + MPI_SAS_IO_UNIT0_PHY_DATA PhyData[MPI_SAS_IOUNIT0_PHY_MAX]; /* 10h */ +} fCONFIG_PAGE_SAS_IO_UNIT_0, MPI_POINTER PTR_CONFIG_PAGE_SAS_IO_UNIT_0, + SasIOUnitPage0_t, MPI_POINTER pSasIOUnitPage0_t; + +#define MPI_SASIOUNITPAGE0_PAGEVERSION (0x00) + +/* values for SAS IO Unit Page 0 PortFlags */ +#define MPI_SAS_IOUNIT0_PORT_FLAGS_DISCOVERY_IN_PROGRESS (0x08) +#define MPI_SAS_IOUNIT0_PORT_FLAGS_0_TARGET_IOC_NUM (0x00) +#define MPI_SAS_IOUNIT0_PORT_FLAGS_1_TARGET_IOC_NUM (0x04) +#define MPI_SAS_IOUNIT0_PORT_FLAGS_WAIT_FOR_PORTENABLE (0x02) +#define MPI_SAS_IOUNIT0_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) + +/* values for SAS IO Unit Page 0 PhyFlags */ +#define MPI_SAS_IOUNIT0_PHY_FLAGS_PHY_DISABLED (0x04) +#define MPI_SAS_IOUNIT0_PHY_FLAGS_TX_INVERT (0x02) +#define MPI_SAS_IOUNIT0_PHY_FLAGS_RX_INVERT (0x01) + +/* values for SAS IO Unit Page 0 NegotiatedLinkRate */ +#define MPI_SAS_IOUNIT0_RATE_UNKNOWN (0x00) +#define MPI_SAS_IOUNIT0_RATE_PHY_DISABLED (0x01) +#define MPI_SAS_IOUNIT0_RATE_FAILED_SPEED_NEGOTIATION (0x02) +#define MPI_SAS_IOUNIT0_RATE_SATA_OOB_COMPLETE (0x03) +#define MPI_SAS_IOUNIT0_RATE_1_5 (0x08) +#define MPI_SAS_IOUNIT0_RATE_3_0 (0x09) + +/* see mpi_sas.h for values for SAS IO Unit Page 0 ControllerPhyDeviceInfo values */ + + +typedef struct _MPI_SAS_IO_UNIT1_PHY_DATA +{ + U8 Port; /* 00h */ + U8 PortFlags; /* 01h */ + U8 PhyFlags; /* 02h */ + U8 MaxMinLinkRate; /* 03h */ + U32 ControllerPhyDeviceInfo;/* 04h */ + U32 Reserved1; /* 08h */ +} MPI_SAS_IO_UNIT1_PHY_DATA, MPI_POINTER PTR_MPI_SAS_IO_UNIT1_PHY_DATA, + SasIOUnit1PhyData, MPI_POINTER pSasIOUnit1PhyData; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_SAS_IOUNIT1_PHY_MAX +#define MPI_SAS_IOUNIT1_PHY_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_SAS_IO_UNIT_1 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U8 NumPhys; /* 0Ch */ + U8 Reserved2; /* 0Dh */ + U16 Reserved3; /* 0Eh */ + MPI_SAS_IO_UNIT1_PHY_DATA PhyData[MPI_SAS_IOUNIT1_PHY_MAX]; /* 10h */ +} fCONFIG_PAGE_SAS_IO_UNIT_1, MPI_POINTER PTR_CONFIG_PAGE_SAS_IO_UNIT_1, + SasIOUnitPage1_t, MPI_POINTER pSasIOUnitPage1_t; + +#define MPI_SASIOUNITPAGE1_PAGEVERSION (0x00) + +/* values for SAS IO Unit Page 0 PortFlags */ +#define MPI_SAS_IOUNIT1_PORT_FLAGS_0_TARGET_IOC_NUM (0x00) +#define MPI_SAS_IOUNIT1_PORT_FLAGS_1_TARGET_IOC_NUM (0x04) +#define MPI_SAS_IOUNIT1_PORT_FLAGS_WAIT_FOR_PORTENABLE (0x02) +#define MPI_SAS_IOUNIT1_PORT_FLAGS_AUTO_PORT_CONFIG (0x01) + +/* values for SAS IO Unit Page 0 PhyFlags */ +#define MPI_SAS_IOUNIT1_PHY_FLAGS_PHY_DISABLE (0x04) +#define MPI_SAS_IOUNIT1_PHY_FLAGS_TX_INVERT (0x02) +#define MPI_SAS_IOUNIT1_PHY_FLAGS_RX_INVERT (0x01) + +/* values for SAS IO Unit Page 0 MaxMinLinkRate */ +#define MPI_SAS_IOUNIT1_MAX_RATE_MASK (0xF0) +#define MPI_SAS_IOUNIT1_MAX_RATE_1_5 (0x80) +#define MPI_SAS_IOUNIT1_MAX_RATE_3_0 (0x90) +#define MPI_SAS_IOUNIT1_MIN_RATE_MASK (0x0F) +#define MPI_SAS_IOUNIT1_MIN_RATE_1_5 (0x08) +#define MPI_SAS_IOUNIT1_MIN_RATE_3_0 (0x09) + +/* see mpi_sas.h for values for SAS IO Unit Page 1 ControllerPhyDeviceInfo values */ + + +typedef struct _CONFIG_PAGE_SAS_IO_UNIT_2 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U16 MaxPersistentIDs; /* 0Ch */ + U16 NumPersistentIDsUsed; /* 0Eh */ + U8 Status; /* 10h */ + U8 Flags; /* 11h */ + U16 Reserved2; /* 12h */ +} fCONFIG_PAGE_SAS_IO_UNIT_2, MPI_POINTER PTR_CONFIG_PAGE_SAS_IO_UNIT_2, + SasIOUnitPage2_t, MPI_POINTER pSasIOUnitPage2_t; + +#define MPI_SASIOUNITPAGE2_PAGEVERSION (0x00) + +/* values for SAS IO Unit Page 2 Status field */ +#define MPI_SAS_IOUNIT2_STATUS_DISABLED_PERSISTENT_MAPPINGS (0x02) +#define MPI_SAS_IOUNIT2_STATUS_FULL_PERSISTENT_MAPPINGS (0x01) + +/* values for SAS IO Unit Page 2 Flags field */ +#define MPI_SAS_IOUNIT2_FLAGS_DISABLE_PERSISTENT_MAPPINGS (0x01) + + +typedef struct _CONFIG_PAGE_SAS_IO_UNIT_3 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U32 MaxInvalidDwordCount; /* 0Ch */ + U32 InvalidDwordCountTime; /* 10h */ + U32 MaxRunningDisparityErrorCount; /* 14h */ + U32 RunningDisparityErrorTime; /* 18h */ + U32 MaxLossDwordSynchCount; /* 1Ch */ + U32 LossDwordSynchCountTime; /* 20h */ + U32 MaxPhyResetProblemCount; /* 24h */ + U32 PhyResetProblemTime; /* 28h */ +} fCONFIG_PAGE_SAS_IO_UNIT_3, MPI_POINTER PTR_CONFIG_PAGE_SAS_IO_UNIT_3, + SasIOUnitPage3_t, MPI_POINTER pSasIOUnitPage3_t; + +#define MPI_SASIOUNITPAGE3_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_SAS_EXPANDER_0 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U64 SASAddress; /* 0Ch */ + U32 Reserved2; /* 14h */ + U16 DevHandle; /* 18h */ + U16 ParentDevHandle; /* 1Ah */ + U16 ExpanderChangeCount; /* 1Ch */ + U16 ExpanderRouteIndexes; /* 1Eh */ + U8 NumPhys; /* 20h */ + U8 SASLevel; /* 21h */ + U8 Flags; /* 22h */ + U8 Reserved3; /* 23h */ +} fCONFIG_PAGE_SAS_EXPANDER_0, MPI_POINTER PTR_CONFIG_PAGE_SAS_EXPANDER_0, + SasExpanderPage0_t, MPI_POINTER pSasExpanderPage0_t; + +#define MPI_SASEXPANDER0_PAGEVERSION (0x00) + +/* values for SAS Expander Page 0 Flags field */ +#define MPI_SAS_EXPANDER0_FLAGS_ROUTE_TABLE_CONFIG (0x02) +#define MPI_SAS_EXPANDER0_FLAGS_CONFIG_IN_PROGRESS (0x01) + + +typedef struct _CONFIG_PAGE_SAS_DEVICE_0 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U64 SASAddress; /* 0Ch */ + U32 Reserved2; /* 14h */ + U16 DevHandle; /* 18h */ + U8 TargetID; /* 1Ah */ + U8 Bus; /* 1Bh */ + U32 DeviceInfo; /* 1Ch */ + U16 Flags; /* 20h */ + U8 PhysicalPort; /* 22h */ + U8 Reserved3; /* 23h */ +} fCONFIG_PAGE_SAS_DEVICE_0, MPI_POINTER PTR_CONFIG_PAGE_SAS_DEVICE_0, + SasDevicePage0_t, MPI_POINTER pSasDevicePage0_t; + +#define MPI_SASDEVICE0_PAGEVERSION (0x00) + +/* values for SAS Device Page 0 Flags field */ +#define MPI_SAS_DEVICE0_FLAGS_MAPPING_PERSISTENT (0x04) +#define MPI_SAS_DEVICE0_FLAGS_DEVICE_MAPPED (0x02) +#define MPI_SAS_DEVICE0_FLAGS_DEVICE_PRESENT (0x01) + +/* see mpi_sas.h for values for SAS Device Page 0 DeviceInfo values */ + + +typedef struct _CONFIG_PAGE_SAS_DEVICE_1 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U64 SASAddress; /* 0Ch */ + U32 Reserved2; /* 14h */ + U16 DevHandle; /* 18h */ + U8 TargetID; /* 1Ah */ + U8 Bus; /* 1Bh */ + U8 InitialRegDeviceFIS[20];/* 1Ch */ +} fCONFIG_PAGE_SAS_DEVICE_1, MPI_POINTER PTR_CONFIG_PAGE_SAS_DEVICE_1, + SasDevicePage1_t, MPI_POINTER pSasDevicePage1_t; + +#define MPI_SASDEVICE1_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_SAS_PHY_0 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U64 SASAddress; /* 0Ch */ + U16 AttachedDevHandle; /* 14h */ + U8 AttachedPhyIdentifier; /* 16h */ + U8 Reserved2; /* 17h */ + U32 AttachedDeviceInfo; /* 18h */ + U8 ProgrammedLinkRate; /* 20h */ + U8 HwLinkRate; /* 21h */ + U8 ChangeCount; /* 22h */ + U8 Reserved3; /* 23h */ + U32 PhyInfo; /* 24h */ +} fCONFIG_PAGE_SAS_PHY_0, MPI_POINTER PTR_CONFIG_PAGE_SAS_PHY_0, + SasPhyPage0_t, MPI_POINTER pSasPhyPage0_t; + +#define MPI_SASPHY0_PAGEVERSION (0x00) + +/* values for SAS PHY Page 0 ProgrammedLinkRate field */ +#define MPI_SAS_PHY0_PRATE_MAX_RATE_MASK (0xF0) +#define MPI_SAS_PHY0_PRATE_MAX_RATE_NOT_PROGRAMMABLE (0x00) +#define MPI_SAS_PHY0_PRATE_MAX_RATE_1_5 (0x80) +#define MPI_SAS_PHY0_PRATE_MAX_RATE_3_0 (0x90) +#define MPI_SAS_PHY0_PRATE_MIN_RATE_MASK (0x0F) +#define MPI_SAS_PHY0_PRATE_MIN_RATE_NOT_PROGRAMMABLE (0x00) +#define MPI_SAS_PHY0_PRATE_MIN_RATE_1_5 (0x08) +#define MPI_SAS_PHY0_PRATE_MIN_RATE_3_0 (0x09) + +/* values for SAS PHY Page 0 HwLinkRate field */ +#define MPI_SAS_PHY0_HWRATE_MAX_RATE_MASK (0xF0) +#define MPI_SAS_PHY0_HWRATE_MAX_RATE_1_5 (0x80) +#define MPI_SAS_PHY0_HWRATE_MAX_RATE_3_0 (0x90) +#define MPI_SAS_PHY0_HWRATE_MIN_RATE_MASK (0x0F) +#define MPI_SAS_PHY0_HWRATE_MIN_RATE_1_5 (0x08) +#define MPI_SAS_PHY0_HWRATE_MIN_RATE_3_0 (0x09) + +/* values for SAS PHY Page 0 PhyInfo field */ +#define MPI_SAS_PHY0_PHYINFO_SATA_PORT_ACTIVE (0x00004000) +#define MPI_SAS_PHY0_PHYINFO_SATA_PORT_SELECTOR (0x00002000) +#define MPI_SAS_PHY0_PHYINFO_VIRTUAL_PHY (0x00001000) + +#define MPI_SAS_PHY0_PHYINFO_MASK_PARTIAL_PATHWAY_TIME (0x00000F00) +#define MPI_SAS_PHY0_PHYINFO_SHIFT_PARTIAL_PATHWAY_TIME (8) + +#define MPI_SAS_PHY0_PHYINFO_MASK_ROUTING_ATTRIBUTE (0x000000F0) +#define MPI_SAS_PHY0_PHYINFO_DIRECT_ROUTING (0x00000000) +#define MPI_SAS_PHY0_PHYINFO_SUBTRACTIVE_ROUTING (0x00000010) +#define MPI_SAS_PHY0_PHYINFO_TABLE_ROUTING (0x00000020) + +#define MPI_SAS_PHY0_PHYINFO_MASK_LINK_RATE (0x0000000F) +#define MPI_SAS_PHY0_PHYINFO_UNKNOWN_LINK_RATE (0x00000000) +#define MPI_SAS_PHY0_PHYINFO_PHY_DISABLED (0x00000001) +#define MPI_SAS_PHY0_PHYINFO_NEGOTIATION_FAILED (0x00000002) +#define MPI_SAS_PHY0_PHYINFO_SATA_OOB_COMPLETE (0x00000003) +#define MPI_SAS_PHY0_PHYINFO_RATE_1_5 (0x00000008) +#define MPI_SAS_PHY0_PHYINFO_RATE_3_0 (0x00000009) + + +typedef struct _CONFIG_PAGE_SAS_PHY_1 +{ + fCONFIG_EXTENDED_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 08h */ + U32 InvalidDwordCount; /* 0Ch */ + U32 RunningDisparityErrorCount; /* 10h */ + U32 LossDwordSynchCount; /* 14h */ + U32 PhyResetProblemCount; /* 18h */ +} fCONFIG_PAGE_SAS_PHY_1, MPI_POINTER PTR_CONFIG_PAGE_SAS_PHY_1, + SasPhyPage1_t, MPI_POINTER pSasPhyPage1_t; + +#define MPI_SASPHY1_PAGEVERSION (0x00) + + #endif diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_fc.h linux-2.4.27/drivers/message/fusion/lsi/mpi_fc.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_fc.h 2003-06-13 07:51:34.000000000 -0700 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_fc.h 2004-08-07 16:26:04.905357091 -0700 @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2002 LSI Logic Corporation. + * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_FC.H + * Name: mpi_fc.h * Title: MPI Fibre Channel messages and structures * Creation Date: June 12, 2000 * - * MPI_FC.H Version: 01.02.03 + * mpi_fc.h Version: 01.05.xx * * Version History * --------------- @@ -45,7 +45,7 @@ /***************************************************************************** * -* F C T a r g e t M o d e M e s s a g e s +* F C D i r e c t A c c e s s M e s s a g e s * *****************************************************************************/ @@ -334,6 +334,7 @@ FcPrimitiveSendRequest_t, MPI_POINTER pFcPrimitiveSendRequest_t; #define MPI_FC_PRIM_SEND_FLAGS_PORT_MASK (0x01) +#define MPI_FC_PRIM_SEND_FLAGS_ML_RESET_LINK (0x02) #define MPI_FC_PRIM_SEND_FLAGS_RESET_LINK (0x04) #define MPI_FC_PRIM_SEND_FLAGS_STOP_SEND (0x08) #define MPI_FC_PRIM_SEND_FLAGS_SEND_ONCE (0x10) diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_inb.h linux-2.4.27/drivers/message/fusion/lsi/mpi_inb.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_inb.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_inb.h 2004-08-07 16:26:04.906357132 -0700 @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2003 LSI Logic Corporation. + * + * + * Name: mpi_inb.h + * Title: MPI Inband structures and definitions + * Creation Date: September 30, 2003 + * + * mpi_inb.h Version: 01.03.xx + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * ??-??-?? 01.03.01 Original release. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_INB_H +#define MPI_INB_H + +/****************************************************************************** +* +* I n b a n d M e s s a g e s +* +*******************************************************************************/ + + +/****************************************************************************/ +/* Inband Buffer Post Request */ +/****************************************************************************/ + +typedef struct _MSG_INBAND_BUFFER_POST_REQUEST +{ + U8 Reserved1; /* 00h */ + U8 BufferCount; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Reserved4; /* 0Ch */ + SGE_TRANS_SIMPLE_UNION SGL; /* 10h */ +} MSG_INBAND_BUFFER_POST_REQUEST, MPI_POINTER PTR_MSG_INBAND_BUFFER_POST_REQUEST, + MpiInbandBufferPostRequest_t , MPI_POINTER pMpiInbandBufferPostRequest_t; + + +typedef struct _WWN_FC_FORMAT +{ + U64 NodeName; /* 00h */ + U64 PortName; /* 08h */ +} WWN_FC_FORMAT, MPI_POINTER PTR_WWN_FC_FORMAT, + WwnFcFormat_t, MPI_POINTER pWwnFcFormat_t; + +typedef struct _WWN_SAS_FORMAT +{ + U64 WorldWideID; /* 00h */ + U32 Reserved1; /* 08h */ + U32 Reserved2; /* 0Ch */ +} WWN_SAS_FORMAT, MPI_POINTER PTR_WWN_SAS_FORMAT, + WwnSasFormat_t, MPI_POINTER pWwnSasFormat_t; + +typedef union _WWN_INBAND_FORMAT +{ + WWN_FC_FORMAT Fc; + WWN_SAS_FORMAT Sas; +} WWN_INBAND_FORMAT, MPI_POINTER PTR_WWN_INBAND_FORMAT, + WwnInbandFormat, MPI_POINTER pWwnInbandFormat; + + +/* Inband Buffer Post reply message */ + +typedef struct _MSG_INBAND_BUFFER_POST_REPLY +{ + U16 Reserved1; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TransferLength; /* 14h */ + U32 TransactionContext; /* 18h */ + WWN_INBAND_FORMAT Wwn; /* 1Ch */ + U32 IOCIdentifier[4]; /* 2Ch */ +} MSG_INBAND_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_INBAND_BUFFER_POST_REPLY, + MpiInbandBufferPostReply_t, MPI_POINTER pMpiInbandBufferPostReply_t; + + +/****************************************************************************/ +/* Inband Send Request */ +/****************************************************************************/ + +typedef struct _MSG_INBAND_SEND_REQUEST +{ + U16 Reserved1; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Reserved4; /* 0Ch */ + WWN_INBAND_FORMAT Wwn; /* 10h */ + U32 Reserved5; /* 20h */ + SGE_IO_UNION SGL; /* 24h */ +} MSG_INBAND_SEND_REQUEST, MPI_POINTER PTR_MSG_INBAND_SEND_REQUEST, + MpiInbandSendRequest_t , MPI_POINTER pMpiInbandSendRequest_t; + + +/* Inband Send reply message */ + +typedef struct _MSG_INBAND_SEND_REPLY +{ + U16 Reserved1; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ResponseLength; /* 14h */ +} MSG_INBAND_SEND_REPLY, MPI_POINTER PTR_MSG_INBAND_SEND_REPLY, + MpiInbandSendReply_t, MPI_POINTER pMpiInbandSendReply_t; + + +/****************************************************************************/ +/* Inband Response Request */ +/****************************************************************************/ + +typedef struct _MSG_INBAND_RSP_REQUEST +{ + U16 Reserved1; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Reserved4; /* 0Ch */ + WWN_INBAND_FORMAT Wwn; /* 10h */ + U32 IOCIdentifier[4]; /* 20h */ + U32 ResponseLength; /* 30h */ + SGE_IO_UNION SGL; /* 34h */ +} MSG_INBAND_RSP_REQUEST, MPI_POINTER PTR_MSG_INBAND_RSP_REQUEST, + MpiInbandRspRequest_t , MPI_POINTER pMpiInbandRspRequest_t; + + +/* Inband Response reply message */ + +typedef struct _MSG_INBAND_RSP_REPLY +{ + U16 Reserved1; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_INBAND_RSP_REPLY, MPI_POINTER PTR_MSG_INBAND_RSP_REPLY, + MpiInbandRspReply_t, MPI_POINTER pMpiInbandRspReply_t; + + +/****************************************************************************/ +/* Inband Abort Request */ +/****************************************************************************/ + +typedef struct _MSG_INBAND_ABORT_REQUEST +{ + U8 Reserved1; /* 00h */ + U8 AbortType; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Reserved4; /* 0Ch */ + U32 ContextToAbort; /* 10h */ +} MSG_INBAND_ABORT_REQUEST, MPI_POINTER PTR_MSG_INBAND_ABORT_REQUEST, + MpiInbandAbortRequest_t , MPI_POINTER pMpiInbandAbortRequest_t; + +#define MPI_INBAND_ABORT_TYPE_ALL_BUFFERS (0x00) +#define MPI_INBAND_ABORT_TYPE_EXACT_BUFFER (0x01) +#define MPI_INBAND_ABORT_TYPE_SEND_REQUEST (0x02) +#define MPI_INBAND_ABORT_TYPE_RESPONSE_REQUEST (0x03) + + +/* Inband Abort reply message */ + +typedef struct _MSG_INBAND_ABORT_REPLY +{ + U8 Reserved1; /* 00h */ + U8 AbortType; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_INBAND_ABORT_REPLY, MPI_POINTER PTR_MSG_INBAND_ABORT_REPLY, + MpiInbandAbortReply_t, MPI_POINTER pMpiInbandAbortReply_t; + + +#endif + diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_init.h linux-2.4.27/drivers/message/fusion/lsi/mpi_init.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_init.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_init.h 2004-08-07 16:26:04.907357173 -0700 @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2002 LSI Logic Corporation. + * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_INIT.H + * Name: mpi_init.h * Title: MPI initiator mode messages and structures * Creation Date: June 8, 2000 * - * MPI_INIT.H Version: 01.02.07 + * mpi_init.h Version: 01.05.xx * * Version History * --------------- @@ -47,7 +47,7 @@ *****************************************************************************/ /****************************************************************************/ -/* SCSI IO messages and assocaited structures */ +/* SCSI IO messages and associated structures */ /****************************************************************************/ typedef struct _MSG_SCSI_IO_REQUEST @@ -80,6 +80,16 @@ #define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00) #define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02) #define MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR (0x04) +#define MPI_SCSIIO_MSGFLGS_EEDP_TYPE_MASK (0xE0) +#define MPI_SCSIIO_MSGFLGS_EEDP_NONE (0x00) +#define MPI_SCSIIO_MSGFLGS_EEDP_RDPROTECT_T10 (0x20) +#define MPI_SCSIIO_MSGFLGS_EEDP_VRPROTECT_T10 (0x40) +#define MPI_SCSIIO_MSGFLGS_EEDP_WRPROTECT_T10 (0x60) +#define MPI_SCSIIO_MSGFLGS_EEDP_520_READ_MODE1 (0x20) +#define MPI_SCSIIO_MSGFLGS_EEDP_520_WRITE_MODE1 (0x40) +#define MPI_SCSIIO_MSGFLGS_EEDP_8_9_READ_MODE1 (0x60) +#define MPI_SCSIIO_MSGFLGS_EEDP_8_9_WRITE_MODE1 (0x80) + /* SCSI IO LUN fields */ @@ -182,6 +192,33 @@ /****************************************************************************/ +/* SCSI IO 32 Request message structure */ +/****************************************************************************/ + +typedef struct _MSG_SCSI_IO32_REQUEST +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 CDBLength; /* 04h */ + U8 SenseBufferLength; /* 05h */ + U8 Reserved; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 LUN[8]; /* 0Ch */ + U32 Control; /* 14h */ + U8 CDB[32]; /* 18h */ + U32 DataLength; /* 38h */ + U32 SenseBufferLowAddr; /* 3Ch */ + SGE_IO_UNION SGL; /* 40h */ +} MSG_SCSI_IO32_REQUEST, MPI_POINTER PTR_MSG_SCSI_IO32_REQUEST, + SCSIIO32Request_t, MPI_POINTER pSCSIIO32Request_t; + +/* SCSI IO 32 uses the same defines as above for SCSI IO */ + + +/****************************************************************************/ /* SCSI Task Management messages */ /****************************************************************************/ @@ -209,6 +246,7 @@ #define MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) #define MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS (0x04) #define MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) +#define MPI_SCSITASKMGMT_TASKTYPE_CLEAR_TASK_SET (0x06) /* MsgFlags bits */ #define MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION (0x00) diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_ioc.h linux-2.4.27/drivers/message/fusion/lsi/mpi_ioc.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_ioc.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_ioc.h 2004-08-07 16:26:04.908357214 -0700 @@ -2,11 +2,11 @@ * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_IOC.H + * Name: mpi_ioc.h * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages * Creation Date: August 11, 2000 * - * MPI_IOC.H Version: 01.02.08 + * mpi_ioc.h Version: 01.05.xx * * Version History * --------------- @@ -89,19 +89,21 @@ U8 Reserved1[2]; /* 0Eh */ U32 HostMfaHighAddr; /* 10h */ U32 SenseBufferHighAddr; /* 14h */ + U32 ReplyFifoHostSignalingAddr; /* 18h */ } MSG_IOC_INIT, MPI_POINTER PTR_MSG_IOC_INIT, IOCInit_t, MPI_POINTER pIOCInit_t; /* WhoInit values */ -#define MPI_WHOINIT_NO_ONE (0x00) -#define MPI_WHOINIT_SYSTEM_BIOS (0x01) -#define MPI_WHOINIT_ROM_BIOS (0x02) -#define MPI_WHOINIT_PCI_PEER (0x03) -#define MPI_WHOINIT_HOST_DRIVER (0x04) -#define MPI_WHOINIT_MANUFACTURER (0x05) +#define MPI_WHOINIT_NO_ONE (0x00) +#define MPI_WHOINIT_SYSTEM_BIOS (0x01) +#define MPI_WHOINIT_ROM_BIOS (0x02) +#define MPI_WHOINIT_PCI_PEER (0x03) +#define MPI_WHOINIT_HOST_DRIVER (0x04) +#define MPI_WHOINIT_MANUFACTURER (0x05) /* Flags values */ -#define MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE (0x01) +#define MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE (0x01) +#define MPI_IOCINIT_FLAGS_REPLY_FIFO_HOST_SIGNAL (0x02) typedef struct _MSG_IOC_INIT_REPLY { @@ -181,8 +183,10 @@ U8 MaxDevices; /* 2Eh */ U8 MaxBuses; /* 2Fh */ U32 FWImageSize; /* 30h */ - U32 Reserved4; /* 34h */ + U32 IOCCapabilities; /* 34h */ MPI_FW_VERSION FWVersion; /* 38h */ + U16 HighPriorityQueueDepth; /* 3Ch */ + U16 Reserved2; /* 3Eh */ } MSG_IOC_FACTS_REPLY, MPI_POINTER PTR_MSG_IOC_FACTS_REPLY, IOCFactsReply_t, MPI_POINTER pIOCFactsReply_t; @@ -194,12 +198,22 @@ #define MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001) #define MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002) +#define MPI_IOCFACTS_EXCEPT_FW_CHECKSUM_FAIL (0x0004) +#define MPI_IOCFACTS_EXCEPT_PERSISTENT_TABLE_FULL (0x0008) #define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (0x01) #define MPI_IOCFACTS_EVENTSTATE_DISABLED (0x00) #define MPI_IOCFACTS_EVENTSTATE_ENABLED (0x01) +#define MPI_IOCFACTS_CAPABILITY_HIGH_PRI_Q (0x00000001) +#define MPI_IOCFACTS_CAPABILITY_REPLY_HOST_SIGNAL (0x00000002) +#define MPI_IOCFACTS_CAPABILITY_QUEUE_FULL_HANDLING (0x00000004) +#define MPI_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER (0x00000008) +#define MPI_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER (0x00000010) +#define MPI_IOCFACTS_CAPABILITY_EXTENDED_BUFFER (0x00000020) +#define MPI_IOCFACTS_CAPABILITY_EEDP (0x00000040) + /***************************************************************************** @@ -255,6 +269,8 @@ #define MPI_PORTFACTS_PORTTYPE_INACTIVE (0x00) #define MPI_PORTFACTS_PORTTYPE_SCSI (0x01) #define MPI_PORTFACTS_PORTTYPE_FC (0x10) +#define MPI_PORTFACTS_PORTTYPE_ISCSI (0x20) +#define MPI_PORTFACTS_PORTTYPE_SAS (0x30) /* ProtocolFlags values */ @@ -388,6 +404,10 @@ #define MPI_EVENT_INTEGRATED_RAID (0x0000000B) #define MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE (0x0000000C) #define MPI_EVENT_ON_BUS_TIMER_EXPIRED (0x0000000D) +#define MPI_EVENT_QUEUE_FULL (0x0000000E) +#define MPI_EVENT_SAS_DEVICE_STATUS_CHANGE (0x0000000F) +#define MPI_EVENT_SAS_SES (0x00000010) +#define MPI_EVENT_PERSISTENT_TABLE_FULL (0x00000011) /* AckRequired field values */ @@ -435,6 +455,39 @@ #define MPI_EVENT_SCSI_DEV_STAT_RC_NOT_RESPONDING (0x04) #define MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA (0x05) +/* SAS Device Status Change Event data */ + +typedef struct _EVENT_DATA_SAS_DEVICE_STATUS_CHANGE +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 ReasonCode; /* 02h */ + U8 Reserved; /* 03h */ + U8 ASC; /* 04h */ + U8 ASCQ; /* 05h */ + U16 DevHandle; /* 06h */ + U32 DeviceInfo; /* 08h */ +} EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, + MPI_POINTER PTR_EVENT_DATA_SAS_DEVICE_STATUS_CHANGE, + MpiEventDataSasDeviceStatusChange_t, + MPI_POINTER pMpiEventDataSasDeviceStatusChange_t; + +/* MPI SAS Device Status Change Event data ReasonCode values */ +#define MPI_EVENT_SAS_DEV_STAT_RC_ADDED (0x03) +#define MPI_EVENT_SAS_DEV_STAT_RC_NOT_RESPONDING (0x04) +#define MPI_EVENT_SAS_DEV_STAT_RC_SMART_DATA (0x05) +#define MPI_EVENT_SAS_DEV_STAT_RC_NO_PERSIST_ADDED (0x06) + +/* SCSI Event data for Queue Full event */ + +typedef struct _EVENT_DATA_QUEUE_FULL +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U16 CurrentDepth; /* 02h */ +} EVENT_DATA_QUEUE_FULL, MPI_POINTER PTR_EVENT_DATA_QUEUE_FULL, + EventDataQueueFull_t, MPI_POINTER pEventDataQueueFull_t; + /* MPI Link Status Change Event data */ typedef struct _EVENT_DATA_LINK_STATUS @@ -540,6 +593,7 @@ #define MPI_FW_DOWNLOAD_ITYPE_FW (0x01) #define MPI_FW_DOWNLOAD_ITYPE_BIOS (0x02) #define MPI_FW_DOWNLOAD_ITYPE_NVDATA (0x03) +#define MPI_FW_DOWNLOAD_ITYPE_BOOTLOADER (0x04) typedef struct _FWDownloadTCSGE @@ -592,6 +646,7 @@ #define MPI_FW_UPLOAD_ITYPE_FW_FLASH (0x01) #define MPI_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) #define MPI_FW_UPLOAD_ITYPE_NVDATA (0x03) +#define MPI_FW_UPLOAD_ITYPE_BOOTLOADER (0x04) typedef struct _FWUploadTCSGE { @@ -655,6 +710,7 @@ #define MPI_FW_HEADER_PID_TYPE_MASK (0xF000) #define MPI_FW_HEADER_PID_TYPE_SCSI (0x0000) #define MPI_FW_HEADER_PID_TYPE_FC (0x1000) +#define MPI_FW_HEADER_PID_TYPE_SAS (0x2000) #define MPI_FW_HEADER_SIGNATURE_0 (0x5AEAA55A) #define MPI_FW_HEADER_SIGNATURE_1 (0xA55AEAA5) @@ -669,6 +725,7 @@ #define MPI_FW_HEADER_PID_PROD_CTX_SCSI (0x0600) #define MPI_FW_HEADER_PID_FAMILY_MASK (0x00FF) +/* SCSI */ #define MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI (0x0001) #define MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI (0x0002) #define MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI (0x0003) @@ -681,9 +738,15 @@ #define MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI (0x000A) #define MPI_FW_HEADER_PID_FAMILY_1030TA0_SCSI (0x000B) #define MPI_FW_HEADER_PID_FAMILY_1020TA0_SCSI (0x000C) +/* Fibre Channel */ #define MPI_FW_HEADER_PID_FAMILY_909_FC (0x0000) #define MPI_FW_HEADER_PID_FAMILY_919_FC (0x0001) #define MPI_FW_HEADER_PID_FAMILY_919X_FC (0x0002) +#define MPI_FW_HEADER_PID_FAMILY_919XL_FC (0x0003) +#define MPI_FW_HEADER_PID_FAMILY_949_FC (0x0004) +#define MPI_FW_HEADER_PID_FAMILY_959_FC (0x0005) +/* SAS */ +#define MPI_FW_HEADER_PID_FAMILY_1064_SAS (0x0001) typedef struct _MPI_EXT_IMAGE_HEADER { @@ -702,5 +765,6 @@ #define MPI_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) #define MPI_EXT_IMAGE_TYPE_FW (0x01) #define MPI_EXT_IMAGE_TYPE_NVDATA (0x03) +#define MPI_EXT_IMAGE_TYPE_BOOTLOADER (0x04) #endif diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_lan.h linux-2.4.27/drivers/message/fusion/lsi/mpi_lan.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_lan.h 2003-06-13 07:51:34.000000000 -0700 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_lan.h 2004-08-07 16:26:04.909357255 -0700 @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2002 LSI Logic Corporation. + * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_LAN.H + * Name: mpi_lan.h * Title: MPI LAN messages and structures * Creation Date: June 30, 2000 * - * MPI_LAN.H Version: 01.02.01 + * mpi_lan.h Version: 01.05.xx * * Version History * --------------- diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_raid.h linux-2.4.27/drivers/message/fusion/lsi/mpi_raid.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_raid.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_raid.h 2004-08-07 16:26:04.909357255 -0700 @@ -2,11 +2,11 @@ * Copyright (c) 2001-2003 LSI Logic Corporation. * * - * Name: MPI_RAID.H + * Name: mpi_raid.h * Title: MPI RAID message and structures * Creation Date: February 27, 2001 * - * MPI_RAID.H Version: 01.02.09 + * mpi_raid.h Version: 01.05.xx * * Version History * --------------- @@ -43,7 +43,7 @@ /****************************************************************************/ -/* RAID Volume Request */ +/* RAID Action Request */ /****************************************************************************/ typedef struct _MSG_RAID_ACTION diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_sas.h linux-2.4.27/drivers/message/fusion/lsi/mpi_sas.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_sas.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_sas.h 2004-08-07 16:26:04.911357338 -0700 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2003 LSI Logic Corporation. + * + * + * Name: mpi_sas.h + * Title: MPI Serial Attached SCSI structures and definitions + * Creation Date: April 23, 2003 + * + * mpi_sas.h Version: 01.05.xx + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * xx-yy-zz 01.05.01 Original release. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_SAS_H +#define MPI_SAS_H + +/***************************************************************************** +* +* S e r i a l A t t a c h e d S C S I M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Serial Management Protocol Passthrough Request */ +/****************************************************************************/ + +typedef struct _MSG_SMP_PASSTHROUGH_REQUEST +{ + U8 PassthroughFlags; /* 00h */ + U8 PhysicalPort; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 RequestDataLength; /* 04h */ + U8 ConnectionRate; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Reserved1; /* 0Ch */ + U64 SASAddress; /* 10h */ + U32 Reserved2; /* 18h */ + U32 Reserved3; /* 1Ch */ + SGE_SIMPLE_UNION SGL; /* 20h */ +} MSG_SMP_PASSTHROUGH_REQUEST, MPI_POINTER PTR_MSG_SMP_PASSTHROUGH_REQUEST, + SmpPassthroughRequest_t, MPI_POINTER pSmpPassthroughRequest_t; + +#define MPI_SMP_PT_REQ_PT_FLAGS_IMMEDIATE (0x80) + +#define MPI_SMP_PT_REQ_CONNECT_RATE_NEGOTIATED (0x00) +#define MPI_SMP_PT_REQ_CONNECT_RATE_1_5 (0x08) +#define MPI_SMP_PT_REQ_CONNECT_RATE_3_0 (0x09) + + +/* Serial Management Protocol Passthrough Reply */ +typedef struct _MSG_SMP_PASSTHROUGH_REPLY +{ + U8 PassthroughFlags; /* 00h */ + U8 PhysicalPort; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 ResponseDataLength; /* 04h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Reserved2; /* 0Ch */ + U8 SASStatus; /* 0Dh */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 Reserved3; /* 14h */ + U8 ResponseData[4]; /* 18h */ +} MSG_SMP_PASSTHROUGH_REPLY, MPI_POINTER PTR_MSG_SMP_PASSTHROUGH_REPLY, + SmpPassthroughReply_t, MPI_POINTER pSmpPassthroughReply_t; + +#define MPI_SMP_PT_REPLY_PT_FLAGS_IMMEDIATE (0x80) + +/* values for the SASStatus field */ +#define MPI_SASSTATUS_SUCCESS (0x00) +#define MPI_SASSTATUS_UNKNOWN_ERROR (0x01) +#define MPI_SASSTATUS_INVALID_FRAME (0x02) +#define MPI_SASSTATUS_UTC_BAD_DEST (0x03) +#define MPI_SASSTATUS_UTC_BREAK_RECEIVED (0x04) +#define MPI_SASSTATUS_UTC_CONNECT_RATE_NOT_SUPPORTED (0x05) +#define MPI_SASSTATUS_UTC_PORT_LAYER_REQUEST (0x06) +#define MPI_SASSTATUS_UTC_PROTOCOL_NOT_SUPPORTED (0x07) +#define MPI_SASSTATUS_UTC_STP_RESOURCES_BUSY (0x08) +#define MPI_SASSTATUS_UTC_WRONG_DESTINATION (0x09) +#define MPI_SASSTATUS_SHORT_INFORMATION_UNIT (0x0A) +#define MPI_SASSTATUS_LONG_INFORMATION_UNIT (0x0B) +#define MPI_SASSTATUS_XFER_RDY_INCORRECT_WRITE_DATA (0x0C) +#define MPI_SASSTATUS_XFER_RDY_REQUEST_OFFSET_ERROR (0x0D) +#define MPI_SASSTATUS_XFER_RDY_NOT_EXPECTED (0x0E) +#define MPI_SASSTATUS_DATA_INCORRECT_DATA_LENGTH (0x0F) +#define MPI_SASSTATUS_DATA_TOO_MUCH_READ_DATA (0x10) +#define MPI_SASSTATUS_DATA_OFFSET_ERROR (0x11) +#define MPI_SASSTATUS_SDSF_NAK_RECEIVED (0x12) +#define MPI_SASSTATUS_SDSF_CONNECTION_FAILED (0x13) +#define MPI_SASSTATUS_INITIATOR_RESPONSE_TIMEOUT (0x14) + + +/* + * Values for the SAS DeviceInfo field used in SAS Device Status Change Event + * data and SAS IO Unit Configuration pages. + */ +#define MPI_SAS_DEVICE_INFO_ATAPI_DEVICE (0x00002000) +#define MPI_SAS_DEVICE_INFO_LSI_DEVICE (0x00001000) +#define MPI_SAS_DEVICE_INFO_DIRECT_ATTACH (0x00000800) +#define MPI_SAS_DEVICE_INFO_SSP_TARGET (0x00000400) +#define MPI_SAS_DEVICE_INFO_STP_TARGET (0x00000200) +#define MPI_SAS_DEVICE_INFO_SMP_TARGET (0x00000100) +#define MPI_SAS_DEVICE_INFO_SATA_DEVICE (0x00000080) +#define MPI_SAS_DEVICE_INFO_SSP_INITIATOR (0x00000040) +#define MPI_SAS_DEVICE_INFO_STP_INITIATOR (0x00000020) +#define MPI_SAS_DEVICE_INFO_SMP_INITIATOR (0x00000010) +#define MPI_SAS_DEVICE_INFO_SATA_HOST (0x00000008) + +#define MPI_SAS_DEVICE_INFO_MASK_DEVICE_TYPE (0x00000007) +#define MPI_SAS_DEVICE_INFO_NO_DEVICE (0x00000000) +#define MPI_SAS_DEVICE_INFO_END_DEVICE (0x00000001) +#define MPI_SAS_DEVICE_INFO_EDGE_EXPANDER (0x00000002) +#define MPI_SAS_DEVICE_INFO_FANOUT_EXPANDER (0x00000003) + + +/****************************************************************************/ +/* SAS IO Unit Control Request */ +/****************************************************************************/ + +typedef struct _MSG_SAS_IOUNIT_CONTROL_REQUEST +{ + U8 Operation; /* 00h */ + U8 Reserved1; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 TargetID; /* 0Ch */ + U8 Bus; /* 0Dh */ + U8 PhyNum; /* 0Eh */ + U8 Reserved4; /* 0Fh */ + U32 Reserved5; /* 10h */ + U64 SASAddress; /* 14h */ + U32 Reserved6; /* 1Ch */ +} MSG_SAS_IOUNIT_CONTROL_REQUEST, MPI_POINTER PTR_MSG_SAS_IOUNIT_CONTROL_REQUEST, + SasIoUnitControlRequest_t, MPI_POINTER pSasIoUnitControlRequest_t; + +/* values for the ... field */ +#define MPI_SAS_OP_CLEAR_NOT_PRESENT (0x01) +#define MPI_SAS_OP_CLEAR_ALL (0x02) +#define MPI_SAS_OP_MAP (0x03) +#define MPI_SAS_OP_MOVE (0x04) +#define MPI_SAS_OP_CLEAR (0x05) +#define MPI_SAS_OP_PHY_LINK_RESET (0x06) +#define MPI_SAS_OP_PHY_HARD_RESET (0x07) +#define MPI_SAS_OP_PHY_CLEAR_ERROR_LOG (0x08) + + +/* SAS IO Unit Control Reply */ +typedef struct _MSG_SAS_IOUNIT_CONTROL_REPLY +{ + U8 Operation; /* 00h */ + U8 Reserved1; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_SAS_IOUNIT_CONTROL_REPLY, MPI_POINTER PTR_MSG_SAS_IOUNIT_CONTROL_REPLY, + SasIoUnitControlReply_t, MPI_POINTER pSasIoUnitControlReply_t; + +#endif + + diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_targ.h linux-2.4.27/drivers/message/fusion/lsi/mpi_targ.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_targ.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_targ.h 2004-08-07 16:26:04.911357338 -0700 @@ -2,11 +2,11 @@ * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_TARG.H + * Name: mpi_targ.h * Title: MPI Target mode messages and structures * Creation Date: June 22, 2000 * - * MPI_TARG.H Version: 01.02.09 + * mpi_targ.h Version: 01.05.xx * * Version History * --------------- diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_tool.h linux-2.4.27/drivers/message/fusion/lsi/mpi_tool.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_tool.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_tool.h 2004-08-07 16:26:04.912357379 -0700 @@ -1,12 +1,12 @@ /* - * Copyright (c) 2001 LSI Logic Corporation. + * Copyright (c) 2001-2003 LSI Logic Corporation. * * - * Name: MPI_TOOL.H + * Name: mpi_tool.h * Title: MPI Toolbox structures and definitions * Creation Date: July 30, 2001 * - * MPI Version: 01.02.02 + * mpi_tool.h Version: 01.05.xx * * Version History * --------------- @@ -24,6 +24,8 @@ #define MPI_TOOLBOX_CLEAN_TOOL (0x00) #define MPI_TOOLBOX_MEMORY_MOVE_TOOL (0x01) #define MPI_TOOLBOX_DIAG_DATA_UPLOAD_TOOL (0x02) +#define MPI_TOOLBOX_ISTWI_READ_WRITE_TOOL (0x03) +#define MPI_TOOLBOX_FC_MANAGEMENT_TOOL (0x04) /****************************************************************************/ @@ -68,6 +70,12 @@ #define MPI_TOOLBOX_CLEAN_NVSRAM (0x00000001) #define MPI_TOOLBOX_CLEAN_SEEPROM (0x00000002) #define MPI_TOOLBOX_CLEAN_FLASH (0x00000004) +#define MPI_TOOLBOX_CLEAN_BOOTLOADER (0x04000000) +#define MPI_TOOLBOX_CLEAN_FW_BACKUP (0x08000000) +#define MPI_TOOLBOX_CLEAN_FW_CURRENT (0x10000000) +#define MPI_TOOLBOX_CLEAN_OTHER_PERSIST_PAGES (0x20000000) +#define MPI_TOOLBOX_CLEAN_PERSIST_MANUFACT_PAGES (0x40000000) +#define MPI_TOOLBOX_CLEAN_BOOT_SERVICES (0x80000000) /****************************************************************************/ @@ -124,6 +132,174 @@ #define MPI_TB_DIAG_FORMAT_FC_TRACE_1 (0x04) +/****************************************************************************/ +/* Toolbox ISTWI Read Write request */ +/****************************************************************************/ + +typedef struct _MSG_TOOLBOX_ISTWI_READ_WRITE_REQUEST +{ + U8 Tool; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Flags; /* 0Ch */ + U8 BusNum; /* 0Dh */ + U16 Reserved3; /* 0Eh */ + U8 NumAddressBytes; /* 10h */ + U8 Reserved4; /* 11h */ + U16 DataLength; /* 12h */ + U8 DeviceAddr; /* 14h */ + U8 Addr1; /* 15h */ + U8 Addr2; /* 16h */ + U8 Addr3; /* 17h */ + U32 Reserved5; /* 18h */ + SGE_SIMPLE_UNION SGL; /* 1Ch */ +} MSG_TOOLBOX_ISTWI_READ_WRITE_REQUEST, MPI_POINTER PTR_MSG_TOOLBOX_ISTWI_READ_WRITE_REQUEST, + ToolboxIstwiReadWriteRequest_t, MPI_POINTER pToolboxIstwiReadWriteRequest_t; + +#define MPI_TB_ISTWI_FLAGS_WRITE (0x00) +#define MPI_TB_ISTWI_FLAGS_READ (0x01) + + +/****************************************************************************/ +/* Toolbox FC Management request */ +/****************************************************************************/ + +/* ActionInfo for Bus and TargetId */ +typedef struct _MPI_TB_FC_MANAGE_BUS_TID_AI +{ + U16 Reserved; /* 00h */ + U8 Bus; /* 02h */ + U8 TargetId; /* 03h */ +} MPI_TB_FC_MANAGE_BUS_TID_AI, MPI_POINTER PTR_MPI_TB_FC_MANAGE_BUS_TID_AI, + MpiTbFcManageBusTidAi_t, MPI_POINTER pMpiTbFcManageBusTidAi_t; + +/* ActionInfo for port identifier */ +typedef struct _MPI_TB_FC_MANAGE_PID_AI +{ + U32 PortIdentifier; /* 00h */ +} MPI_TB_FC_MANAGE_PID_AI, MPI_POINTER PTR_MPI_TB_FC_MANAGE_PID_AI, + MpiTbFcManagePidAi_t, MPI_POINTER pMpiTbFcManagePidAi_t; + +/* union of ActionInfo */ +typedef union _MPI_TB_FC_MANAGE_AI_UNION +{ + MPI_TB_FC_MANAGE_BUS_TID_AI BusTid; + MPI_TB_FC_MANAGE_PID_AI Port; +} MPI_TB_FC_MANAGE_AI_UNION, MPI_POINTER PTR_MPI_TB_FC_MANAGE_AI_UNION, + MpiTbFcManageAiUnion_t, MPI_POINTER pMpiTbFcManageAiUnion_t; + +typedef struct _MSG_TOOLBOX_FC_MANAGE_REQUEST +{ + U8 Tool; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Action; /* 0Ch */ + U8 Reserved3; /* 0Dh */ + U16 Reserved4; /* 0Eh */ + MPI_TB_FC_MANAGE_AI_UNION ActionInfo; /* 10h */ +} MSG_TOOLBOX_FC_MANAGE_REQUEST, MPI_POINTER PTR_MSG_TOOLBOX_FC_MANAGE_REQUEST, + ToolboxFcManageRequest_t, MPI_POINTER pToolboxFcManageRequest_t; + +/* defines for the Action field */ +#define MPI_TB_FC_MANAGE_ACTION_DISC_ALL (0x00) +#define MPI_TB_FC_MANAGE_ACTION_DISC_PID (0x01) +#define MPI_TB_FC_MANAGE_ACTION_DISC_BUS_TID (0x02) + + +/****************************************************************************/ +/* Diagnostic Buffer Post request */ +/****************************************************************************/ + +typedef struct _MSG_DIAG_BUFFER_POST_REQUEST +{ + U8 TraceLevel; /* 00h */ + U8 BufferType; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 ExtendedType; /* 0Ch */ + U32 BufferLength; /* 10h */ + U32 ProductSpecific[4]; /* 14h */ + U32 Reserved3; /* 18h */ + SGE_SIMPLE_UNION SGL; /* 28h */ +} MSG_DIAG_BUFFER_POST_REQUEST, MPI_POINTER PTR_MSG_DIAG_BUFFER_POST_REQUEST, + DiagBufferPostRequest_t, MPI_POINTER pDiagBufferPostRequest_t; + +#define MPI_DIAG_BUF_TYPE_TRACE (0x00) +#define MPI_DIAG_BUF_TYPE_SNAPSHOT (0x01) +#define MPI_DIAG_BUF_TYPE_EXTENDED (0x02) + +#define MPI_DIAG_EXTENDED_QTAG (0x00000001) + + +/* Diagnostic Buffer Post reply */ +typedef struct _MSG_DIAG_BUFFER_POST_REPLY +{ + U8 Reserved1; /* 00h */ + U8 BufferType; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TransferLength; /* 14h */ +} MSG_DIAG_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_DIAG_BUFFER_POST_REPLY, + DiagBufferPostReply_t, MPI_POINTER pDiagBufferPostReply_t; + + +/****************************************************************************/ +/* Diagnostic Release request */ +/****************************************************************************/ + +typedef struct _MSG_DIAG_RELEASE_REQUEST +{ + U8 Reserved1; /* 00h */ + U8 BufferType; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ +} MSG_DIAG_RELEASE_REQUEST, MPI_POINTER PTR_MSG_DIAG_RELEASE_REQUEST, + DiagReleaseRequest_t, MPI_POINTER pDiagReleaseRequest_t; + + +/* Diagnostic Release reply */ +typedef struct _MSG_DIAG_RELEASE_REPLY +{ + U8 Reserved1; /* 00h */ + U8 BufferType; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved4; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_DIAG_RELEASE_REPLY, MPI_POINTER PTR_MSG_DIAG_RELEASE_REPLY, + DiagReleaseReply_t, MPI_POINTER pDiagReleaseReply_t; + + #endif diff -urN linux-2.4.26/drivers/message/fusion/lsi/mpi_type.h linux-2.4.27/drivers/message/fusion/lsi/mpi_type.h --- linux-2.4.26/drivers/message/fusion/lsi/mpi_type.h 2003-06-13 07:51:34.000000000 -0700 +++ linux-2.4.27/drivers/message/fusion/lsi/mpi_type.h 2004-08-07 16:26:04.913357420 -0700 @@ -1,12 +1,12 @@ /* - * Copyright (c) 2000-2002 LSI Logic Corporation. + * Copyright (c) 2000-2003 LSI Logic Corporation. * * - * Name: MPI_TYPE.H + * Name: mpi_type.h * Title: MPI Basic type definitions * Creation Date: June 6, 2000 * - * MPI Version: 01.02.01 + * mpi_type.h Version: 01.05.xx * * Version History * --------------- diff -urN linux-2.4.26/drivers/message/fusion/mptbase.c linux-2.4.27/drivers/message/fusion/mptbase.c --- linux-2.4.26/drivers/message/fusion/mptbase.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/mptbase.c 2004-08-07 16:26:04.920357707 -0700 @@ -209,7 +209,6 @@ static int GetIoUnitPage2(MPT_ADAPTER *ioc); static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum); static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum); -static int mpt_findImVolumes(MPT_ADAPTER *ioc); static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); static void mpt_read_ioc_pg_4(MPT_ADAPTER *ioc); static void mpt_timer_expired(unsigned long data); @@ -567,7 +566,8 @@ } else if (func == MPI_FUNCTION_EVENT_ACK) { dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, EventAck reply received\n", ioc->name)); - } else if (func == MPI_FUNCTION_CONFIG) { + } else if (func == MPI_FUNCTION_CONFIG || + func == MPI_FUNCTION_TOOLBOX) { CONFIGPARMS *pCfg; unsigned long flags; @@ -904,6 +904,8 @@ mf_dma_addr = iocp->req_frames_low_dma + req_offset; CHIPREG_WRITE32(&iocp->chip->RequestFifo, mf_dma_addr); + } else { + printk (KERN_ERR "mpt_put_msg_frame: Invalid iocid=%d\n", iocid); } } @@ -1053,7 +1055,7 @@ ((reqBytes/4)<chip->IntStatus, 0); - if ((r = WaitForDoorbellAck(iocp, 2, sleepFlag)) < 0) { + if ((r = WaitForDoorbellAck(iocp, 5, sleepFlag)) < 0) { return -2; } @@ -1080,7 +1082,7 @@ (req_as_bytes[(ii*4) + 2] << 16) | (req_as_bytes[(ii*4) + 3] << 24)); CHIPREG_WRITE32(&iocp->chip->Doorbell, word); - if ((r = WaitForDoorbellAck(iocp, 2, sleepFlag)) < 0) { + if ((r = WaitForDoorbellAck(iocp, 5, sleepFlag)) < 0) { r = -3; break; } @@ -1279,6 +1281,8 @@ int ii; int r = -ENODEV; u64 mask = 0xffffffffffffffffULL; + u8 revision; + u8 pcixcmd; if (pci_enable_device(pdev)) return r; @@ -1418,46 +1422,45 @@ } else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929X) { ioc->chip_type = FC929X; - ioc->prod_name = "LSIFC929X"; - { + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + if (revision < XL_929) { + ioc->prod_name = "LSIFC929X"; /* 929X Chip Fix. Set Split transactions level - * for PCIX. Set MOST bits to zero. - */ - u8 pcixcmd; + * for PCIX. Set MOST bits to zero. + */ pci_read_config_byte(pdev, 0x6a, &pcixcmd); pcixcmd &= 0x8F; pci_write_config_byte(pdev, 0x6a, pcixcmd); + } else { + ioc->prod_name = "LSIFC929XL"; + /* 929XL Chip Fix. Set MMRBC to 0x08. + */ + pci_read_config_byte(pdev, 0x6a, &pcixcmd); + pcixcmd |= 0x08; + pci_write_config_byte(pdev, 0x6a, pcixcmd); } } else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919X) { ioc->chip_type = FC919X; ioc->prod_name = "LSIFC919X"; - { - /* 919X Chip Fix. Set Split transactions level - * for PCIX. Set MOST bits to zero. - */ - u8 pcixcmd; - pci_read_config_byte(pdev, 0x6a, &pcixcmd); - pcixcmd &= 0x8F; - pci_write_config_byte(pdev, 0x6a, pcixcmd); - } + /* 919X Chip Fix. Set Split transactions level + * for PCIX. Set MOST bits to zero. + */ + pci_read_config_byte(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0x8F; + pci_write_config_byte(pdev, 0x6a, pcixcmd); } else if (pdev->device == MPI_MANUFACTPAGE_DEVID_53C1030) { ioc->chip_type = C1030; ioc->prod_name = "LSI53C1030"; - { - u8 revision; - - /* 1030 Chip Fix. Disable Split transactions - * for PCIX. Set MOST bits to zero if Rev < C0( = 8). - */ - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); - if (revision < 0x08) { - u8 pcixcmd; - pci_read_config_byte(pdev, 0x6a, &pcixcmd); - pcixcmd &= 0x8F; - pci_write_config_byte(pdev, 0x6a, pcixcmd); - } + /* 1030 Chip Fix. Disable Split transactions + * for PCIX. Set MOST bits to zero if Rev < C0( = 8). + */ + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + if (revision < C0_1030) { + pci_read_config_byte(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0x8F; + pci_write_config_byte(pdev, 0x6a, pcixcmd); } } else if (pdev->device == MPI_MANUFACTPAGE_DEVID_1030_53C1035) { @@ -1610,11 +1613,17 @@ ioc->alt_ioc->name, r); } + for (ii=0; ii<5; ii++) { /* Get IOC facts! Allow 1 retry */ - if ((r = GetIocFacts(ioc, sleepFlag, reason)) != 0) - r = GetIocFacts(ioc, sleepFlag, reason); + if ((r = GetIocFacts(ioc, sleepFlag, reason)) != 0) { + dinitprintk((MYIOC_s_INFO_FMT "ii=%d IocFacts failed r=%x\n", ioc->name, ii, r)); + } else + break; + } + if (r) { + dinitprintk((MYIOC_s_INFO_FMT "Retry IocFacts failed r=%x\n", ioc->name, r)); ret = -2; } else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { MptDisplayIocCapabilities(ioc); @@ -1622,11 +1631,13 @@ if (alt_ioc_ready) { if ((r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) { + dinitprintk((MYIOC_s_INFO_FMT "Initial Alt IocFacts failed r=%x\n", ioc->name, r)); /* Retry - alt IOC was initialized once */ r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason); } if (r) { + dinitprintk((MYIOC_s_INFO_FMT "Retry Alt IocFacts failed r=%x\n", ioc->name, r)); alt_ioc_ready = 0; reset_alt_ioc_active = 0; } else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { @@ -1862,15 +1873,8 @@ u32 state; int ret; - /* Disable the FW */ - state = mpt_GetIocState(this, 1); - if (state == MPI_IOC_STATE_OPERATIONAL) { - SendIocReset(this, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, NO_SLEEP); - } - if (this->cached_fw != NULL) { ddlprintk((KERN_INFO MYNAM ": Pushing FW onto adapter\n")); - if ((ret = mpt_downloadboot(this, NO_SLEEP)) < 0) { printk(KERN_WARNING MYNAM ": firmware downloadboot failure (%d)!\n", ret); @@ -1921,25 +1925,11 @@ } if (freeup && this->cached_fw != NULL) { - int ii = 0; - - while ((ii < this->num_fw_frags) && (this->cached_fw[ii]!= NULL)) { - sz = this->cached_fw[ii]->size; - pci_free_consistent(this->pcidev, sz, - this->cached_fw[ii]->fw, this->cached_fw[ii]->fw_dma); - this->cached_fw[ii]->fw = NULL; - this->alloc_total -= sz; - - kfree(this->cached_fw[ii]); - this->cached_fw[ii] = NULL; - this->alloc_total -= sizeof(fw_image_t); - - ii++; - } - kfree(this->cached_fw); + sz = this->facts.FWImageSize; + pci_free_consistent(this->pcidev, sz, + this->cached_fw, this->cached_fw_dma); this->cached_fw = NULL; - sz = this->num_fw_frags * sizeof(void *); this->alloc_total -= sz; } @@ -1981,11 +1971,6 @@ sz_first = this->alloc_total; - if (this->alt_ioc != NULL) { - this->alt_ioc->alt_ioc = NULL; - this->alt_ioc = NULL; - } - mpt_adapter_disable(this, 1); if (this->pci_irq != -1) { @@ -2101,13 +2086,14 @@ if ((int)ioc->chip_type <= (int)FC929) return 0; else { + return 0; /* Workaround from broken 1030 FW. * Force a diagnostic reset if fails. */ - if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) == 0) +/* if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) == 0) return 0; else - statefault = 4; + statefault = 4; */ } } @@ -2126,7 +2112,7 @@ * Hmmm... Did it get left operational? */ if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL) { - dprintk((MYIOC_s_WARN_FMT "IOC operational unexpected\n", + dinitprintk((MYIOC_s_WARN_FMT "IOC operational unexpected\n", ioc->name)); /* Check WhoInit. @@ -2270,13 +2256,13 @@ get_facts.Function = MPI_FUNCTION_IOC_FACTS; /* Assert: All other get_facts fields are zero! */ - dprintk((MYIOC_s_INFO_FMT "Sending get IocFacts request\n", ioc->name)); + dinitprintk((MYIOC_s_INFO_FMT "Sending get IocFacts request\n", ioc->name)); /* No non-zero fields in the get_facts request are greater than * 1 byte in size, so we can just fire it off as is. */ r = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&get_facts, - reply_sz, (u16*)facts, 3 /*seconds*/, sleepFlag); + reply_sz, (u16*)facts, 5 /*seconds*/, sleepFlag); if (r != 0) return r; @@ -2370,8 +2356,8 @@ return r; } } else { - printk(MYIOC_s_ERR_FMT "Invalid IOC facts reply!\n", - ioc->name); + printk(MYIOC_s_ERR_FMT "Invalid IOC facts reply, msgLength=%d offsetof=%d!\n", + ioc->name, facts->MsgLength, (offsetof(IOCFactsReply_t, RequestFrameSize)/sizeof(u32))); return -66; } @@ -2425,7 +2411,7 @@ * 1 byte in size, so we can just fire it off as is. */ ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&get_pfacts, - reply_sz, (u16*)pfacts, 3 /*seconds*/, sleepFlag); + reply_sz, (u16*)pfacts, 5 /*seconds*/, sleepFlag); if (ii != 0) return ii; @@ -2511,7 +2497,7 @@ ioc_init.SenseBufferHighAddr = cpu_to_le32(0); } - dprintk((MYIOC_s_INFO_FMT "Sending IOCInit (req @ %p)\n", + dhsprintk((MYIOC_s_INFO_FMT "Sending IOCInit (req @ %p)\n", ioc->name, &ioc_init)); r = mpt_handshake_req_reply_wait(ioc, sizeof(IOCInit_t), (u32*)&ioc_init, @@ -2523,6 +2509,9 @@ * since we don't even look at it's contents. */ + dhsprintk((MYIOC_s_INFO_FMT "Sending PortEnable (req @ %p)\n", + ioc->name, &ioc_init)); + if ((r = SendPortEnable(ioc, 0, sleepFlag)) != 0) return r; @@ -2589,7 +2578,7 @@ /* port_enable.MsgFlags = 0; */ /* port_enable.MsgContext = 0; */ - dprintk((MYIOC_s_INFO_FMT "Sending Port(%d)Enable (req @ %p)\n", + dinitprintk((MYIOC_s_INFO_FMT "Sending Port(%d)Enable (req @ %p)\n", ioc->name, portnum, &port_enable)); /* RAID FW may take a long time to enable @@ -2617,86 +2606,14 @@ * Outputs: frags - number of fragments needed * Return NULL if failed. */ -void * -mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz) +void +mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size) { - fw_image_t **cached_fw; - u8 *mem; - dma_addr_t fw_dma; - int alloc_total = 0; - int bytes_left, bytes, num_frags; - int sz, ii; - /* cached_fw */ - sz = ioc->num_fw_frags * sizeof(void *); - mem = kmalloc(sz, GFP_ATOMIC); - if (mem == NULL) - return NULL; - - memset(mem, 0, sz); - cached_fw = (fw_image_t **)mem; - alloc_total += sz; - - /* malloc fragment memory - * fw_image_t struct and dma for fw data - */ - bytes_left = size; - ii = 0; - num_frags = 0; - bytes = bytes_left; - while((bytes_left) && (num_frags < ioc->num_fw_frags)) { - if (cached_fw[ii] == NULL) { - mem = kmalloc(sizeof(fw_image_t), GFP_ATOMIC); - if (mem == NULL) - break; - - memset(mem, 0, sizeof(fw_image_t)); - cached_fw[ii] = (fw_image_t *)mem; - alloc_total += sizeof(fw_image_t); - } - - mem = pci_alloc_consistent(ioc->pcidev, bytes, &fw_dma); - if (mem == NULL) { - if (bytes > 0x10000) - bytes = 0x10000; - else if (bytes > 0x8000) - bytes = 0x8000; - else if (bytes > 0x4000) - bytes = 0x4000; - else if (bytes > 0x2000) - bytes = 0x2000; - else if (bytes > 0x1000) - bytes = 0x1000; - else - break; - - continue; - } - - cached_fw[ii]->fw = mem; - cached_fw[ii]->fw_dma = fw_dma; - cached_fw[ii]->size = bytes; - memset(mem, 0, bytes); - alloc_total += bytes; - - bytes_left -= bytes; - - num_frags++; - ii++; - } - - if (bytes_left ) { - /* Major Failure. - */ - mpt_free_fw_memory(ioc, cached_fw); - return NULL; - } - - *frags = num_frags; - *alloc_sz = alloc_total; - return (void *) cached_fw; + if ( (ioc->cached_fw = pci_alloc_consistent(ioc->pcidev, size, &ioc->cached_fw_dma) ) ) + ioc->alloc_total += size; } /* @@ -2704,45 +2621,14 @@ * Else, delete a secondary image in same format. */ void -mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img) +mpt_free_fw_memory(MPT_ADAPTER *ioc) { - fw_image_t **cached_fw; - int ii; int sz; - int alloc_freed = 0; - - if (alt_img != NULL) - cached_fw = alt_img; - else - cached_fw = ioc->cached_fw; - - if (cached_fw == NULL) - return; - ii = 0; - while ((ii < ioc->num_fw_frags) && (cached_fw[ii]!= NULL)) { - sz = cached_fw[ii]->size; - if (sz > 0) { - pci_free_consistent(ioc->pcidev, sz, - cached_fw[ii]->fw, cached_fw[ii]->fw_dma); - } - cached_fw[ii]->fw = NULL; - alloc_freed += sz; - - kfree(cached_fw[ii]); - cached_fw[ii] = NULL; - alloc_freed += sizeof(fw_image_t); - - ii++; - } - - kfree(cached_fw); - cached_fw = NULL; - sz = ioc->num_fw_frags * sizeof(void *); - alloc_freed += sz; - - if (alt_img == NULL) - ioc->alloc_total -= alloc_freed; + sz = ioc->facts.FWImageSize; + pci_free_consistent(ioc->pcidev, sz, + ioc->cached_fw, ioc->cached_fw_dma); + ioc->cached_fw = NULL; return; } @@ -2771,9 +2657,9 @@ FWUploadReply_t *preply; FWUploadTCSGE_t *ptcsge; int sgeoffset; + u32 flagsLength; int ii, sz, reply_sz; int cmdStatus, freeMem = 0; - int num_frags, alloc_sz; /* If the image size is 0 or if the pointer is * not NULL (error), we are done. @@ -2781,24 +2667,21 @@ if (((sz = ioc->facts.FWImageSize) == 0) || ioc->cached_fw) return 0; - ioc->num_fw_frags = ioc->req_sz - sizeof(FWUpload_t) + sizeof(dma_addr_t) + sizeof(u32) -1; - ioc->num_fw_frags /= sizeof(dma_addr_t) + sizeof(u32); + if ( sz & 0x01 ) + sz += 1; + if ( sz & 0x02 ) + sz += 2; - ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc, - ioc->facts.FWImageSize, &num_frags, &alloc_sz); + mpt_alloc_fw_memory(ioc, sz); if (ioc->cached_fw == NULL) { /* Major Failure. */ - mpt_free_fw_memory(ioc, NULL); - ioc->cached_fw = NULL; - return -ENOMEM; } - ioc->alloc_total += alloc_sz; - ddlprintk((KERN_INFO MYNAM ": FW Image @ %p, sz=%d bytes\n", - (void *)(ulong)ioc->cached_fw, ioc->facts.FWImageSize)); + dinitprintk((KERN_WARNING MYNAM ": FW Image @ %p[%p], sz=%d[%x] bytes\n", + ioc->cached_fw, (void *)(ulong)ioc->cached_fw_dma, sz, sz)); prequest = (FWUpload_t *)&request; preply = (FWUploadReply_t *)&reply; @@ -2811,39 +2694,27 @@ prequest->ImageType = MPI_FW_UPLOAD_ITYPE_FW_IOC_MEM; prequest->Function = MPI_FUNCTION_FW_UPLOAD; - prequest->MsgContext = 0; /* anything */ ptcsge = (FWUploadTCSGE_t *) &prequest->SGL; - ptcsge->Reserved = 0; - ptcsge->ContextSize = 0; ptcsge->DetailsLength = 12; ptcsge->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT; - ptcsge->Reserved1 = 0; - ptcsge->ImageOffset = 0; ptcsge->ImageSize = cpu_to_le32(sz); sgeoffset = sizeof(FWUpload_t) - sizeof(SGE_MPI_UNION) + sizeof(FWUploadTCSGE_t); - for (ii = 0; ii < (num_frags-1); ii++) { - mpt_add_sge(&request[sgeoffset], MPT_SGE_FLAGS_SIMPLE_ELEMENT | - MPT_SGE_FLAGS_ADDRESSING | MPT_TRANSFER_IOC_TO_HOST | - (u32) ioc->cached_fw[ii]->size, ioc->cached_fw[ii]->fw_dma); - - sgeoffset += sizeof(u32) + sizeof(dma_addr_t); - } - - mpt_add_sge(&request[sgeoffset], - MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size, - ioc->cached_fw[ii]->fw_dma); + flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ | sz; + mpt_add_sge(&request[sgeoffset], flagsLength, ioc->cached_fw_dma); sgeoffset += sizeof(u32) + sizeof(dma_addr_t); - - dprintk((MYIOC_s_INFO_FMT "Sending FW Upload (req @ %p) size %d \n", - ioc->name, prequest, sgeoffset)); + dinitprintk((KERN_WARNING MYNAM "Sending FW Upload (req @ %p) sgeoffset=%d \n", + prequest, sgeoffset)); + DBG_DUMP_FW_REQUEST_FRAME(prequest) ii = mpt_handshake_req_reply_wait(ioc, sgeoffset, (u32*)prequest, reply_sz, (u16*)preply, 65 /*seconds*/, sleepFlag); + dinitprintk((KERN_WARNING MYNAM "FW Upload completed rc=%x \n", ii)); + cmdStatus = -EFAULT; if (ii == 0) { /* Handshake transfer was complete and successful. @@ -2857,7 +2728,7 @@ cmdStatus = 0; } } - ddlprintk((MYIOC_s_INFO_FMT ": do_upload status %d \n", + dinitprintk((MYIOC_s_INFO_FMT ": do_upload status %d \n", ioc->name, cmdStatus)); /* Check to see if we have a copy of this image in @@ -2877,8 +2748,7 @@ ddlprintk((MYIOC_s_INFO_FMT ": do_upload freeing %s image \n", ioc->name, cmdStatus ? "incomplete" : "duplicate")); - mpt_free_fw_memory(ioc, NULL); - ioc->cached_fw = NULL; + mpt_free_fw_memory(ioc); } return cmdStatus; @@ -2901,241 +2771,141 @@ static int mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) { - MpiFwHeader_t *FwHdr; - MpiExtImageHeader_t *ExtHdr; - fw_image_t **pCached; - int fw_sz; + MpiFwHeader_t *pFwHeader; + MpiExtImageHeader_t *pExtImage; + u32 fwSize; u32 diag0val; #ifdef MPT_DEBUG u32 diag1val = 0; #endif int count = 0; - u32 *ptru32; + u32 *ptrFw; u32 diagRwData; u32 nextImage; u32 ext_offset; u32 load_addr; - int max_idx, fw_idx, ext_idx; - int left_u32s; - - ddlprintk((MYIOC_s_INFO_FMT "DbGb0: downloadboot entered.\n", - ioc->name)); -#ifdef MPT_DEBUG - diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - if (ioc->alt_ioc) - diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); - ddlprintk((MYIOC_s_INFO_FMT "DbGb1: diag0=%08x, diag1=%08x\n", - ioc->name, diag0val, diag1val)); -#endif + u32 ioc_state; + int fw_idx; + int r; - ddlprintk((MYIOC_s_INFO_FMT "fw size 0x%x, ioc FW Ptr %p\n", + ddlprintk((MYIOC_s_INFO_FMT "downloadboot: fw size 0x%x, ioc FW Ptr %p\n", ioc->name, ioc->facts.FWImageSize, ioc->cached_fw)); - if (ioc->alt_ioc) - ddlprintk((MYIOC_s_INFO_FMT "alt ioc FW Ptr %p\n", - ioc->name, ioc->alt_ioc->cached_fw)); /* Get dma_addr and data transfer size. */ - if ((fw_sz = ioc->facts.FWImageSize) == 0) + if ( ioc->facts.FWImageSize == 0 ) return -1; /* Get the DMA from ioc or ioc->alt_ioc */ - if (ioc->cached_fw != NULL) - pCached = (fw_image_t **)ioc->cached_fw; - else if (ioc->alt_ioc && (ioc->alt_ioc->cached_fw != NULL)) - pCached = (fw_image_t **)ioc->alt_ioc->cached_fw; - - ddlprintk((MYIOC_s_INFO_FMT "DbGb2: FW Image @ %p\n", - ioc->name, pCached)); - if (!pCached) + if (ioc->cached_fw == NULL) return -2; - /* Write magic sequence to WriteSequence register - * until enter diagnostic mode - */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - while ((diag0val & MPI_DIAG_DRWE) == 0) { - CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); - CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); - CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); - CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); - CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); - CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + diag0val |= (MPI_DIAG_PREVENT_IOC_BOOT | MPI_DIAG_DISABLE_ARM); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); - /* wait 100 msec */ + /* wait 100 msec */ + if (sleepFlag == CAN_SLEEP) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100 * HZ / 1000); + } else { + mdelay (100); + } + + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_RESET_ADAPTER); + + for (count = 0; count < 30; count ++) { + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + if (!(diag0val & MPI_DIAG_RESET_ADAPTER)) { + ddlprintk((MYIOC_s_INFO_FMT "RESET_ADAPTER cleared, count=%d\n", + ioc->name, count)); + break; + } + /* wait 1 sec */ if (sleepFlag == CAN_SLEEP) { set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(100 * HZ / 1000); + schedule_timeout(HZ); } else { - mdelay (100); - } - - count++; - if (count > 20) { - printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n", - ioc->name, diag0val); - return -EFAULT; - + mdelay (1000); } - - diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); -#ifdef MPT_DEBUG - if (ioc->alt_ioc) - diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); - ddlprintk((MYIOC_s_INFO_FMT "DbGb3: diag0=%08x, diag1=%08x\n", - ioc->name, diag0val, diag1val)); -#endif - ddlprintk((MYIOC_s_INFO_FMT "Wrote magic DiagWriteEn sequence (%x)\n", - ioc->name, diag0val)); } - /* Set the DiagRwEn and Disable ARM bits */ - diag0val |= (MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM); - CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + if ( count == 30 ) { + ddlprintk((MYIOC_s_INFO_FMT "downloadboot failed! Unable to RESET_ADAPTER diag0val=%x\n", + ioc->name, diag0val)); + return -3; + } -#ifdef MPT_DEBUG - if (ioc->alt_ioc) - diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); - ddlprintk((MYIOC_s_INFO_FMT "DbGb3: diag0=%08x, diag1=%08x\n", - ioc->name, diag0val, diag1val)); -#endif + /* Set the DiagRwEn and Disable ARM bits */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, (diag0val | MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM)); - /* max_idx = 1 + maximum valid buffer index - */ - max_idx = 0; - while (pCached[max_idx]) - max_idx++; - - fw_idx = 0; - FwHdr = (MpiFwHeader_t *) pCached[fw_idx]->fw; - ptru32 = (u32 *) FwHdr; - count = (FwHdr->ImageSize + 3)/4; - nextImage = FwHdr->NextImageHeaderOffset; + pFwHeader = (MpiFwHeader_t *) ioc->cached_fw; + fwSize = (pFwHeader->ImageSize + 3)/4; + ptrFw = (u32 *) pFwHeader; /* Write the LoadStartAddress to the DiagRw Address Register * using Programmed IO */ - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, FwHdr->LoadStartAddress); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, pFwHeader->LoadStartAddress); ddlprintk((MYIOC_s_INFO_FMT "LoadStart addr written 0x%x \n", - ioc->name, FwHdr->LoadStartAddress)); + ioc->name, pFwHeader->LoadStartAddress)); - ddlprintk((MYIOC_s_INFO_FMT "Write FW Image: 0x%x u32's @ %p\n", - ioc->name, count, ptru32)); - left_u32s = pCached[fw_idx]->size/4; - while (count--) { - if (left_u32s == 0) { - fw_idx++; - if (fw_idx >= max_idx) { - /* FIXME - ERROR CASE - */ - ; - } - ptru32 = (u32 *) pCached[fw_idx]->fw; - left_u32s = pCached[fw_idx]->size / 4; - } - left_u32s--; - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32); - ptru32++; + ddlprintk((MYIOC_s_INFO_FMT "Write FW Image: 0x%x bytes @ %p\n", + ioc->name, fwSize*4, ptrFw)); + while (fwSize--) { + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptrFw++); } - /* left_u32s, fw_idx and ptru32 are all valid - */ + nextImage = pFwHeader->NextImageHeaderOffset; while (nextImage) { - ext_idx = 0; - ext_offset = nextImage; - while (ext_offset > pCached[ext_idx]->size) { - ext_idx++; - if (ext_idx >= max_idx) { - /* FIXME - ERROR CASE - */ - ; - } - ext_offset -= pCached[ext_idx]->size; - } - ptru32 = (u32 *) ((char *)pCached[ext_idx]->fw + ext_offset); - left_u32s = pCached[ext_idx]->size - ext_offset; - - if ((left_u32s * 4) >= sizeof(MpiExtImageHeader_t)) { - ExtHdr = (MpiExtImageHeader_t *) ptru32; - count = (ExtHdr->ImageSize + 3 )/4; - nextImage = ExtHdr->NextImageHeaderOffset; - load_addr = ExtHdr->LoadStartAddress; - } else { - u32 * ptmp = (u32 *)pCached[ext_idx+1]->fw; - - switch (left_u32s) { - case 5: - count = *(ptru32 + 2); - nextImage = *(ptru32 + 3); - load_addr = *(ptru32 + 4); - break; - case 4: - count = *(ptru32 + 2); - nextImage = *(ptru32 + 3); - load_addr = *ptmp; - break; - case 3: - count = *(ptru32 + 2); - nextImage = *ptmp; - load_addr = *(ptmp + 1); - break; - case 2: - count = *ptmp; - nextImage = *(ptmp + 1); - load_addr = *(ptmp + 2); - break; - - case 1: - count = *(ptmp + 1); - nextImage = *(ptmp + 2); - load_addr = *(ptmp + 3); - break; + pExtImage = (MpiExtImageHeader_t *) ((char *)pFwHeader + nextImage); - default: - count = 0; - nextImage = 0; - load_addr = 0; - /* FIXME - ERROR CASE - */ - ; + load_addr = pExtImage->LoadStartAddress; - } - count = (count +3)/4; - } + fwSize = (pExtImage->ImageSize + 3) >> 2; + ptrFw = (u32 *)pExtImage; - ddlprintk((MYIOC_s_INFO_FMT "Write Ext Image: 0x%x u32's @ %p\n", - ioc->name, count, ptru32)); + ddlprintk((MYIOC_s_INFO_FMT "Write Ext Image: 0x%x bytes @ %p load_addr=%x\n", + ioc->name, fwSize*4, ptrFw, load_addr)); CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, load_addr); - while (count--) { - if (left_u32s == 0) { - fw_idx++; - if (fw_idx >= max_idx) { - /* FIXME - ERROR CASE - */ - ; - } - ptru32 = (u32 *) pCached[fw_idx]->fw; - left_u32s = pCached[fw_idx]->size / 4; - } - left_u32s--; - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32); - ptru32++; + while (fwSize--) { + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptrFw++); } + nextImage = pExtImage->NextImageHeaderOffset; } /* Write the IopResetVectorRegAddr */ - ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Addr! \n", ioc->name)); - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, FwHdr->IopResetRegAddr); + ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Addr=%x! \n", ioc->name, pFwHeader->IopResetRegAddr)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, pFwHeader->IopResetRegAddr); /* Write the IopResetVectorValue */ - ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Value! \n", ioc->name)); - CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, FwHdr->IopResetVectorValue); + ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Value=%x! \n", ioc->name, pFwHeader->IopResetVectorValue)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, pFwHeader->IopResetVectorValue); + + /* clear the PREVENT_IOC_BOOT bit */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, turning off PREVENT_IOC_BOOT\n", + ioc->name, diag0val)); + diag0val &= ~(MPI_DIAG_PREVENT_IOC_BOOT); + ddlprintk((MYIOC_s_INFO_FMT "downloadboot now diag0val=%x\n", + ioc->name, diag0val)); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); /* Clear the internal flash bad bit - autoincrementing register, * so must do two writes. @@ -3146,15 +2916,63 @@ CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData); - /* clear the RW enable and DISARM bits */ diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); - diag0val &= ~(MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE | MPI_DIAG_FLASH_BAD_SIG); + ddlprintk((MYIOC_s_INFO_FMT "downloadboot diag0val=%x, turning off DISABLE_ARM, RW_ENABLE, RESET_HISTORY\n", + ioc->name, diag0val)); + diag0val &= ~(MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE | MPI_DIAG_RESET_HISTORY); + ddlprintk((MYIOC_s_INFO_FMT "downloadboot now diag0val=%x\n", + ioc->name, diag0val)); CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + /* wait 100 msec */ + if (sleepFlag == CAN_SLEEP) { + ddlprintk((MYIOC_s_INFO_FMT "CAN_SLEEP 100 msec before reset the sequencer\n", ioc->name)); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100 * HZ / 1000); + } else { + ddlprintk((MYIOC_s_INFO_FMT "mdelay 100 msec before reset the sequencer\n", ioc->name)); + mdelay (100); + } + + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + if ( diag0val & (MPI_DIAG_FLASH_BAD_SIG | MPI_DIAG_DISABLE_ARM) ) { + ddlprintk((MYIOC_s_INFO_FMT "downloadboot failed, diag0val=%x FLASH_BAD_SIG | DISABLE_ARM on\n ", + ioc->name, diag0val)); + } /* Write 0xFF to reset the sequencer */ CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); - return 0; + for (count=0; countname, count, ioc_state)); +/* if ((r = GetIocFacts(ioc, sleepFlag, MPT_HOSTEVENT_IOC_BRINGUP)) != 0) { + if ((r = GetIocFacts(ioc, sleepFlag, MPT_HOSTEVENT_IOC_BRINGUP)) != 0) { + ddlprintk((MYIOC_s_INFO_FMT "GetIocFacts failed\n", + ioc->name)); + return -EFAULT; + } + } */ + /* wait 2 sec */ +/* if (sleepFlag == CAN_SLEEP) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(5000 * HZ / 1000); + } else { + mdelay (5000); + } */ + + return 0; + } + if (sleepFlag == CAN_SLEEP) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } else { + mdelay (10); + } + } + ddlprintk((MYIOC_s_INFO_FMT "downloadboot failed! IocState=%x\n", + ioc->name, ioc_state)); + return -EFAULT; } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -3190,7 +3008,7 @@ u32 ioc_state; int cnt = 0; - dprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name)); + dinitprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name)); if ((int)ioc->chip_type > (int)FC929) { /* Always issue a Msg Unit Reset first. This will clear some * SCSI bus hang conditions. @@ -3437,6 +3255,7 @@ /* Write magic sequence to WriteSequence register * Loop until in diagnostic mode */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); @@ -3516,10 +3335,10 @@ u32 state; int cntdn, count; - dprintk((KERN_WARNING MYNAM ": %s: Sending IOC reset(0x%02x)!\n", + drsprintk((KERN_WARNING MYNAM ": %s: Sending IOC reset(0x%02x)!\n", ioc->name, reset_type)); CHIPREG_WRITE32(&ioc->chip->Doorbell, reset_type<chip->IntStatus, 0); - if (!failcnt && (t = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0) + if (!failcnt && (t = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) failcnt++; if (!failcnt) { @@ -3819,7 +3638,7 @@ (req_as_bytes[(ii*4) + 3] << 24)); CHIPREG_WRITE32(&ioc->chip->Doorbell, word); - if ((t = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0) + if ((t = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) failcnt++; } @@ -3835,6 +3654,9 @@ if (!failcnt && (t = WaitForDoorbellReply(ioc, maxwait, sleepFlag)) < 0) failcnt++; + dhsprintk((MYIOC_s_INFO_FMT "HandShake reply count=%d%s\n", + ioc->name, t, failcnt ? " - MISSING DOORBELL REPLY!" : "")); + /* * Copy out the cached reply... */ @@ -3865,7 +3687,7 @@ { int cntdn; int count = 0; - u32 intstat; + u32 intstat=0; cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong; @@ -3889,13 +3711,13 @@ } if (cntdn) { - dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell ACK (cnt=%d)\n", + dprintk((MYIOC_s_INFO_FMT "WaitForDoorbell ACK (count=%d)\n", ioc->name, count)); return count; } - printk(MYIOC_s_ERR_FMT "Doorbell ACK timeout(%d)!\n", - ioc->name, (count+5)/HZ); + printk(MYIOC_s_ERR_FMT "Doorbell ACK timeout (count=%d), IntStatus=%x!\n", + ioc->name, count, intstat); return -1; } @@ -3916,7 +3738,7 @@ { int cntdn; int count = 0; - u32 intstat; + u32 intstat=0; cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong; if (sleepFlag == CAN_SLEEP) { @@ -3939,13 +3761,13 @@ } if (cntdn) { - dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell INT (cnt=%d)\n", - ioc->name, count)); + dprintk((MYIOC_s_INFO_FMT "WaitForDoorbell INT (cnt=%d) howlong=%d\n", + ioc->name, count, howlong)); return count; } - printk(MYIOC_s_ERR_FMT "Doorbell INT timeout(%d)!\n", - ioc->name, (count+5)/HZ); + printk(MYIOC_s_ERR_FMT "Doorbell INT timeout (count=%d), IntStatus=%x!\n", + ioc->name, count, intstat); return -1; } @@ -3983,7 +3805,7 @@ } else { hs_reply[u16cnt++] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); - if ((t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0) + if ((t = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0) failcnt++; else { hs_reply[u16cnt++] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); @@ -3991,8 +3813,8 @@ } } - dhsprintk((MYIOC_s_INFO_FMT "First handshake reply word=%08x%s\n", - ioc->name, le32_to_cpu(*(u32 *)hs_reply), + dhsprintk((MYIOC_s_INFO_FMT "WaitCnt=%d First handshake reply word=%08x%s\n", + ioc->name, t, le32_to_cpu(*(u32 *)hs_reply), failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); /* @@ -4000,7 +3822,7 @@ * reply 16 bits at a time. */ for (u16cnt=2; !failcnt && u16cnt < (2 * mptReply->MsgLength); u16cnt++) { - if ((t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0) + if ((t = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0) failcnt++; hword = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); /* don't overflow our IOC hs_reply[] buffer! */ @@ -4009,7 +3831,7 @@ CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); } - if (!failcnt && (t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0) + if (!failcnt && (t = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0) failcnt++; CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); @@ -4030,8 +3852,8 @@ dmfprintk((MYIOC_s_INFO_FMT "Got Handshake reply:\n", ioc->name)); DBG_DUMP_REPLY_FRAME(mptReply) - dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell REPLY (sz=%d)\n", - ioc->name, u16cnt/2)); + dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell REPLY WaitCnt=%d (sz=%d)\n", + ioc->name, t, u16cnt/2)); return u16cnt/2; } @@ -4374,6 +4196,8 @@ pPP0->Capabilities = le32_to_cpu(pPP0->Capabilities); pPP0->PhysicalInterface = le32_to_cpu(pPP0->PhysicalInterface); + if ( (pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_QAS) == 0 ) + ioc->spi_data.noQas |= MPT_TARGET_NO_NEGO_QAS; ioc->spi_data.maxBusWidth = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_WIDE ? 1 : 0; data = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK; if (data) { @@ -4517,10 +4341,11 @@ * -EFAULT if read of config page header fails or data pointer not NULL * -ENOMEM if pci_alloc failed */ -static int +int mpt_findImVolumes(MPT_ADAPTER *ioc) { IOCPage2_t *pIoc2; + u8 *mem; ConfigPageIoc2RaidVol_t *pIocRv; dma_addr_t ioc2_dma; CONFIGPARMS cfg; @@ -4531,9 +4356,6 @@ u8 nVols, nPhys; u8 vid, vbus, vioc; - if (ioc->spi_data.pIocPg3) - return -EFAULT; - /* Read IOCP2 header then the page. */ header.PageVersion = 0; @@ -4562,11 +4384,22 @@ if (mpt_config(ioc, &cfg) != 0) goto done_and_free; + if ( (mem = (u8 *)ioc->spi_data.pIocPg2) == NULL ) { + mem = kmalloc(iocpage2sz, GFP_ATOMIC); + if (mem) { + ioc->spi_data.pIocPg2 = (IOCPage2_t *) mem; + } else { + goto done_and_free; + } + } + memcpy(mem, (u8 *)pIoc2, iocpage2sz); + /* Identify RAID Volume Id's */ nVols = pIoc2->NumActiveVolumes; if ( nVols == 0) { - /* No RAID Volumes. Done. + /* No RAID Volume. */ + goto done_and_free; } else { /* At least 1 RAID Volume */ @@ -4591,7 +4424,7 @@ /* Identify Hidden Physical Disk Id's */ nPhys = pIoc2->NumActivePhysDisks; if (nPhys == 0) { - /* No physical disks. Done. + /* No physical disks. */ } else { mpt_read_ioc_pg_3(ioc); @@ -4883,7 +4716,7 @@ MPT_FRAME_HDR *mf; unsigned long flags; int ii, rc; - int flagsLength; + u32 flagsLength; int in_isr; /* (Bugzilla:fibrebugs, #513) @@ -4910,9 +4743,8 @@ pReq->Reserved = 0; pReq->ChainOffset = 0; pReq->Function = MPI_FUNCTION_CONFIG; - pReq->Reserved1[0] = 0; - pReq->Reserved1[1] = 0; - pReq->Reserved1[2] = 0; + pReq->ExtPageLength = 0; + pReq->ExtPageType = 0; pReq->MsgFlags = 0; for (ii=0; ii < 8; ii++) pReq->Reserved2[ii] = 0; @@ -4971,6 +4803,114 @@ } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_toolbox - Generic function to issue toolbox message + * @ioc - Pointer to an adapter structure + * @cfg - Pointer to a toolbox structure. Struct contains + * action, page address, direction, physical address + * and pointer to a configuration page header + * Page header is updated. + * + * Returns 0 for success + * -EPERM if not allowed due to ISR context + * -EAGAIN if no msg frames currently available + * -EFAULT for non-successful reply or no reply (timeout) + */ +int +mpt_toolbox(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg) +{ + ToolboxIstwiReadWriteRequest_t *pReq; + u32 *toolbox_alloc; + dma_addr_t toolbox_dma; + MPT_FRAME_HDR *mf; + unsigned long flags; + int ii, rc; + u32 flagsLength; + int in_isr; + + /* (Bugzilla:fibrebugs, #513) + * Bug fix (part 1)! 20010905 -sralston + * Prevent calling wait_event() (below), if caller happens + * to be in ISR context, because that is fatal! + */ + in_isr = in_interrupt(); + if (in_isr) { + dcprintk((MYIOC_s_WARN_FMT "toobox request not allowed in ISR context!\n", + ioc->name)); + return -EPERM; + } + + /* Get and Populate a free Frame + */ + if ((mf = mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) { + dcprintk((MYIOC_s_WARN_FMT "mpt_toolbox: no msg frames!\n", + ioc->name)); + return -EAGAIN; + } + pReq = (ToolboxIstwiReadWriteRequest_t *)mf; + pReq->Tool = pCfg->action; + pReq->Reserved = 0; + pReq->ChainOffset = 0; + pReq->Function = MPI_FUNCTION_TOOLBOX; + pReq->Reserved1 = 0; + pReq->Reserved2 = 0; + pReq->MsgFlags = 0; + pReq->Flags = pCfg->dir; + pReq->BusNum = 0; + pReq->Reserved3 = 0; + pReq->NumAddressBytes = 0x01; + pReq->Reserved4 = 0; + pReq->DataLength = 0x04; + pReq->DeviceAddr = 0xB0; + pReq->Addr1 = 0; + pReq->Addr2 = 0; + pReq->Addr3 = 0; + pReq->Reserved5 = 0; + + /* Add a SGE to the config request. + */ + + flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ | 4; + + mpt_add_sge((char *)&pReq->SGL, flagsLength, pCfg->physAddr); + + dcprintk((MYIOC_s_INFO_FMT "Sending Toolbox request, Tool=%x\n", + ioc->name, pReq->Tool)); + + /* Append pCfg pointer to end of mf + */ + *((void **) (((u8 *) mf) + (ioc->req_sz - sizeof(void *)))) = (void *) pCfg; + + /* Initalize the timer + */ + init_timer(&pCfg->timer); + pCfg->timer.data = (unsigned long) ioc; + pCfg->timer.function = mpt_timer_expired; + pCfg->wait_done = 0; + + /* Set the timer; ensure 10 second minimum */ + if (pCfg->timeout < 10) + pCfg->timer.expires = jiffies + HZ*10; + else + pCfg->timer.expires = jiffies + HZ*pCfg->timeout; + + /* Add to end of Q, set timer and then issue this command */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_ADD_TAIL(&ioc->configQ.head, &pCfg->linkage, Q_ITEM); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + add_timer(&pCfg->timer); + mpt_put_msg_frame(mpt_base_index, ioc->id, mf); + wait_event(mpt_waitq, pCfg->wait_done); + + /* mf has been freed - do not access */ + + rc = pCfg->status; + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * mpt_timer_expired - Call back for timer process. * Used only internal config functionality. @@ -5753,109 +5693,11 @@ "FCP Initiator", "FCP Target", "LAN", "MPI Message Layer", "FC Link", "Context Manager", "Invalid Field Offset", "State Change Info" }; - char *desc = "unknown"; u8 subcl = (log_info >> 24) & 0x7; u32 SubCl = log_info & 0x27000000; - switch(log_info) { -/* FCP Initiator */ - case MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME: - desc = "Received an out of order frame - unsupported"; - break; - case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_START_OF_FRAME: - desc = "Bad start of frame primative"; - break; - case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_END_OF_FRAME: - desc = "Bad end of frame primative"; - break; - case MPI_IOCLOGINFO_FC_INIT_ERROR_OVER_RUN: - desc = "Receiver hardware detected overrun"; - break; - case MPI_IOCLOGINFO_FC_INIT_ERROR_RX_OTHER: - desc = "Other errors caught by IOC which require retries"; - break; - case MPI_IOCLOGINFO_FC_INIT_ERROR_SUBPROC_DEAD: - desc = "Main processor could not initialize sub-processor"; - break; -/* FC Target */ - case MPI_IOCLOGINFO_FC_TARGET_NO_PDISC: - desc = "Not sent because we are waiting for a PDISC from the initiator"; - break; - case MPI_IOCLOGINFO_FC_TARGET_NO_LOGIN: - desc = "Not sent because we are not logged in to the remote node"; - break; - case MPI_IOCLOGINFO_FC_TARGET_DOAR_KILLED_BY_LIP: - desc = "Data Out, Auto Response, not sent due to a LIP"; - break; - case MPI_IOCLOGINFO_FC_TARGET_DIAR_KILLED_BY_LIP: - desc = "Data In, Auto Response, not sent due to a LIP"; - break; - case MPI_IOCLOGINFO_FC_TARGET_DIAR_MISSING_DATA: - desc = "Data In, Auto Response, missing data frames"; - break; - case MPI_IOCLOGINFO_FC_TARGET_DONR_KILLED_BY_LIP: - desc = "Data Out, No Response, not sent due to a LIP"; - break; - case MPI_IOCLOGINFO_FC_TARGET_WRSP_KILLED_BY_LIP: - desc = "Auto-response after a write not sent due to a LIP"; - break; - case MPI_IOCLOGINFO_FC_TARGET_DINR_KILLED_BY_LIP: - desc = "Data In, No Response, not completed due to a LIP"; - break; - case MPI_IOCLOGINFO_FC_TARGET_DINR_MISSING_DATA: - desc = "Data In, No Response, missing data frames"; - break; - case MPI_IOCLOGINFO_FC_TARGET_MRSP_KILLED_BY_LIP: - desc = "Manual Response not sent due to a LIP"; - break; - case MPI_IOCLOGINFO_FC_TARGET_NO_CLASS_3: - desc = "Not sent because remote node does not support Class 3"; - break; - case MPI_IOCLOGINFO_FC_TARGET_LOGIN_NOT_VALID: - desc = "Not sent because login to remote node not validated"; - break; - case MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND: - desc = "Cleared from the outbound queue after a logout"; - break; - case MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN: - desc = "Cleared waiting for data after a logout"; - break; -/* LAN */ - case MPI_IOCLOGINFO_FC_LAN_TRANS_SGL_MISSING: - desc = "Transaction Context Sgl Missing"; - break; - case MPI_IOCLOGINFO_FC_LAN_TRANS_WRONG_PLACE: - desc = "Transaction Context found before an EOB"; - break; - case MPI_IOCLOGINFO_FC_LAN_TRANS_RES_BITS_SET: - desc = "Transaction Context value has reserved bits set"; - break; - case MPI_IOCLOGINFO_FC_LAN_WRONG_SGL_FLAG: - desc = "Invalid SGL Flags"; - break; -/* FC Link */ - case MPI_IOCLOGINFO_FC_LINK_LOOP_INIT_TIMEOUT: - desc = "Loop initialization timed out"; - break; - case MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED: - desc = "Another system controller already initialized the loop"; - break; - case MPI_IOCLOGINFO_FC_LINK_LINK_NOT_ESTABLISHED: - desc = "Not synchronized to signal or still negotiating (possible cable problem)"; - break; - case MPI_IOCLOGINFO_FC_LINK_CRC_ERROR: - desc = "CRC check detected error on received frame"; - break; - } - printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): SubCl={%s}", ioc->name, log_info, subcl_str[subcl]); - if (SubCl == MPI_IOCLOGINFO_FC_INVALID_FIELD_BYTE_OFFSET) - printk(", byte_offset=%d\n", log_info & MPI_IOCLOGINFO_FC_INVALID_FIELD_MAX_OFFSET); - else if (SubCl == MPI_IOCLOGINFO_FC_STATE_CHANGE) - printk("\n"); /* StateChg in LogInfo & 0x00FFFFFF, above */ - else - printk("\n" KERN_INFO " %s\n", desc); } /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -5984,6 +5826,8 @@ EXPORT_SYMBOL(mpt_stm_index); EXPORT_SYMBOL(mpt_HardResetHandler); EXPORT_SYMBOL(mpt_config); +EXPORT_SYMBOL(mpt_toolbox); +EXPORT_SYMBOL(mpt_findImVolumes); EXPORT_SYMBOL(mpt_read_ioc_pg_3); EXPORT_SYMBOL(mpt_alloc_fw_memory); EXPORT_SYMBOL(mpt_free_fw_memory); @@ -6055,8 +5899,9 @@ { MPT_ADAPTER *this; struct pci_dev *pdev; + int ii; - dprintk((KERN_INFO MYNAM ": fusion_exit() called!\n")); + dexitprintk((KERN_INFO MYNAM ": fusion_exit() called!\n")); /* Whups? 20010120 -sralston * Moved this *above* removal of all MptAdapters! @@ -6064,23 +5909,40 @@ #ifdef CONFIG_PROC_FS (void) procmpt_destroy(); #endif + /* Find onboard HBAs */ + for (ii=0; ii < MPT_MAX_ADAPTERS; ii++) { + if ( (this = mpt_adapters[ii]) ) { + if (this->cached_fw != NULL) { + if (this->alt_ioc) { + pdev = (struct pci_dev *)this->alt_ioc->pcidev; + mpt_sync_irq(pdev->irq); + + dexitprintk((MYIOC_s_INFO_FMT "Calling mpt_adapter_dispose for alt_ioc %d\n", this->name, this->alt_ioc->id)); + + Q_DEL_ITEM(this->alt_ioc); + mpt_adapter_dispose(this->alt_ioc); + } + pdev = (struct pci_dev *)this->pcidev; + mpt_sync_irq(pdev->irq); + + dexitprintk((MYIOC_s_INFO_FMT "Calling mpt_adapter_dispose for on-board ioc %d\n", this->name, ii)); + + Q_DEL_ITEM(this); + mpt_adapter_dispose(this); + } + } + } + while (! Q_IS_EMPTY(&MptAdapters)) { this = MptAdapters.head; - /* Disable interrupts! */ - CHIPREG_WRITE32(&this->chip->IntMask, 0xFFFFFFFF); - this->active = 0; pdev = (struct pci_dev *)this->pcidev; mpt_sync_irq(pdev->irq); - /* Clear any lingering interrupt */ - CHIPREG_WRITE32(&this->chip->IntStatus, 0); - - CHIPREG_READ32(&this->chip->IntStatus); - + dexitprintk((MYIOC_s_INFO_FMT "Calling mpt_adapter_dispose for ioc %d\n", this->name,this->id)); Q_DEL_ITEM(this); mpt_adapter_dispose(this); } diff -urN linux-2.4.26/drivers/message/fusion/mptbase.h linux-2.4.27/drivers/message/fusion/mptbase.h --- linux-2.4.26/drivers/message/fusion/mptbase.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/mptbase.h 2004-08-07 16:26:04.922357790 -0700 @@ -8,7 +8,7 @@ * Credits: * (see mptbase.c) * - * Copyright (c) 1999-2002 LSI Logic Corporation + * Copyright (c) 1999-2004 LSI Logic Corporation * Originally By: Steven J. Ralston * (mailto:sjralston1@netscape.net) * (mailto:mpt_linux_developer@lsil.com) @@ -68,6 +68,7 @@ #include "lsi/mpi_fc.h" /* Fibre Channel (lowlevel) support */ #include "lsi/mpi_targ.h" /* SCSI/FCP Target protcol support */ +#include "lsi/mpi_tool.h" /* Tools support */ #include "lsi/fc_log.h" /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -77,11 +78,11 @@ #endif #ifndef COPYRIGHT -#define COPYRIGHT "Copyright (c) 1999-2003 " MODULEAUTHOR +#define COPYRIGHT "Copyright (c) 1999-2004 " MODULEAUTHOR #endif -#define MPT_LINUX_VERSION_COMMON "2.05.11.03" -#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.05.11.03" +#define MPT_LINUX_VERSION_COMMON "2.05.16" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.05.16" #define WHAT_MAGIC_STRING "@" "(" "#" ")" #define show_mptmod_ver(s,ver) \ @@ -152,6 +153,9 @@ #define MPT_NARROW 0 #define MPT_WIDE 1 +#define C0_1030 0x08 +#define XL_929 0x01 + #ifdef __KERNEL__ /* { */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ @@ -527,6 +531,7 @@ typedef struct _ScsiCfgData { u32 PortFlags; int *nvram; /* table of device NVRAM values */ + IOCPage2_t *pIocPg2; /* table of Raid Volumes */ IOCPage3_t *pIocPg3; /* table of physical disks */ IOCPage4_t *pIocPg4; /* SEP devices addressing */ dma_addr_t IocPg4_dma; /* Phys Addr of IOCPage4 data */ @@ -548,13 +553,6 @@ u8 rsvd[1]; } ScsiCfgData; -typedef struct _fw_image { - char *fw; - dma_addr_t fw_dma; - u32 size; - u32 rsvd; -} fw_image_t; - /* * Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS */ @@ -620,9 +618,9 @@ int timeout_maxcnt; #endif struct _mpt_ioctl_events *events; /* pointer to event log */ - fw_image_t **cached_fw; /* Pointer to FW SG List */ + u8 *cached_fw; /* Pointer to FW */ + dma_addr_t cached_fw_dma; Q_TRACKER configQ; /* linked list of config. requests */ - int num_fw_frags; /* Number of SGE in FW SG List */ int hs_reply_idx; #ifndef MFCNT u32 pad0; @@ -700,6 +698,33 @@ #define dprintk(x) #endif +#ifdef MPT_DEBUG_INIT +#define dinitprintk(x) printk x +#define DBG_DUMP_FW_REQUEST_FRAME(mfp) \ + { int i, n = 10; \ + u32 *m = (u32 *)(mfp); \ + printk(KERN_INFO " "); \ + for (i=0; iid])) + rc = -ERESTARTSYS; + } else { + rc = -EPERM; + } +#else if (nonblock) { if (down_trylock(&mptctl_syscall_sem_ioc[ioc->id])) rc = -EAGAIN; @@ -216,6 +224,7 @@ if (down_interruptible(&mptctl_syscall_sem_ioc[ioc->id])) rc = -ERESTARTSYS; } +#endif dctlprintk((KERN_INFO MYNAM "::mptctl_syscall_down return %d\n", rc)); return rc; } @@ -1357,16 +1366,19 @@ MPT_ADAPTER *ioc; struct Scsi_Host *sh; MPT_SCSI_HOST *hd; + VirtDevice *vdev; char *pmem; int *pdata; + IOCPage2_t *pIoc2; + IOCPage3_t *pIoc3; int iocnum; int numDevices = 0; unsigned int max_id; - int ii, jj, indexed_lun, lun_index; + int id, jj, indexed_lun, lun_index; u32 lun; int maxWordsLeft; int numBytes; - u8 port; + u8 port, devType, bus_id; dctlprintk(("mptctl_gettargetinfo called.\n")); if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_targetinfo))) { @@ -1433,29 +1445,63 @@ * sh->max_id = maximum target ID + 1 */ if (hd && hd->Targets) { - ii = 0; - while (ii <= max_id) { - if (hd->Targets[ii]) { + mpt_findImVolumes(ioc); + pIoc2 = ioc->spi_data.pIocPg2; + for ( id = 0; id <= max_id; ) { + if ( pIoc2 && pIoc2->NumActiveVolumes ) { + if ( id == pIoc2->RaidVolume[0].VolumeID ) { + if (maxWordsLeft <= 0) { + printk(KERN_ERR "mptctl_gettargetinfo - " + "buffer is full but volume is available on ioc %d\n, numDevices=%d", iocnum, numDevices); + goto data_space_full; + } + if ( ( pIoc2->RaidVolume[0].Flags & MPI_IOCPAGE2_FLAG_VOLUME_INACTIVE ) == 0 ) + devType = 0x80; + else + devType = 0xC0; + bus_id = pIoc2->RaidVolume[0].VolumeBus; + numDevices++; + *pdata = ( (devType << 24) | (bus_id << 8) | id ); + dctlprintk((KERN_ERR "mptctl_gettargetinfo - " + "volume ioc=%d target=%x numDevices=%d pdata=%p\n", iocnum, *pdata, numDevices, pdata)); + pdata++; + --maxWordsLeft; + goto next_id; + } else { + pIoc3 = ioc->spi_data.pIocPg3; + for ( jj = 0; jj < pIoc3->NumPhysDisks; jj++ ) { + if ( pIoc3->PhysDisk[jj].PhysDiskID == id ) + goto next_id; + } + } + } + if ( (vdev = hd->Targets[id]) ) { for (jj = 0; jj <= MPT_LAST_LUN; jj++) { lun_index = (jj >> 5); indexed_lun = (jj % 32); lun = (1 << indexed_lun); - if (hd->Targets[ii]->luns[lun_index] & lun) { + if (vdev->luns[lun_index] & lun) { + if (maxWordsLeft <= 0) { + printk(KERN_ERR "mptctl_gettargetinfo - " + "buffer is full but more targets are available on ioc %d numDevices=%d\n", iocnum, numDevices); + goto data_space_full; + } + bus_id = vdev->bus_id; numDevices++; - *pdata = (jj << 16) | ii; - --maxWordsLeft; - + *pdata = ( (jj << 16) | (bus_id << 8) | id ); + dctlprintk((KERN_ERR "mptctl_gettargetinfo - " + "target ioc=%d target=%x numDevices=%d pdata=%p\n", iocnum, *pdata, numDevices, pdata)); pdata++; - - if (maxWordsLeft <= 0) - break; + --maxWordsLeft; } } } - ii++; +next_id: + id++; } } } +data_space_full: karg.numDevices = numDevices; /* Copy part of the data from kernel memory to user memory @@ -1693,12 +1739,11 @@ struct mpt_ioctl_replace_fw *uarg = (struct mpt_ioctl_replace_fw *) arg; struct mpt_ioctl_replace_fw karg; MPT_ADAPTER *ioc; - fw_image_t **fwmem = NULL; + u8 *fwmem = NULL; int iocnum; int newFwSize; - int num_frags, alloc_sz; + int alloc_sz; int ii; - u32 offset; dctlprintk(("mptctl_replace_fw called.\n")); if (copy_from_user(&karg, uarg, sizeof(struct mpt_ioctl_replace_fw))) { @@ -1715,51 +1760,39 @@ return -ENODEV; } - /* If not caching FW, return 0 + /* If caching FW, Free the old FW image */ - if ((ioc->cached_fw == NULL) && (ioc->alt_ioc) && (ioc->alt_ioc->cached_fw == NULL)) + if (ioc->cached_fw) { + mpt_free_fw_memory(ioc); + } else return 0; /* Allocate memory for the new FW image */ newFwSize = karg.newImageSize; - fwmem = mpt_alloc_fw_memory(ioc, newFwSize, &num_frags, &alloc_sz); - if (fwmem == NULL) - return -ENOMEM; - - offset = 0; - for (ii = 0; ii < num_frags; ii++) { - /* Copy the data from user memory to kernel space - */ - if (copy_from_user(fwmem[ii]->fw, uarg->newImage + offset, fwmem[ii]->size)) { - printk(KERN_ERR "%s@%d::mptctl_replace_fw - " - "Unable to read in mpt_ioctl_replace_fw image @ %p\n", - __FILE__, __LINE__, (void*)uarg); - - mpt_free_fw_memory(ioc, fwmem); - return -EFAULT; - } - offset += fwmem[ii]->size; - } + if ( newFwSize & 0x01 ) + newFwSize += 1; + if ( newFwSize & 0x02 ) + newFwSize += 2; + mpt_alloc_fw_memory(ioc, newFwSize); + if (ioc->cached_fw == NULL) + return -ENOMEM; - /* Free the old FW image + /* Copy the data from user memory to kernel space */ - if (ioc->cached_fw) { - mpt_free_fw_memory(ioc, 0); - ioc->cached_fw = fwmem; - ioc->alloc_total += alloc_sz; - } else if ((ioc->alt_ioc) && (ioc->alt_ioc->cached_fw)) { - mpt_free_fw_memory(ioc->alt_ioc, 0); - ioc->alt_ioc->cached_fw = fwmem; - ioc->alt_ioc->alloc_total += alloc_sz; + if (copy_from_user(ioc->cached_fw, uarg->newImage, newFwSize)) { + printk(KERN_ERR "%s@%d::mptctl_replace_fw - " + "Unable to read in mpt_ioctl_replace_fw image @ %p\n", + __FILE__, __LINE__, (void*)uarg); + + mpt_free_fw_memory(ioc); + return -EFAULT; } /* Update IOCFactsReply */ ioc->facts.FWImageSize = newFwSize; - if (ioc->alt_ioc) - ioc->alt_ioc->facts.FWImageSize = newFwSize; return 0; } @@ -2520,6 +2553,20 @@ } } + cfg.pageAddr = 0; + cfg.action = MPI_TOOLBOX_ISTWI_READ_WRITE_TOOL; + cfg.dir = MPI_TB_ISTWI_FLAGS_READ; + cfg.timeout = 10; + pbuf = pci_alloc_consistent(ioc->pcidev, 4, &buf_dma); + if (pbuf) { + cfg.physAddr = buf_dma; + if ((mpt_toolbox(ioc, &cfg)) == 0) { + karg.rsvd = *(u32 *)pbuf; + } + pci_free_consistent(ioc->pcidev, 4, pbuf, buf_dma); + pbuf = NULL; + } + /* Copy the data from kernel memory to user memory */ if (copy_to_user((char *)arg, &karg, @@ -2900,8 +2947,7 @@ if (++where && err) goto out_fail; err = register_ioctl32_conversion(HP_GETHOSTINFO, compat_mptctl_ioctl); if (++where && err) goto out_fail; - err = register_ioctl32_conversion(HP_GETTARGETINFO, - compat_mptctl_ioctl); + err = register_ioctl32_conversion(HP_GETTARGETINFO, compat_mptctl_ioctl); if (++where && err) goto out_fail; #endif diff -urN linux-2.4.26/drivers/message/fusion/mptctl.h linux-2.4.27/drivers/message/fusion/mptctl.h --- linux-2.4.26/drivers/message/fusion/mptctl.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/mptctl.h 2004-08-07 16:26:04.925357913 -0700 @@ -15,7 +15,7 @@ * * (see also mptbase.c) * - * Copyright (c) 1999-2002 LSI Logic Corporation + * Copyright (c) 1999-2004 LSI Logic Corporation * Originally By: Steven J. Ralston * (mailto:sjralston1@netscape.net) * (mailto:mpt_linux_developer@lsil.com) diff -urN linux-2.4.26/drivers/message/fusion/mptlan.c linux-2.4.27/drivers/message/fusion/mptlan.c --- linux-2.4.26/drivers/message/fusion/mptlan.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/message/fusion/mptlan.c 2004-08-07 16:26:04.926357954 -0700 @@ -23,7 +23,7 @@ * * (see also mptbase.c) * - * Copyright (c) 2000-2002 LSI Logic Corporation + * Copyright (c) 2000-2004 LSI Logic Corporation * Originally By: Noah Romer * * $Id: mptlan.c,v 1.55 2003/05/07 14:08:32 pdelaney Exp $ diff -urN linux-2.4.26/drivers/message/fusion/mptscsih.c linux-2.4.27/drivers/message/fusion/mptscsih.c --- linux-2.4.26/drivers/message/fusion/mptscsih.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/mptscsih.c 2004-08-07 16:26:04.933358242 -0700 @@ -21,7 +21,7 @@ * * (see mptbase.c) * - * Copyright (c) 1999-2002 LSI Logic Corporation + * Copyright (c) 1999-2004 LSI Logic Corporation * Original author: Steven J. Ralston * (mailto:sjralston1@netscape.net) * (mailto:mpt_linux_developer@lsil.com) @@ -217,7 +217,6 @@ */ static int mpt_scsi_hosts = 0; -static atomic_t queue_depth; static int ScsiDoneCtx = -1; static int ScsiTaskCtx = -1; @@ -741,8 +740,6 @@ if (sc == NULL) { MPIHeader_t *hdr = (MPIHeader_t *)mf; - atomic_dec(&queue_depth); - /* Remark: writeSDP1 will use the ScsiDoneCtx * If a SCSI I/O cmd, device disabled by OS and * completion done. Cannot touch sc struct. Just free mem. @@ -757,8 +754,6 @@ dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n", ioc->name, mf, mr, sc, req_idx)); - atomic_dec(&queue_depth); - sc->result = DID_OK << 16; /* Set default reply as OK */ pScsiReq = (SCSIIORequest_t *) mf; pScsiReply = (SCSIIOReply_t *) mr; @@ -788,23 +783,21 @@ /* If regular Inquiry cmd - save inquiry data */ - if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) { + if (pScsiReq->CDB[0] == INQUIRY) { int dlen; dlen = le32_to_cpu(pScsiReq->DataLength); - if (dlen >= SCSI_STD_INQUIRY_BYTES) { - mptscsih_initTarget(hd, - sc->channel, - sc->target, - pScsiReq->LUN[1], - sc->buffer, - dlen); - } + mptscsih_initTarget(hd, + sc->channel, + sc->target, + pScsiReq->LUN[1], + sc->buffer, + dlen); } } else { u32 xfer_cnt; u16 status; - u8 scsi_state; + u8 scsi_state, scsi_status; status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; scsi_state = pScsiReply->SCSIState; @@ -820,14 +813,6 @@ if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) copy_sense_data(sc, hd, mf, pScsiReply); - /* - * Look for + dump FCP ResponseInfo[]! - */ - if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) { - dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n", - le32_to_cpu(pScsiReply->ResponseInfo))); - } - switch(status) { case MPI_IOCSTATUS_BUSY: /* 0x0002 */ /* CHECKME! @@ -877,35 +862,45 @@ mptscsih_no_negotiate(hd, sc->target); break; + case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ + sc->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | + (CHECK_CONDITION << 1); + sc->sense_buffer[0] = 0x70; + sc->sense_buffer[2] = NO_SENSE; + sc->sense_buffer[12] = 0; + sc->sense_buffer[13] = 0; + dprintk((KERN_NOTICE "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->target)); + break; + case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ /* - * YIKES! I just discovered that SCSI IO which - * returns check condition, SenseKey=05 (ILLEGAL REQUEST) - * and ASC/ASCQ=94/01 (LSI Logic RAID vendor specific), - * comes down this path! * Do upfront check for valid SenseData and give it * precedence! */ - sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus; - if (scsi_state == 0) { - ; - } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { + scsi_status = pScsiReply->SCSIStatus; + sc->result = (DID_OK << 16) | scsi_status; + xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); + if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { /* Have already saved the status and sense data */ ; - } else if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { - /* What to do? - */ - sc->result = DID_SOFT_ERROR << 16; - } - else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { - /* Not real sure here either... */ - sc->result = DID_RESET << 16; + } else { + if ( (xfer_cnt == 0) || (sc->underflow > xfer_cnt)) { + sc->result = DID_SOFT_ERROR << 16; + } + if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { + /* What to do? + */ + sc->result = DID_SOFT_ERROR << 16; + } + else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { + /* Not real sure here either... */ + sc->result = DID_RESET << 16; + } } /* Give report and update residual count. */ - xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); dprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow)); dprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt)); @@ -914,22 +909,15 @@ sc->resid = sc->request_bufflen - xfer_cnt; dprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid)); #endif - if((xfer_cnt == 0 ) || (sc->underflow > xfer_cnt)) { - sc->result = DID_SOFT_ERROR << 16; - } - /* Report Queue Full */ - if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL) + if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL) mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); /* If regular Inquiry cmd and some data was transferred, * save inquiry data */ - if ( pScsiReq->CDB[0] == INQUIRY - && !(pScsiReq->CDB[1] & 0x3) - && xfer_cnt >= SCSI_STD_INQUIRY_BYTES - ) { + if ( (pScsiReq->CDB[0] == INQUIRY) && xfer_cnt ) { mptscsih_initTarget(hd, sc->channel, sc->target, @@ -1006,11 +994,9 @@ /* If regular Inquiry cmd - save inquiry data */ xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); - if ( sc->result == (DID_OK << 16) - && pScsiReq->CDB[0] == INQUIRY - && !(pScsiReq->CDB[1] & 0x3) - && xfer_cnt >= SCSI_STD_INQUIRY_BYTES - ) { + if ( (sc->result == (DID_OK << 16)) + && xfer_cnt + && pScsiReq->CDB[0] == INQUIRY ) { mptscsih_initTarget(hd, sc->channel, sc->target, @@ -1037,7 +1023,6 @@ case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */ case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */ case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */ - case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */ default: /* @@ -1371,11 +1356,9 @@ #endif /* Search pendingQ, if found, - * delete from Q. If found, do not decrement - * queue_depth, command never posted. + * delete from Q. */ - if (mptscsih_search_pendingQ(hd, ii) == NULL) - atomic_dec(&queue_depth); + mptscsih_search_pendingQ(hd, ii); /* Null ScsiLookup index */ @@ -2429,7 +2412,6 @@ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ - static int max_qd = 1; #if 0 static int index_log[128]; static int index_ent = 0; @@ -2443,32 +2425,6 @@ #define ADD_INDEX_LOG(req_ent) do { } while(0) #endif -/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ -/* - * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W. - * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx) - * @id: IOC id number - * @mf: Pointer to message frame - * - * Handles the call to mptbase for posting request and queue depth - * tracking. - * - * Returns none. - */ -static inline void -mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf) -{ - /* Main banana... */ - atomic_inc(&queue_depth); - if (atomic_read(&queue_depth) > max_qd) { - max_qd = atomic_read(&queue_depth); - dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd)); - } - - mpt_put_msg_frame(context, id, mf); - - return; -} /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** @@ -2599,7 +2555,6 @@ /* * Write SCSI CDB into the message - * Should write from cmd_len up to 16, but skip for performance reasons. */ cmd_len = SCpnt->cmd_len; for (ii=0; ii < cmd_len; ii++) @@ -2640,17 +2595,6 @@ if (dvStatus || hd->ioc->spi_data.forceDv) { - /* Write SDP1 on this I/O to this target */ - if (dvStatus & MPT_SCSICFG_NEGOTIATE) { - mptscsih_writeSDP1(hd, 0, target, hd->negoNvram); - dvStatus &= ~MPT_SCSICFG_NEGOTIATE; - hd->ioc->spi_data.dvStatus[target] = dvStatus; - } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) { - mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO); - dvStatus &= ~MPT_SCSICFG_BLK_NEGO; - hd->ioc->spi_data.dvStatus[target] = dvStatus; - } - #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION if ((dvStatus & MPT_SCSICFG_NEED_DV) || (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) { @@ -2698,7 +2642,7 @@ #endif if (issueCmd) { - mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", hd->ioc->name, SCpnt, mf, my_idx)); } else { @@ -3073,8 +3017,8 @@ if (hd->resetPending) return FAILED; - printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p, numIOs=%d)\n", - hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)\n", + hd->ioc->name, SCpnt)); if (hd->timeouts < -1) hd->timeouts++; @@ -3099,7 +3043,7 @@ * and then following up with the reset request. */ if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { - mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); post_pendingQ_commands(hd); nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " "Posting pended cmd! (sc=%p)\n", @@ -3164,8 +3108,8 @@ if (hd->resetPending) return FAILED; - printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p, numIOs=%d)\n", - hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)\n", + hd->ioc->name, SCpnt); /* Unsupported for SCSI. Supported for FCP */ @@ -3211,8 +3155,8 @@ return FAILED; } - printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p, numIOs=%d)\n", - hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)\n", + hd->ioc->name, SCpnt)); if (hd->timeouts < -1) hd->timeouts++; @@ -3262,8 +3206,6 @@ printk(KERN_WARNING MYNAM ": %s: >> Attempting host reset! (sc=%p)\n", hd->ioc->name, SCpnt); - printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n", - hd->ioc->name, atomic_read(&queue_depth)); /* If our attempts to reset the host failed, then return a failed * status. The host will be taken off line by the SCSI mid-layer. @@ -3339,7 +3281,6 @@ int scpnt_idx; printk(KERN_WARNING MYNAM ": OldAbort scheduling ABORT SCSI IO (sc=%p)\n", (void *) SCpnt); - printk(KERN_WARNING " IOs outstanding = %d\n", atomic_read(&queue_depth)); if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { printk(KERN_WARNING " WARNING - OldAbort, NULL hostdata ptr!!\n"); @@ -3366,7 +3307,7 @@ */ #ifndef MPTSCSIH_DBG_TIMEOUT if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { - mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); post_pendingQ_commands(hd); } #endif @@ -3472,7 +3413,6 @@ int scpnt_idx; printk(KERN_WARNING MYNAM ": OldReset scheduling BUS_RESET (sc=%p)\n", (void *) SCpnt); - printk(KERN_WARNING " IOs outstanding = %d\n", atomic_read(&queue_depth)); if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { SCpnt->result = DID_ERROR << 16; @@ -3498,7 +3438,7 @@ */ #ifndef MPTSCSIH_DBG_TIMEOUT if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { - mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); post_pendingQ_commands(hd); } #endif @@ -3740,7 +3680,6 @@ ": WARNING[1] - IOC error processing TaskMgmt request (sc=%p)\n", (void *) SCpnt); if (hd->ScsiLookup[scpnt_idx] != NULL) { - atomic_dec(&queue_depth); SCpnt->result = DID_SOFT_ERROR << 16; MPT_HOST_LOCK(flags); SCpnt->scsi_done(SCpnt); @@ -3937,7 +3876,6 @@ continue; if (hd->Targets != NULL) { - pTarget = NULL; if (device->id > sh->max_id) { /* error case, should never happen */ device->queue_depth = 1; @@ -3949,25 +3887,27 @@ if (pTarget == NULL) { /* error case - don't know about this device */ device->queue_depth = 1; - } else if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { - if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) - device->queue_depth = 1; - else if (((pTarget->inq_data[0] & 0x1f) == 0x00) - && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)){ - device->queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; - } else - device->queue_depth = MPT_SCSI_CMD_PER_DEV_LOW; - } else { - /* error case - No Inq. Data */ - device->queue_depth = 1; + device->queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; + if ( hd->is_spi ) { + if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { + if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) + device->queue_depth = 1; + else if (((pTarget->inq_data[0] & 0x1f) == 0x00) && (pTarget->minSyncFactor <= MPT_ULTRA160 )){ + device->queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; + } else + device->queue_depth = MPT_SCSI_CMD_PER_DEV_LOW; + } else { + /* error case - No Inq. Data */ + device->queue_depth = 1; + } + } } if (pTarget != NULL) { dprintk((MYIOC_s_INFO_FMT - "scsi%d: Id=%d Lun=%d: Queue depth=%d\n", - hd->ioc->name, - device->id, device->lun, device->queue_depth)); + "scsi%d: Id=%d Lun=%d: Queue depth=%d tflags=%x\n", + hd->ioc->name, device->id, pTarget->target_id, device->lun, device->queue_depth, pTarget->tflags)); dprintk((MYIOC_s_INFO_FMT "Id = %d, sync factor = %x\n", @@ -4172,7 +4112,7 @@ continue; } - mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); #if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) { @@ -4239,7 +4179,6 @@ if (hd->cmdPtr) { del_timer(&hd->timer); mpt_free_msg_frame(ScsiScanDvCtx, ioc->id, hd->cmdPtr); - atomic_dec(&queue_depth); } /* 2d. If a task management has not completed, @@ -4978,31 +4917,41 @@ { int indexed_lun, lun_index; VirtDevice *vdev; + char data_56; - dprintk((MYIOC_s_INFO_FMT "initTarget (%d,%d,%d) called, hd=%p\n", + dprintk((MYIOC_s_INFO_FMT "initTarget bus=%d id=%d lun=%d hd=%p\n", hd->ioc->name, bus_id, target_id, lun, hd)); + /* Is LUN supported? If so, upper 3 bits will be 0 + * in first byte of inquiry data. + */ + if (data[0] & 0xe0) + return; + if ((vdev = hd->Targets[target_id]) == NULL) { if ((vdev = kmalloc(sizeof(VirtDevice), GFP_ATOMIC)) == NULL) { printk(MYIOC_s_ERR_FMT "initTarget kmalloc(%d) FAILED!\n", hd->ioc->name, (int)sizeof(VirtDevice)); return; - } else { - memset(vdev, 0, sizeof(VirtDevice)); - rwlock_init(&vdev->VdevLock); - Q_INIT(&vdev->WaitQ, void); - Q_INIT(&vdev->SentQ, void); - Q_INIT(&vdev->DoneQ, void); - vdev->tflags = 0; - vdev->ioc_id = hd->ioc->id; - vdev->target_id = target_id; - vdev->bus_id = bus_id; - - hd->Targets[target_id] = vdev; - dprintk((KERN_INFO " *NEW* Target structure (id %d) @ %p\n", - target_id, vdev)); } - } + memset(vdev, 0, sizeof(VirtDevice)); + rwlock_init(&vdev->VdevLock); + Q_INIT(&vdev->WaitQ, void); + Q_INIT(&vdev->SentQ, void); + Q_INIT(&vdev->DoneQ, void); + vdev->tflags = 0; + vdev->ioc_id = hd->ioc->id; + vdev->target_id = target_id; + vdev->bus_id = bus_id; + + hd->Targets[target_id] = vdev; + dprintk((KERN_INFO " *NEW* Target structure (id %d) @ %p\n", + target_id, vdev)); + } + + lun_index = (lun >> 5); /* 32 luns per lun_index */ + indexed_lun = (lun % 32); + vdev->luns[lun_index] |= (1 << indexed_lun); vdev->raidVolume = 0; if (hd->is_spi) { @@ -5013,10 +4962,20 @@ } if (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) { - /* Copy the inquiry data - if we haven't yet. - */ - - memcpy (vdev->inq_data, data, 8); + if ( dlen > 8 ) { + memcpy (vdev->inq_data, data, 8); + } else { + memcpy (vdev->inq_data, data, dlen); + } + vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; + + /* If LUN 0, tape and have not done DV, set the DV flag. + */ + if (hd->is_spi && (lun == 0) && (data[0] == SCSI_TYPE_TAPE)) { + ScsiCfgData *pSpi = &hd->ioc->spi_data; + if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE) + pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV; + } if ( (data[0] == SCSI_TYPE_PROC) && !(vdev->tflags & MPT_TARGET_FLAGS_SAF_TE_ISSUED )) { @@ -5039,35 +4998,18 @@ mptscsih_writeIOCPage4(hd, target_id, bus_id); } } - } else - vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; + } - if ((dlen > 56) && (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) { + data_56 = 0x0F; /* Default to full capabilities if Inq data length is < 57 */ + if (dlen > 56) { + if ( (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56))) { /* Update the target capabilities */ - if (dlen > 56) { - mptscsih_setTargetNegoParms(hd, vdev, data[56]); + data_56 = data[56]; vdev->tflags |= MPT_TARGET_FLAGS_VALID_56; - } else - mptscsih_setTargetNegoParms(hd, vdev, 0); - - /* If LUN 0, tape and have not done DV, set the DV flag. - */ - if (hd->is_spi && (lun == 0) && ((data[0] & 0x1F) == 0x01)) { - ScsiCfgData *pSpi = &hd->ioc->spi_data; - if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE) - pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV; } } - } - - /* Is LUN supported? If so, upper 3 bits will be 0 - * in first byte of inquiry data. - */ - if ((*data & 0xe0) == 0) { - lun_index = (lun >> 5); /* 32 luns per lun_index */ - indexed_lun = (lun % 32); - vdev->luns[lun_index] |= (1 << indexed_lun); + mptscsih_setTargetNegoParms(hd, vdev, data_56); } dprintk((KERN_INFO " target = %p\n", vdev)); @@ -5093,7 +5035,7 @@ u8 offset = 0; u8 version, nfactor; u8 noQas = 1; - + if (!hd->is_spi) { if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { if (target->inq_data[7] & 0x02) @@ -5123,14 +5065,16 @@ } if (target->inq_data[7] & 0x10) { - /* bits 2 & 3 show DT support + /* bits 2 & 3 show Clocking support */ - if ((byte56 & 0x04) == 0) + if ((byte56 & 0x0C) == 0) factor = MPT_ULTRA2; - else if ((byte56 & 0x03) == 0) - factor = MPT_ULTRA160; - else - factor = MPT_ULTRA320; + else { + if ((byte56 & 0x03) == 0) + factor = MPT_ULTRA160; + else + factor = MPT_ULTRA320; + } offset = pspi_data->maxSyncOffset; /* If RAID, never disable QAS @@ -5139,8 +5083,9 @@ * bit 1 QAS support, non-raid only * bit 0 IU support */ - if ((target->raidVolume == 1) || ((byte56 & 0x02) != 0)) + if ((target->raidVolume == 1) || (byte56 & 0x02)) { noQas = 0; + } } else { factor = MPT_ASYNC; offset = 0; @@ -5218,7 +5163,7 @@ /* Disable QAS in a mixed configuration case */ -// ddvtprintk((KERN_INFO "Disabling QAS!\n")); + ddvtprintk((KERN_INFO "Disabling QAS due to noQas=%02x on id=%d!\n", noQas, id)); for (ii = 0; ii < id; ii++) { if ( (vdev = hd->Targets[ii]) ) { vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS; @@ -5226,8 +5171,17 @@ } } } + + /* Write SDP1 on this I/O to this target */ + if (pspi_data->dvStatus[id] & MPT_SCSICFG_NEGOTIATE) { + mptscsih_writeSDP1(hd, 0, id, hd->negoNvram); + pspi_data->dvStatus[id] &= ~MPT_SCSICFG_NEGOTIATE; + } else if (pspi_data->dvStatus[id] & MPT_SCSICFG_BLK_NEGO) { + mptscsih_writeSDP1(hd, 0, id, MPT_SCSICFG_BLK_NEGO); + pspi_data->dvStatus[id] &= ~MPT_SCSICFG_BLK_NEGO; + } + } - return; } @@ -5488,9 +5442,8 @@ pReq->Reserved = 0; pReq->ChainOffset = 0; pReq->Function = MPI_FUNCTION_CONFIG; - pReq->Reserved1[0] = 0; - pReq->Reserved1[1] = 0; - pReq->Reserved1[2] = 0; + pReq->ExtPageLength = 0; + pReq->ExtPageType = 0; pReq->MsgFlags = 0; for (ii=0; ii < 8; ii++) { pReq->Reserved2[ii] = 0; @@ -5522,7 +5475,7 @@ ioc->name, id, (id | (bus<<8)), requested, configuration)); - mptscsih_put_msgframe(ScsiDoneCtx, ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, ioc->id, mf); } return 0; @@ -5576,9 +5529,8 @@ pReq->Reserved = 0; pReq->ChainOffset = 0; pReq->Function = MPI_FUNCTION_CONFIG; - pReq->Reserved1[0] = 0; - pReq->Reserved1[1] = 0; - pReq->Reserved1[2] = 0; + pReq->ExtPageLength = 0; + pReq->ExtPageType = 0; pReq->MsgFlags = 0; for (ii=0; ii < 8; ii++) { pReq->Reserved2[ii] = 0; @@ -5603,7 +5555,7 @@ "writeIOCPage4: pgaddr 0x%x\n", ioc->name, (target_id | (bus<<8)))); - mptscsih_put_msgframe(ScsiDoneCtx, ioc->id, mf); + mpt_put_msg_frame(ScsiDoneCtx, ioc->id, mf); return 0; } @@ -5704,8 +5656,6 @@ ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n", hd->ioc->name, mf, mr, req_idx)); - atomic_dec(&queue_depth); - hd->pLocal = &hd->localReply; hd->pLocal->scsiStatus = 0; @@ -5948,7 +5898,7 @@ hd->ioc->name, action, io->id)); hd->pLocal = NULL; - hd->timer.expires = jiffies + HZ*2; /* 2 second timeout */ + hd->timer.expires = jiffies + HZ*10; /* 10 second timeout */ scandv_wait_done = 0; /* Save cmd pointer, for resource free if timeout or @@ -5957,7 +5907,7 @@ hd->cmdPtr = mf; add_timer(&hd->timer); - mptscsih_put_msgframe(ScsiScanDvCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiScanDvCtx, hd->ioc->id, mf); wait_event(scandv_waitq, scandv_wait_done); if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD)) @@ -6194,7 +6144,7 @@ hd->cmdPtr = mf; add_timer(&hd->timer); - mptscsih_put_msgframe(ScsiScanDvCtx, hd->ioc->id, mf); + mpt_put_msg_frame(ScsiScanDvCtx, hd->ioc->id, mf); wait_event(scandv_waitq, scandv_wait_done); if (hd->pLocal) { @@ -6638,8 +6588,8 @@ lun = 0; bus = (u8) bus_number; ddvtprintk((MYIOC_s_NOTE_FMT - "DV started: numIOs %d bus=%d, id %d dv @ %p\n", - ioc->name, atomic_read(&queue_depth), bus, id, &dv)); + "DV started: bus=%d, id %d dv @ %p\n", + ioc->name, bus, id, &dv)); /* Prep DV structure */ @@ -6818,7 +6768,7 @@ sz = SCSI_STD_INQUIRY_BYTES; rc = MPT_SCANDV_GOOD; while (1) { - ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test.\n", ioc->name)); + ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test on id=%d\n", ioc->name, id)); retcode = 0; dv.cmd = MPT_SET_MIN; mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); @@ -7010,7 +6960,7 @@ firstPass = 0; } } - ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test completed OK.\n", ioc->name)); + ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test on id=%d completed OK.\n", ioc->name, id)); inq0 = (*pbuf1) & 0x1F; /* Continue only for disks @@ -7461,8 +7411,8 @@ if (pDvBuf) pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma); - ddvtprintk((MYIOC_s_INFO_FMT "DV Done. IOs outstanding = %d\n", - ioc->name, atomic_read(&queue_depth))); + ddvtprintk((MYIOC_s_INFO_FMT "DV Done.\n", + ioc->name)); return retcode; } @@ -7631,7 +7581,6 @@ factor = MPT_ULTRA; width = MPT_WIDE; } else if ((factor == MPT_ULTRA) && width) { - factor = MPT_ULTRA; width = MPT_NARROW; } else if (factor < MPT_FAST) { factor = MPT_FAST; diff -urN linux-2.4.26/drivers/message/fusion/mptscsih.h linux-2.4.27/drivers/message/fusion/mptscsih.h --- linux-2.4.26/drivers/message/fusion/mptscsih.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/mptscsih.h 2004-08-07 16:26:04.934358283 -0700 @@ -15,7 +15,7 @@ * * (see also mptbase.c) * - * Copyright (c) 1999-2002 LSI Logic Corporation + * Copyright (c) 1999-2004 LSI Logic Corporation * Originally By: Steven J. Ralston * (mailto:sjralston1@netscape.net) * (mailto:mpt_linux_developer@lsil.com) diff -urN linux-2.4.26/drivers/message/fusion/scsi3.h linux-2.4.27/drivers/message/fusion/scsi3.h --- linux-2.4.26/drivers/message/fusion/scsi3.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/message/fusion/scsi3.h 2004-08-07 16:26:04.935358324 -0700 @@ -4,7 +4,7 @@ * (Ultimately) SCSI-3 definitions; for now, inheriting * SCSI-2 definitions. * - * Copyright (c) 1996-2002 Steven J. Ralston + * Copyright (c) 1996-2004 Steven J. Ralston * Written By: Steven J. Ralston (19960517) * (mailto:sjralston1@netscape.net) * (mailto:mpt_linux_developer@lsil.com) diff -urN linux-2.4.26/drivers/mtd/mtdchar.c linux-2.4.27/drivers/mtd/mtdchar.c --- linux-2.4.26/drivers/mtd/mtdchar.c 2003-06-13 07:51:34.000000000 -0700 +++ linux-2.4.27/drivers/mtd/mtdchar.c 2004-08-07 16:26:04.936358365 -0700 @@ -125,11 +125,15 @@ int ret=0; int len; char *kbuf; + loff_t pos = *ppos; DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); - if (*ppos + count > mtd->size) - count = mtd->size - *ppos; + if (pos < 0 || pos > mtd->size) + return 0; + + if (count > mtd->size - pos) + count = mtd->size - pos; if (!count) return 0; @@ -146,9 +150,9 @@ if (!kbuf) return -ENOMEM; - ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + ret = MTD_READ(mtd, pos, len, &retlen, kbuf); if (!ret) { - *ppos += retlen; + pos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); return -EFAULT; @@ -167,6 +171,8 @@ kfree(kbuf); } + *ppos = pos; + return total_retlen; } /* mtd_read */ @@ -176,17 +182,18 @@ char *kbuf; size_t retlen; size_t total_retlen=0; + loff_t pos = *ppos; int ret=0; int len; DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); - if (*ppos == mtd->size) + if (pos < 0 || pos >= mtd->size) return -ENOSPC; - - if (*ppos + count > mtd->size) - count = mtd->size - *ppos; + if (count > mtd->size - pos) + count = mtd->size - pos; + if (!count) return 0; @@ -207,9 +214,9 @@ return -EFAULT; } - ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf); if (!ret) { - *ppos += retlen; + pos += retlen; total_retlen += retlen; count -= retlen; buf += retlen; @@ -221,6 +228,7 @@ kfree(kbuf); } + *ppos = pos; return total_retlen; } /* mtd_write */ diff -urN linux-2.4.26/drivers/net/8139cp.c linux-2.4.27/drivers/net/8139cp.c --- linux-2.4.26/drivers/net/8139cp.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/net/8139cp.c 2004-08-07 16:26:04.938358447 -0700 @@ -1,6 +1,6 @@ /* 8139cp.c: A Linux PCI Ethernet driver for the RealTek 8139C+ chips. */ /* - Copyright 2001,2002 Jeff Garzik + Copyright 2001-2004 Jeff Garzik Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) [tg3.c] Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com) [sungem.c] @@ -48,8 +48,8 @@ */ #define DRV_NAME "8139cp" -#define DRV_VERSION "1.1" -#define DRV_RELDATE "Aug 30, 2003" +#define DRV_VERSION "1.2" +#define DRV_RELDATE "Mar 22, 2004" #include @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -334,36 +335,36 @@ }; struct cp_private { - unsigned tx_head; - unsigned tx_tail; - unsigned rx_tail; - void *regs; struct net_device *dev; spinlock_t lock; + u32 msg_enable; + + struct pci_dev *pdev; + u32 rx_config; + u16 cpcmd; + + struct net_device_stats net_stats; + struct cp_extra_stats cp_stats; + struct cp_dma_stats *nic_stats; + dma_addr_t nic_stats_dma; + unsigned rx_tail ____cacheline_aligned; struct cp_desc *rx_ring; - struct cp_desc *tx_ring; - struct ring_info tx_skb[CP_TX_RING_SIZE]; struct ring_info rx_skb[CP_RX_RING_SIZE]; unsigned rx_buf_sz; + + unsigned tx_head ____cacheline_aligned; + unsigned tx_tail; + + struct cp_desc *tx_ring; + struct ring_info tx_skb[CP_TX_RING_SIZE]; dma_addr_t ring_dma; #if CP_VLAN_TAG_USED struct vlan_group *vlgrp; #endif - u32 msg_enable; - - struct net_device_stats net_stats; - struct cp_extra_stats cp_stats; - struct cp_dma_stats *nic_stats; - dma_addr_t nic_stats_dma; - - struct pci_dev *pdev; - u32 rx_config; - u16 cpcmd; - unsigned int wol_enabled : 1; /* Is Wake-on-LAN enabled? */ u32 power_state[16]; @@ -424,25 +425,27 @@ #if CP_VLAN_TAG_USED static void cp_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); cp->vlgrp = grp; cp->cpcmd |= RxVlanOn; cpw16(CpCmd, cp->cpcmd); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); } static void cp_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); cp->cpcmd &= ~RxVlanOn; cpw16(CpCmd, cp->cpcmd); if (cp->vlgrp) cp->vlgrp->vlan_devices[vid] = NULL; - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); } #endif /* CP_VLAN_TAG_USED */ @@ -510,7 +513,7 @@ static int cp_rx_poll (struct net_device *dev, int *budget) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); unsigned rx_tail = cp->rx_tail; unsigned rx_work = dev->quota; unsigned rx; @@ -630,9 +633,13 @@ cp_interrupt (int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = dev_instance; - struct cp_private *cp = dev->priv; + struct cp_private *cp; u16 status; + if (unlikely(dev == NULL)) + return IRQ_NONE; + cp = netdev_priv(dev); + status = cpr16(IntrStatus); if (!status || (status == 0xFFFF)) return IRQ_NONE; @@ -648,20 +655,23 @@ /* close possible race's with dev_close */ if (unlikely(!netif_running(dev))) { cpw16(IntrMask, 0); - goto out; + spin_unlock(&cp->lock); + return IRQ_HANDLED; } - if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) { + if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) if (netif_rx_schedule_prep(dev)) { cpw16_f(IntrMask, cp_norx_intr_mask); __netif_rx_schedule(dev); } - } + if (status & (TxOK | TxErr | TxEmpty | SWInt)) cp_tx(cp); if (status & LinkChg) mii_check_media(&cp->mii_if, netif_msg_link(cp), FALSE); + spin_unlock(&cp->lock); + if (status & PciErr) { u16 pci_status; @@ -672,8 +682,7 @@ /* TODO: reset hardware */ } -out: - spin_unlock(&cp->lock); + return IRQ_HANDLED; } @@ -736,7 +745,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); unsigned entry; u32 eor; #if CP_VLAN_TAG_USED @@ -894,7 +903,7 @@ static void __cp_set_rx_mode (struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; u32 tmp; @@ -939,7 +948,7 @@ static void cp_set_rx_mode (struct net_device *dev) { unsigned long flags; - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); spin_lock_irqsave (&cp->lock, flags); __cp_set_rx_mode(dev); @@ -955,35 +964,28 @@ static struct net_device_stats *cp_get_stats(struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; /* The chip only need report frame silently dropped. */ - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); if (netif_running(dev) && netif_device_present(dev)) __cp_get_stats(cp); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); return &cp->net_stats; } static void cp_stop_hw (struct cp_private *cp) { - struct net_device *dev = cp->dev; - cpw16(IntrStatus, ~(cpr16(IntrStatus))); cpw16_f(IntrMask, 0); cpw8(Cmd, 0); cpw16_f(CpCmd, 0); - cpw16(IntrStatus, ~(cpr16(IntrStatus))); - synchronize_irq(); - udelay(10); + cpw16_f(IntrStatus, ~(cpr16(IntrStatus))); cp->rx_tail = 0; cp->tx_head = cp->tx_tail = 0; - - (void) dev; /* avoid compiler warning when synchronize_irq() - * disappears during !CONFIG_SMP - */ } static void cp_reset_hw (struct cp_private *cp) @@ -1012,6 +1014,7 @@ static void cp_init_hw (struct cp_private *cp) { struct net_device *dev = cp->dev; + dma_addr_t ring_dma; cp_reset_hw(cp); @@ -1037,10 +1040,13 @@ cpw32_f(HiTxRingAddr, 0); cpw32_f(HiTxRingAddr + 4, 0); - cpw32_f(RxRingAddr, cp->ring_dma); - cpw32_f(RxRingAddr + 4, 0); /* FIXME: 64-bit PCI */ - cpw32_f(TxRingAddr, cp->ring_dma + (sizeof(struct cp_desc) * CP_RX_RING_SIZE)); - cpw32_f(TxRingAddr + 4, 0); /* FIXME: 64-bit PCI */ + ring_dma = cp->ring_dma; + cpw32_f(RxRingAddr, ring_dma & 0xffffffff); + cpw32_f(RxRingAddr + 4, (ring_dma >> 16) >> 16); + + ring_dma += sizeof(struct cp_desc) * CP_RX_RING_SIZE; + cpw32_f(TxRingAddr, ring_dma & 0xffffffff); + cpw32_f(TxRingAddr + 4, (ring_dma >> 16) >> 16); cpw16(MultiIntr, 0); @@ -1154,7 +1160,7 @@ static int cp_open (struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); int rc; if (netif_msg_ifup(cp)) @@ -1184,19 +1190,24 @@ static int cp_close (struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; if (netif_msg_ifdown(cp)) printk(KERN_DEBUG "%s: disabling interface\n", dev->name); + spin_lock_irqsave(&cp->lock, flags); + netif_stop_queue(dev); netif_carrier_off(dev); - spin_lock_irq(&cp->lock); cp_stop_hw(cp); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); + + synchronize_irq(); free_irq(dev->irq, dev); + cp_free_rings(cp); return 0; } @@ -1204,8 +1215,9 @@ #ifdef BROKEN static int cp_change_mtu(struct net_device *dev, int new_mtu) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); int rc; + unsigned long flags; /* check for invalid MTU, according to hardware limits */ if (new_mtu < CP_MIN_MTU || new_mtu > CP_MAX_MTU) @@ -1218,7 +1230,7 @@ return 0; } - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); cp_stop_hw(cp); /* stop h/w and free rings */ cp_clean_rings(cp); @@ -1229,7 +1241,7 @@ rc = cp_init_rings(cp); /* realloc and restart h/w */ cp_start_hw(cp); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); return rc; } @@ -1248,7 +1260,7 @@ static int mdio_read(struct net_device *dev, int phy_id, int location) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); return location < 8 && mii_2_8139_map[location] ? readw(cp->regs + mii_2_8139_map[location]) : 0; @@ -1258,7 +1270,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int location, int value) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); if (location == 0) { cpw8(Cfg9346, Cfg9346_Unlock); @@ -1326,7 +1338,7 @@ static void cp_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); strcpy (info->driver, DRV_NAME); strcpy (info->version, DRV_VERSION); @@ -1345,55 +1357,57 @@ static int cp_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); int rc; + unsigned long flags; - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); rc = mii_ethtool_gset(&cp->mii_if, cmd); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); return rc; } static int cp_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); int rc; + unsigned long flags; - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); rc = mii_ethtool_sset(&cp->mii_if, cmd); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); return rc; } static int cp_nway_reset(struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); return mii_nway_restart(&cp->mii_if); } static u32 cp_get_msglevel(struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); return cp->msg_enable; } static void cp_set_msglevel(struct net_device *dev, u32 value) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); cp->msg_enable = value; } static u32 cp_get_rx_csum(struct net_device *dev) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); return (cpr16(CpCmd) & RxChkSum) ? 1 : 0; } static int cp_set_rx_csum(struct net_device *dev, u32 data) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); u16 cmd = cp->cpcmd, newcmd; newcmd = cmd; @@ -1404,10 +1418,12 @@ newcmd &= ~RxChkSum; if (newcmd != cmd) { - spin_lock_irq(&cp->lock); + unsigned long flags; + + spin_lock_irqsave(&cp->lock, flags); cp->cpcmd = newcmd; cpw16_f(CpCmd, newcmd); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); } return 0; @@ -1416,35 +1432,38 @@ static void cp_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; if (regs->len < CP_REGS_SIZE) return /* -EINVAL */; regs->version = CP_REGS_VER; - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); memcpy_fromio(p, cp->regs, CP_REGS_SIZE); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); } static void cp_get_wol (struct net_device *dev, struct ethtool_wolinfo *wol) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; - spin_lock_irq (&cp->lock); + spin_lock_irqsave (&cp->lock, flags); netdev_get_wol (cp, wol); - spin_unlock_irq (&cp->lock); + spin_unlock_irqrestore (&cp->lock, flags); } static int cp_set_wol (struct net_device *dev, struct ethtool_wolinfo *wol) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); + unsigned long flags; int rc; - spin_lock_irq (&cp->lock); + spin_lock_irqsave (&cp->lock, flags); rc = netdev_set_wol (cp, wol); - spin_unlock_irq (&cp->lock); + spin_unlock_irqrestore (&cp->lock, flags); return rc; } @@ -1464,13 +1483,13 @@ static void cp_get_ethtool_stats (struct net_device *dev, struct ethtool_stats *estats, u64 *tmp_stats) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); unsigned int work = 100; int i; /* begin NIC statistics dump */ - cpw32(StatsAddr + 4, 0); /* FIXME: 64-bit PCI */ - cpw32(StatsAddr, cp->nic_stats_dma | DumpStats); + cpw32(StatsAddr + 4, (cp->nic_stats_dma >> 16) >> 16); + cpw32(StatsAddr, (cp->nic_stats_dma & 0xffffffff) | DumpStats); cpr32(StatsAddr); while (work-- > 0) { @@ -1526,16 +1545,17 @@ static int cp_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) { - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); struct mii_ioctl_data *mii = (struct mii_ioctl_data *) &rq->ifr_data; int rc; + unsigned long flags; if (!netif_running(dev)) return -EINVAL; - spin_lock_irq(&cp->lock); + spin_lock_irqsave(&cp->lock, flags); rc = generic_mii_ioctl(&cp->mii_if, mii, cmd, NULL); - spin_unlock_irq(&cp->lock); + spin_unlock_irqrestore(&cp->lock, flags); return rc; } @@ -1637,7 +1657,9 @@ if (!dev) return -ENOMEM; SET_MODULE_OWNER(dev); - cp = dev->priv; + SET_NETDEV_DEV(dev, &pdev->dev); + + cp = netdev_priv(dev); cp->pdev = pdev; cp->dev = dev; cp->msg_enable = (debug < 0 ? CP_DEF_MSG_ENABLE : debug); @@ -1662,12 +1684,6 @@ if (rc) goto err_out_mwi; - if (pdev->irq < 2) { - rc = -EIO; - printk(KERN_ERR PFX "invalid irq (%d) for pci dev %s\n", - pdev->irq, pci_name(pdev)); - goto err_out_res; - } pciaddr = pci_resource_start(pdev, 1); if (!pciaddr) { rc = -EIO; @@ -1687,19 +1703,20 @@ !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) { pci_using_dac = 1; } else { + pci_using_dac = 0; + rc = pci_set_dma_mask(pdev, 0xffffffffULL); if (rc) { printk(KERN_ERR PFX "No usable DMA configuration, " "aborting.\n"); goto err_out_res; } - pci_using_dac = 0; } cp->cpcmd = (pci_using_dac ? PCIDAC : 0) | PCIMulRW | RxChkSum | CpRxOn | CpTxOn; - regs = ioremap_nocache(pciaddr, CP_REGS_SIZE); + regs = ioremap(pciaddr, CP_REGS_SIZE); if (!regs) { rc = -EIO; printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n", @@ -1740,6 +1757,9 @@ dev->vlan_rx_kill_vid = cp_vlan_rx_kill_vid; #endif + if (pci_using_dac) + dev->features |= NETIF_F_HIGHDMA; + dev->irq = pdev->irq; rc = register_netdev(dev); @@ -1774,14 +1794,14 @@ err_out_disable: pci_disable_device(pdev); err_out_free: - kfree(dev); + free_netdev(dev); return rc; } static void cp_remove_one (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct cp_private *cp = dev->priv; + struct cp_private *cp = netdev_priv(dev); if (!dev) BUG(); @@ -1792,7 +1812,7 @@ pci_clear_mwi(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - kfree(dev); + free_netdev(dev); } #ifdef CONFIG_PM @@ -1803,7 +1823,7 @@ unsigned long flags; dev = pci_get_drvdata (pdev); - cp = dev->priv; + cp = netdev_priv(dev); if (!dev || !netif_running (dev)) return 0; @@ -1832,7 +1852,7 @@ struct cp_private *cp; dev = pci_get_drvdata (pdev); - cp = dev->priv; + cp = netdev_priv(dev); netif_device_attach (dev); diff -urN linux-2.4.26/drivers/net/8139too.c linux-2.4.27/drivers/net/8139too.c --- linux-2.4.26/drivers/net/8139too.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/8139too.c 2004-08-07 16:26:04.940358529 -0700 @@ -2482,6 +2482,8 @@ void *ioaddr = tp->mmio_addr; unsigned long flags; + pci_save_state (pdev, tp->pci_state); + if (!netif_running (dev)) return 0; @@ -2498,7 +2500,6 @@ RTL_W32 (RxMissed, 0); pci_set_power_state (pdev, 3); - pci_save_state (pdev, tp->pci_state); spin_unlock_irqrestore (&tp->lock, flags); return 0; @@ -2510,9 +2511,9 @@ struct net_device *dev = pci_get_drvdata (pdev); struct rtl8139_private *tp = dev->priv; + pci_restore_state (pdev, tp->pci_state); if (!netif_running (dev)) return 0; - pci_restore_state (pdev, tp->pci_state); pci_set_power_state (pdev, 0); rtl8139_init_ring (dev); rtl8139_hw_start (dev); diff -urN linux-2.4.26/drivers/net/8390.c linux-2.4.27/drivers/net/8390.c --- linux-2.4.26/drivers/net/8390.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/net/8390.c 2004-08-07 16:26:04.970359762 -0700 @@ -1109,7 +1109,7 @@ for(i = 0; i < 6; i++) { outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i)); - if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) + if (ei_debug > 1 && inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) printk(KERN_ERR "Hw. address read/write mismap %d\n",i); } diff -urN linux-2.4.26/drivers/net/Config.in linux-2.4.27/drivers/net/Config.in --- linux-2.4.26/drivers/net/Config.in 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/Config.in 2004-08-07 16:26:04.971359803 -0700 @@ -311,6 +311,10 @@ fi fi +if [ "$CONFIG_PPC_PSERIES" = "y" ]; then + dep_tristate 'IBM PowerPC Virtual Ethernet driver support' CONFIG_IBMVETH $CONFIG_PPC_PSERIES +fi + dep_tristate 'PLIP (parallel port) support' CONFIG_PLIP $CONFIG_PARPORT tristate 'PPP (point-to-point protocol) support' CONFIG_PPP diff -urN linux-2.4.26/drivers/net/Makefile linux-2.4.27/drivers/net/Makefile --- linux-2.4.26/drivers/net/Makefile 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/Makefile 2004-08-07 16:26:04.972359844 -0700 @@ -245,6 +245,8 @@ obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o obj-$(CONFIG_USB_USBNET) += mii.o +obj-$(CONFIG_IBMVETH) += ibmveth.o + ifeq ($(CONFIG_ARCH_ACORN),y) mod-subdirs += ../acorn/net subdir-y += ../acorn/net diff -urN linux-2.4.26/drivers/net/a2065.c linux-2.4.27/drivers/net/a2065.c --- linux-2.4.26/drivers/net/a2065.c 2003-06-13 07:51:34.000000000 -0700 +++ linux-2.4.27/drivers/net/a2065.c 2004-08-07 16:26:05.017361693 -0700 @@ -284,6 +284,7 @@ struct sk_buff *skb = 0; /* XXX shut up gcc warnings */ #ifdef TEST_HITS + int i; printk ("["); for (i = 0; i < RX_RING_SIZE; i++) { if (i == lp->rx_new) diff -urN linux-2.4.26/drivers/net/aironet4500_core.c linux-2.4.27/drivers/net/aironet4500_core.c --- linux-2.4.26/drivers/net/aironet4500_core.c 2001-09-30 12:26:06.000000000 -0700 +++ linux-2.4.27/drivers/net/aironet4500_core.c 2004-08-07 16:26:05.019361775 -0700 @@ -2836,7 +2836,7 @@ return 0; final: printk(KERN_ERR "aironet init failed \n"); - return NODEV; + return ENODEV; }; diff -urN linux-2.4.26/drivers/net/amd8111e.c linux-2.4.27/drivers/net/amd8111e.c --- linux-2.4.26/drivers/net/amd8111e.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/net/amd8111e.c 2004-08-07 16:26:05.021361857 -0700 @@ -1,6 +1,6 @@ /* Advanced Micro Devices Inc. AMD8111E Linux Network Driver - * Copyright (C) 2003 Advanced Micro Devices + * Copyright (C) 2004 Advanced Micro Devices * * * Copyright 2001,2002 Jeff Garzik [ 8139cp.c,tg3.c ] @@ -55,6 +55,14 @@ 4. Dynamic IPG support is disabled by default. 3.0.3 06/05/2003 1. Bug fix: Fixed failure to close the interface if SMP is enabled. + 3.0.4 12/09/2003 + 1. Added set_mac_address routine for bonding driver support. + 2. Tested the driver for bonding support + 3. Bug fix: Fixed mismach in actual receive buffer lenth and lenth + indicated to the h/w. + 4. Modified amd8111e_rx() routine to receive all the received packets + in the first interrupt. + 5. Bug fix: Corrected rx_errors reported in get_stats() function. */ @@ -91,9 +99,9 @@ #include "amd8111e.h" #define MODULE_NAME "amd8111e" -#define MODULE_VERSION "3.0.3" +#define MODULE_VERSION "3.0.4" MODULE_AUTHOR("Advanced Micro Devices, Inc."); -MODULE_DESCRIPTION ("AMD8111 based 10/100 Ethernet Controller. Driver Version 3.0.3"); +MODULE_DESCRIPTION ("AMD8111 based 10/100 Ethernet Controller. Driver Version 3.0.4"); MODULE_LICENSE("GPL"); MODULE_PARM(speed_duplex, "1-" __MODULE_STRING (MAX_UNITS) "i"); MODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotitate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex"); @@ -276,8 +284,10 @@ unsigned int mtu = dev->mtu; if (mtu > ETH_DATA_LEN){ - /* MTU + ethernet header + FCS + optional VLAN tag */ - lp->rx_buff_len = mtu + ETH_HLEN + 8; + /* MTU + ethernet header + FCS + + optional VLAN tag + skb reserve space 2 */ + + lp->rx_buff_len = mtu + ETH_HLEN + 10; lp->options |= OPTION_JUMBO_ENABLE; } else{ lp->rx_buff_len = PKT_BUFF_SZ; @@ -337,7 +347,7 @@ lp->rx_skbuff[i]->data,lp->rx_buff_len-2, PCI_DMA_FROMDEVICE); lp->rx_ring[i].buff_phy_addr = cpu_to_le32(lp->rx_dma_addr[i]); - lp->rx_ring[i].buff_count = cpu_to_le16(lp->rx_buff_len); + lp->rx_ring[i].buff_count = cpu_to_le16(lp->rx_buff_len-2); lp->rx_ring[i].rx_flags = cpu_to_le16(OWN_BIT); } @@ -513,6 +523,9 @@ void * mmio = lp->mmio; + /* stop the chip */ + writel(RUN, mmio + CMD0); + /* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */ writew( 0x8101, mmio + AUTOPOLL0); @@ -710,7 +723,7 @@ int rx_index = lp->rx_idx & RX_RING_DR_MOD_MASK; int min_pkt_len, status; int num_rx_pkt = 0; - int max_rx_pkt = NUM_RX_BUFFERS/2; + int max_rx_pkt = NUM_RX_BUFFERS; short pkt_len; #if AMD8111E_VLAN_TAG_USED short vtag; @@ -752,14 +765,14 @@ if (pkt_len < min_pkt_len) { lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; - lp->stats.rx_errors++; + lp->drv_rx_errors++; goto err_next_pkt; } if(!(new_skb = dev_alloc_skb(lp->rx_buff_len))){ /* if allocation fail, ignore that pkt and go to next one */ lp->rx_ring[rx_index].rx_flags &= RESET_RX_FLAGS; - lp->stats.rx_errors++; + lp->drv_rx_errors++; goto err_next_pkt; } @@ -896,12 +909,14 @@ new_stats->tx_bytes = amd8111e_read_mib(mmio, xmt_octets); /* stats.rx_errors */ + /* hw errors + errors driver reported */ new_stats->rx_errors = amd8111e_read_mib(mmio, rcv_undersize_pkts)+ amd8111e_read_mib(mmio, rcv_fragments)+ amd8111e_read_mib(mmio, rcv_jabbers)+ amd8111e_read_mib(mmio, rcv_alignment_errors)+ amd8111e_read_mib(mmio, rcv_fcs_errors)+ - amd8111e_read_mib(mmio, rcv_miss_pkts); + amd8111e_read_mib(mmio, rcv_miss_pkts)+ + lp->drv_rx_errors; /* stats.tx_errors */ new_stats->tx_errors = amd8111e_read_mib(mmio, xmt_underrun_pkts); @@ -1171,7 +1186,7 @@ spin_unlock_irq(&lp->lock); free_irq(dev->irq, dev); - + /* Update the statistics before closing */ amd8111e_get_stats(dev); lp->opened = 0; @@ -1545,6 +1560,23 @@ } return -EOPNOTSUPP; } +static int amd8111e_set_mac_address(struct net_device *dev, void *p) +{ + struct amd8111e_priv *lp = dev->priv; + int i; + struct sockaddr *addr = p; + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + spin_lock_irq(&lp->lock); + /* Setting the MAC address to the device */ + for(i = 0; i < ETH_ADDR_LEN; i++) + writeb( dev->dev_addr[i], lp->mmio + PADR + i ); + + spin_unlock_irq(&lp->lock); + + return 0; +} + /* This function changes the mtu of the device. It restarts the device to initialize the descriptor with new receive buffers. */ @@ -1875,6 +1907,7 @@ dev->stop = amd8111e_close; dev->get_stats = amd8111e_get_stats; dev->set_multicast_list = amd8111e_set_multicast_list; + dev->set_mac_address = amd8111e_set_mac_address; dev->do_ioctl = amd8111e_ioctl; dev->change_mtu = amd8111e_change_mtu; dev->irq =pdev->irq; diff -urN linux-2.4.26/drivers/net/amd8111e.h linux-2.4.27/drivers/net/amd8111e.h --- linux-2.4.26/drivers/net/amd8111e.h 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/net/amd8111e.h 2004-08-07 16:26:05.021361857 -0700 @@ -1,6 +1,6 @@ /* * Advanced Micro Devices Inc. AMD8111E Linux Network Driver - * Copyright (C) 2003 Advanced Micro Devices + * Copyright (C) 2004 Advanced Micro Devices * * 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 @@ -790,6 +790,7 @@ #endif char opened; struct net_device_stats stats; + unsigned int drv_rx_errors; struct dev_mc_list* mc_list; struct amd8111e_coalesce_conf coal_conf; diff -urN linux-2.4.26/drivers/net/b44.c linux-2.4.27/drivers/net/b44.c --- linux-2.4.26/drivers/net/b44.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/net/b44.c 2004-08-07 16:26:05.038362556 -0700 @@ -2,6 +2,8 @@ * * Copyright (C) 2002 David S. Miller (davem@redhat.com) * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) + * + * Distribute under GPL. */ #include @@ -25,8 +27,8 @@ #define DRV_MODULE_NAME "b44" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "0.92" -#define DRV_MODULE_RELDATE "Nov 4, 2003" +#define DRV_MODULE_VERSION "0.93" +#define DRV_MODULE_RELDATE "Mar, 2004" #define B44_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ @@ -83,6 +85,10 @@ static struct pci_device_id b44_pci_tbl[] = { { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B0, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_BCM4401B1, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { } /* terminate list with empty entry */ }; @@ -90,7 +96,7 @@ static void b44_halt(struct b44 *); static void b44_init_rings(struct b44 *); -static int b44_init_hw(struct b44 *); +static void b44_init_hw(struct b44 *); static int b44_wait_bit(struct b44 *bp, unsigned long reg, u32 bit, unsigned long timeout, const int clear) @@ -1170,11 +1176,10 @@ * packet processing. Invoked with bp->lock held. */ static void __b44_set_rx_mode(struct net_device *); -static int b44_init_hw(struct b44 *bp) +static void b44_init_hw(struct b44 *bp) { u32 val; - b44_disable_ints(bp); b44_chip_reset(bp); b44_phy_reset(bp); b44_setup_phy(bp); @@ -1203,8 +1208,6 @@ val = br32(B44_ENET_CTRL); bw32(B44_ENET_CTRL, (val | ENET_CTRL_ENABLE)); - - return 0; } static int b44_open(struct net_device *dev) @@ -1223,9 +1226,7 @@ spin_lock_irq(&bp->lock); b44_init_rings(bp); - err = b44_init_hw(bp); - if (err) - goto err_out_noinit; + b44_init_hw(bp); bp->flags |= B44_FLAG_INIT_COMPLETE; spin_unlock_irq(&bp->lock); @@ -1240,11 +1241,6 @@ return 0; -err_out_noinit: - b44_halt(bp); - b44_free_rings(bp); - spin_unlock_irq(&bp->lock); - free_irq(dev->irq, dev); err_out_free: b44_free_consistent(bp); return err; @@ -1373,7 +1369,7 @@ spin_unlock_irq(&bp->lock); } -static int b44_ethtool_ioctl (struct net_device *dev, void *useraddr) +static int b44_ethtool_ioctl (struct net_device *dev, void __user *useraddr) { struct b44 *bp = dev->priv; struct pci_dev *pci_dev = bp->pdev; @@ -1383,7 +1379,7 @@ return -EFAULT; switch (ethcmd) { - case ETHTOOL_GDRVINFO:{ + case ETHTOOL_GDRVINFO: { struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; strcpy (info.driver, DRV_MODULE_NAME); strcpy (info.version, DRV_MODULE_VERSION); @@ -1616,13 +1612,13 @@ static int b44_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; + struct mii_ioctl_data __user *data = (struct mii_ioctl_data __user *)&ifr->ifr_data; struct b44 *bp = dev->priv; int err; switch (cmd) { case SIOCETHTOOL: - return b44_ethtool_ioctl(dev, (void *) ifr->ifr_data); + return b44_ethtool_ioctl(dev, (void __user*) ifr->ifr_data); case SIOCGMIIPHY: data->phy_id = bp->phy_addr; @@ -1758,6 +1754,7 @@ } SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev,&pdev->dev); /* No interesting netdevice features in this card... */ dev->features |= 0; @@ -1884,6 +1881,8 @@ struct net_device *dev = pci_get_drvdata(pdev); struct b44 *bp = dev->priv; + pci_restore_state(pdev, bp->pci_cfg_state); + if (!netif_running(dev)) return 0; diff -urN linux-2.4.26/drivers/net/defxx.c linux-2.4.27/drivers/net/defxx.c --- linux-2.4.26/drivers/net/defxx.c 2001-12-21 09:41:54.000000000 -0800 +++ linux-2.4.27/drivers/net/defxx.c 2004-08-07 16:26:05.065363665 -0700 @@ -500,7 +500,7 @@ static int __init dfx_eisa_init(void) { - int rc = -NODEV; + int rc = -ENODEV; int i; /* used in for loops */ u16 port; /* temporary I/O (port) address */ u32 slot_id; /* EISA hardware (slot) ID read from adapter */ diff -urN linux-2.4.26/drivers/net/e100/e100.h linux-2.4.27/drivers/net/e100/e100.h --- linux-2.4.26/drivers/net/e100/e100.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/net/e100/e100.h 2004-08-07 16:26:05.087364569 -0700 @@ -434,6 +434,7 @@ #define D102_REV_ID 12 #define D102C_REV_ID 13 /* 82550 step C */ #define D102E_REV_ID 15 +#define D102E_A1_REV_ID 16 /* ############Start of 82555 specific defines################## */ diff -urN linux-2.4.26/drivers/net/e100/e100_main.c linux-2.4.27/drivers/net/e100/e100_main.c --- linux-2.4.26/drivers/net/e100/e100_main.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/net/e100/e100_main.c 2004-08-07 16:26:05.090364693 -0700 @@ -46,7 +46,16 @@ /* Change Log * - * 2.3.38 12/14/03 + * 2.3.40 2/13/04 + * o Updated microcode for D102 rev 15 and rev 16 to include fix + * for TCO issue. NFS packets would be misinterpreted as TCO packets + * and incorrectly routed to the BMC over SMBus. The microcode fix + * checks the fragmented IP bit in the NFS/UDP header to distinguish + * between NFS and TCO. + * o Bug fix: don't strip MAC header count from Rx byte count. + * Ben Greear (greear@candeltech.com). + * + * 2.3.38 12/14/03 * o Added netpoll support. * o Added ICH6 device ID support * o Moved to 2.6 APIs: pci_name() and free_netdev(). @@ -54,12 +63,6 @@ * as such (Anton Blanchard [anton@samba.org]). * * 2.3.33 10/21/03 - * o Bug fix (Bugzilla 97908): Loading e100 was causing crash on Itanium2 - * with HP chipset - * o Bug fix (Bugzilla 101583): e100 can't pass traffic with ipv6 - * o Bug fix (Bugzilla 101360): PRO/10+ can't pass traffic - * - * 2.3.27 08/08/03 */ #include @@ -132,7 +135,7 @@ static inline void e100_tx_skb_free(struct e100_private *bdp, tcb_t *tcb); /* Global Data structures and variables */ char e100_copyright[] __devinitdata = "Copyright (c) 2004 Intel Corporation"; -char e100_driver_version[]="2.3.38-k1"; +char e100_driver_version[]="2.3.43-k1"; const char *e100_full_driver_name = "Intel(R) PRO/100 Network Driver"; char e100_short_driver_name[] = "e100"; static int e100nics = 0; @@ -582,6 +585,7 @@ bdp->device = dev; pci_set_drvdata(pcid, dev); + SET_NETDEV_DEV(dev, &pcid->dev); bdp->flags = 0; bdp->ifs_state = 0; @@ -2062,6 +2066,8 @@ else skb_put(skb, (int) data_sz); + bdp->drv_stats.net_stats.rx_bytes += skb->len; + /* set the protocol */ skb->protocol = eth_type_trans(skb, dev); @@ -2076,8 +2082,6 @@ skb->ip_summed = CHECKSUM_NONE; } - bdp->drv_stats.net_stats.rx_bytes += skb->len; - if(bdp->vlgrp && (rfd_status & CB_STATUS_VLAN)) { vlan_hwaccel_rx(skb, bdp->vlgrp, be16_to_cpu(rfd->vlanid)); } else { @@ -2835,6 +2839,11 @@ D102_E_CPUSAVER_TIMER_DWORD, D102_E_CPUSAVER_BUNDLE_DWORD, D102_E_CPUSAVER_MIN_SIZE_DWORD }, + { D102E_A1_REV_ID, + D102_E_RCVBUNDLE_UCODE, + D102_E_CPUSAVER_TIMER_DWORD, + D102_E_CPUSAVER_BUNDLE_DWORD, + D102_E_CPUSAVER_MIN_SIZE_DWORD }, { 0, {0}, 0, 0, 0} }, *opts; diff -urN linux-2.4.26/drivers/net/e100/e100_ucode.h linux-2.4.27/drivers/net/e100/e100_ucode.h --- linux-2.4.26/drivers/net/e100/e100_ucode.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/net/e100/e100_ucode.h 2004-08-07 16:26:05.091364734 -0700 @@ -346,7 +346,7 @@ #define D102_E_RCVBUNDLE_UCODE \ {\ -0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x1FFF1FFF, 0x1FFF1FFF, \ +0x007D028F, 0x0E4204F9, 0x14ED0C85, 0x14FA14E9, 0x0EF70E36, 0x1FFF1FFF, \ 0x00E014B9, 0x00000000, 0x00000000, 0x00000000, \ 0x00E014BD, 0x00000000, 0x00000000, 0x00000000, \ 0x00E014D5, 0x00000000, 0x00000000, 0x00000000, \ @@ -359,7 +359,26 @@ 0x00200600, 0x00E014EE, 0x00000000, 0x00000000, \ 0x0030FF80, 0x00940E46, 0x00038200, 0x00102000, \ 0x00E00E43, 0x00000000, 0x00000000, 0x00000000, \ -0x00300006, 0x00E014FB, 0x00000000, 0x00000000 \ +0x00300006, 0x00E014FB, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00906E41, 0x00800E3C, 0x00E00E39, 0x00000000, \ +0x00906EFD, 0x00900EFD, 0x00E00EF8, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ +0x00000000, 0x00000000, 0x00000000, 0x00000000, \ } #endif /* _E100_UCODE_H_ */ diff -urN linux-2.4.26/drivers/net/e1000/e1000.h linux-2.4.27/drivers/net/e1000/e1000.h --- linux-2.4.26/drivers/net/e1000/e1000.h 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000.h 2004-08-07 16:26:05.091364734 -0700 @@ -70,6 +70,7 @@ #include #include #include +#include #define BAR_0 0 #define BAR_1 1 @@ -90,6 +91,12 @@ #define E1000_ERR(args...) printk(KERN_ERR "e1000: " args) +#define PFX "e1000: " +#define DPRINTK(nlevel, klevel, fmt, args...) \ + (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ + printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \ + __FUNCTION__ , ## args)) + #define E1000_MAX_INTR 10 /* How many descriptors for TX and RX ? */ @@ -113,7 +120,7 @@ #define E1000_SMARTSPEED_MAX 15 /* Packet Buffer allocations */ -#define E1000_TX_FIFO_SIZE_SHIFT 0xA +#define E1000_PBA_BYTES_SHIFT 0xA #define E1000_TX_HEAD_ADDR_SHIFT 7 #define E1000_PBA_TX_MASK 0xFFFF0000 @@ -190,6 +197,7 @@ uint32_t part_num; uint32_t wol; uint32_t smartspeed; + uint32_t en_mng_pt; uint16_t link_speed; uint16_t link_duplex; spinlock_t stats_lock; @@ -246,5 +254,6 @@ uint32_t pci_state[16]; + int msg_enable; }; #endif /* _E1000_H_ */ diff -urN linux-2.4.26/drivers/net/e1000/e1000_ethtool.c linux-2.4.27/drivers/net/e1000/e1000_ethtool.c --- linux-2.4.26/drivers/net/e1000/e1000_ethtool.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000_ethtool.c 2004-08-07 16:26:05.094364857 -0700 @@ -53,7 +53,7 @@ #define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \ offsetof(struct e1000_adapter, m) -static struct e1000_stats e1000_gstrings_stats[] = { +static const struct e1000_stats e1000_gstrings_stats[] = { { "rx_packets", E1000_STAT(net_stats.rx_packets) }, { "tx_packets", E1000_STAT(net_stats.tx_packets) }, { "rx_bytes", E1000_STAT(net_stats.rx_bytes) }, @@ -89,7 +89,8 @@ { "tx_flow_control_xon", E1000_STAT(stats.xontxc) }, { "tx_flow_control_xoff", E1000_STAT(stats.xofftxc) }, { "rx_csum_offload_good", E1000_STAT(hw_csum_good) }, - { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) } + { "rx_csum_offload_errors", E1000_STAT(hw_csum_err) }, + { "rx_long_byte_count", E1000_STAT(stats.gorcl) } }; #define E1000_STATS_LEN \ sizeof(e1000_gstrings_stats) / sizeof(struct e1000_stats) @@ -100,9 +101,10 @@ }; #define E1000_TEST_LEN sizeof(e1000_gstrings_test) / ETH_GSTRING_LEN -static void -e1000_ethtool_gset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) +static int +e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; if(hw->media_type == e1000_media_type_copper) { @@ -169,11 +171,13 @@ } ecmd->autoneg = (hw->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE); + return 0; } static int -e1000_ethtool_sset(struct e1000_adapter *adapter, struct ethtool_cmd *ecmd) +e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; if(ecmd->autoneg == AUTONEG_ENABLE) { @@ -195,42 +199,41 @@ return 0; } -static int -e1000_ethtool_gpause(struct e1000_adapter *adapter, - struct ethtool_pauseparam *epause) +static void +e1000_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; - - epause->autoneg = + pause->autoneg = (adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE); if(hw->fc == e1000_fc_rx_pause) - epause->rx_pause = 1; + pause->rx_pause = 1; else if(hw->fc == e1000_fc_tx_pause) - epause->tx_pause = 1; + pause->tx_pause = 1; else if(hw->fc == e1000_fc_full) { - epause->rx_pause = 1; - epause->tx_pause = 1; + pause->rx_pause = 1; + pause->tx_pause = 1; } - - return 0; } -static int -e1000_ethtool_spause(struct e1000_adapter *adapter, - struct ethtool_pauseparam *epause) +static int +e1000_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; - adapter->fc_autoneg = epause->autoneg; + adapter->fc_autoneg = pause->autoneg; - if(epause->rx_pause && epause->tx_pause) + if(pause->rx_pause && pause->tx_pause) hw->fc = e1000_fc_full; - else if(epause->rx_pause && !epause->tx_pause) + else if(pause->rx_pause && !pause->tx_pause) hw->fc = e1000_fc_rx_pause; - else if(!epause->rx_pause && epause->tx_pause) + else if(!pause->rx_pause && pause->tx_pause) hw->fc = e1000_fc_tx_pause; - else if(!epause->rx_pause && !epause->tx_pause) + else if(!pause->rx_pause && !pause->tx_pause) hw->fc = e1000_fc_none; hw->original_fc = hw->fc; @@ -248,28 +251,101 @@ return 0; } +static uint32_t +e1000_get_rx_csum(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + return adapter->rx_csum; +} + +static int +e1000_set_rx_csum(struct net_device *netdev, uint32_t data) +{ + struct e1000_adapter *adapter = netdev->priv; + adapter->rx_csum = data; + + if(netif_running(netdev)) { + e1000_down(adapter); + e1000_up(adapter); + } else + e1000_reset(adapter); + return 0; +} + +static uint32_t +e1000_get_tx_csum(struct net_device *netdev) +{ + return (netdev->features & NETIF_F_HW_CSUM) != 0; +} + +static int +e1000_set_tx_csum(struct net_device *netdev, uint32_t data) +{ + struct e1000_adapter *adapter = netdev->priv; + + if(adapter->hw.mac_type < e1000_82543) { + if (!data) + return -EINVAL; + return 0; + } + + if (data) + netdev->features |= NETIF_F_HW_CSUM; + else + netdev->features &= ~NETIF_F_HW_CSUM; + + return 0; +} + +#ifdef NETIF_F_TSO +static int +e1000_set_tso(struct net_device *netdev, uint32_t data) +{ + struct e1000_adapter *adapter = netdev->priv; + if ((adapter->hw.mac_type < e1000_82544) || + (adapter->hw.mac_type == e1000_82547)) + return data ? -EINVAL : 0; + + if (data) + netdev->features |= NETIF_F_TSO; + else + netdev->features &= ~NETIF_F_TSO; + return 0; +} +#endif /* NETIF_F_TSO */ + +static uint32_t +e1000_get_msglevel(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + return adapter->msg_enable; +} + static void -e1000_ethtool_gdrvinfo(struct e1000_adapter *adapter, - struct ethtool_drvinfo *drvinfo) +e1000_set_msglevel(struct net_device *netdev, uint32_t data) +{ + struct e1000_adapter *adapter = netdev->priv; + adapter->msg_enable = data; +} + +static int +e1000_get_regs_len(struct net_device *netdev) { - strncpy(drvinfo->driver, e1000_driver_name, 32); - strncpy(drvinfo->version, e1000_driver_version, 32); - strncpy(drvinfo->fw_version, "N/A", 32); - strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); - drvinfo->n_stats = E1000_STATS_LEN; - drvinfo->testinfo_len = E1000_TEST_LEN; #define E1000_REGS_LEN 32 - drvinfo->regdump_len = E1000_REGS_LEN * sizeof(uint32_t); - drvinfo->eedump_len = adapter->hw.eeprom.word_size * 2; + return E1000_REGS_LEN * sizeof(uint32_t); } static void -e1000_ethtool_gregs(struct e1000_adapter *adapter, - struct ethtool_regs *regs, uint32_t *regs_buff) +e1000_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; + uint32_t *regs_buff = p; uint16_t phy_data; + memset(p, 0, E1000_REGS_LEN * sizeof(uint32_t)); + regs->version = (1 << 24) | (hw->revision_id << 16) | hw->device_id; regs_buff[0] = E1000_READ_REG(hw, CTRL); @@ -342,59 +418,72 @@ e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); regs_buff[24] = (uint32_t)phy_data; /* phy local receiver status */ regs_buff[25] = regs_buff[24]; /* phy remote receiver status */ +} - return; +static int +e1000_get_eeprom_len(struct net_device *netdev) +{ + struct e1000_adapter *adapter = netdev->priv; + return adapter->hw.eeprom.word_size * 2; } static int -e1000_ethtool_geeprom(struct e1000_adapter *adapter, - struct ethtool_eeprom *eeprom, uint16_t *eeprom_buff) +e1000_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, uint8_t *bytes) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; + uint16_t *eeprom_buff; int first_word, last_word; int ret_val = 0; + uint16_t i; - if(eeprom->len == 0) { - ret_val = -EINVAL; - goto geeprom_error; - } + if(eeprom->len == 0) + return -EINVAL; eeprom->magic = hw->vendor_id | (hw->device_id << 16); - if(eeprom->offset > eeprom->offset + eeprom->len) { - ret_val = -EINVAL; - goto geeprom_error; - } - - if((eeprom->offset + eeprom->len) > (hw->eeprom.word_size * 2)) - eeprom->len = ((hw->eeprom.word_size * 2) - eeprom->offset); - first_word = eeprom->offset >> 1; last_word = (eeprom->offset + eeprom->len - 1) >> 1; + eeprom_buff = kmalloc(sizeof(uint16_t) * + (last_word - first_word + 1), GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + if(hw->eeprom.type == e1000_eeprom_spi) ret_val = e1000_read_eeprom(hw, first_word, last_word - first_word + 1, eeprom_buff); else { - uint16_t i; for (i = 0; i < last_word - first_word + 1; i++) if((ret_val = e1000_read_eeprom(hw, first_word + i, 1, &eeprom_buff[i]))) break; } -geeprom_error: + + /* Device's eeprom is always little-endian, word addressable */ + for (i = 0; i < last_word - first_word + 1; i++) + le16_to_cpus(&eeprom_buff[i]); + + + memcpy(bytes, (uint8_t *)eeprom_buff + (eeprom->offset%2), + eeprom->len); + kfree(eeprom_buff); + return ret_val; } static int -e1000_ethtool_seeprom(struct e1000_adapter *adapter, - struct ethtool_eeprom *eeprom, void *user_data) +e1000_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, uint8_t *bytes) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; uint16_t *eeprom_buff; void *ptr; int max_len, first_word, last_word, ret_val = 0; + uint16_t i; if(eeprom->len == 0) return -EOPNOTSUPP; @@ -404,9 +493,6 @@ max_len = hw->eeprom.word_size * 2; - if((eeprom->offset + eeprom->len) > max_len) - eeprom->len = (max_len - eeprom->offset); - first_word = eeprom->offset >> 1; last_word = (eeprom->offset + eeprom->len - 1) >> 1; eeprom_buff = kmalloc(max_len, GFP_KERNEL); @@ -428,10 +514,14 @@ ret_val = e1000_read_eeprom(hw, last_word, 1, &eeprom_buff[last_word - first_word]); } - if((ret_val != 0) || copy_from_user(ptr, user_data, eeprom->len)) { - ret_val = -EFAULT; - goto seeprom_error; - } + + /* Device's eeprom is always little-endian, word addressable */ + for (i = 0; i < last_word - first_word + 1; i++) + le16_to_cpus(&eeprom_buff[i]); + + memcpy(ptr, bytes, eeprom->len); + for (i = 0; i < last_word - first_word + 1; i++) + eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]); ret_val = e1000_write_eeprom(hw, first_word, last_word - first_word + 1, eeprom_buff); @@ -440,11 +530,107 @@ if((ret_val == 0) && first_word <= EEPROM_CHECKSUM_REG) e1000_update_eeprom_checksum(hw); -seeprom_error: kfree(eeprom_buff); return ret_val; } +static void +e1000_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct e1000_adapter *adapter = netdev->priv; + + strncpy(drvinfo->driver, e1000_driver_name, 32); + strncpy(drvinfo->version, e1000_driver_version, 32); + strncpy(drvinfo->fw_version, "N/A", 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + drvinfo->n_stats = E1000_STATS_LEN; + drvinfo->testinfo_len = E1000_TEST_LEN; + drvinfo->regdump_len = e1000_get_regs_len(netdev); + drvinfo->eedump_len = e1000_get_eeprom_len(netdev); +} + +static void +e1000_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + struct e1000_adapter *adapter = netdev->priv; + e1000_mac_type mac_type = adapter->hw.mac_type; + struct e1000_desc_ring *txdr = &adapter->tx_ring; + struct e1000_desc_ring *rxdr = &adapter->rx_ring; + + ring->rx_max_pending = (mac_type < e1000_82544) ? E1000_MAX_RXD : + E1000_MAX_82544_RXD; + ring->tx_max_pending = (mac_type < e1000_82544) ? E1000_MAX_TXD : + E1000_MAX_82544_TXD; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = rxdr->count; + ring->tx_pending = txdr->count; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +} + +static int +e1000_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + int err; + struct e1000_adapter *adapter = netdev->priv; + e1000_mac_type mac_type = adapter->hw.mac_type; + struct e1000_desc_ring *txdr = &adapter->tx_ring; + struct e1000_desc_ring *rxdr = &adapter->rx_ring; + struct e1000_desc_ring tx_old, tx_new; + struct e1000_desc_ring rx_old, rx_new; + + tx_old = adapter->tx_ring; + rx_old = adapter->rx_ring; + + if(netif_running(adapter->netdev)) + e1000_down(adapter); + + rxdr->count = max(ring->rx_pending,(uint32_t)E1000_MIN_RXD); + rxdr->count = min(rxdr->count,(uint32_t)(mac_type < e1000_82544 ? + E1000_MAX_RXD : E1000_MAX_82544_RXD)); + E1000_ROUNDUP(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE); + + txdr->count = max(ring->tx_pending,(uint32_t)E1000_MIN_TXD); + txdr->count = min(txdr->count,(uint32_t)(mac_type < e1000_82544 ? + E1000_MAX_TXD : E1000_MAX_82544_TXD)); + E1000_ROUNDUP(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); + + if(netif_running(adapter->netdev)) { + /* try to get new resources before deleting old */ + if((err = e1000_setup_rx_resources(adapter))) + goto err_setup_rx; + if((err = e1000_setup_tx_resources(adapter))) + goto err_setup_tx; + + /* save the new, restore the old in order to free it, + * then restore the new back again */ + + rx_new = adapter->rx_ring; + tx_new = adapter->tx_ring; + adapter->rx_ring = rx_old; + adapter->tx_ring = tx_old; + e1000_free_rx_resources(adapter); + e1000_free_tx_resources(adapter); + adapter->rx_ring = rx_new; + adapter->tx_ring = tx_new; + if((err = e1000_up(adapter))) + return err; + } + return 0; +err_setup_tx: + e1000_free_rx_resources(adapter); +err_setup_rx: + adapter->rx_ring = rx_old; + adapter->tx_ring = tx_old; + e1000_up(adapter); + return err; +} + + #define REG_PATTERN_TEST(R, M, W) \ { \ uint32_t pat, value; \ @@ -535,6 +721,7 @@ for(i = 0; i < E1000_MC_TBL_SIZE; i++) REG_PATTERN_TEST(MTA + (i << 2), 0xFFFFFFFF, 0xFFFFFFFF); + *data = 0; return 0; } @@ -846,8 +1033,6 @@ e1000_write_phy_reg(&adapter->hw, 30, 0x8FFC); e1000_write_phy_reg(&adapter->hw, 29, 0x001A); e1000_write_phy_reg(&adapter->hw, 30, 0x8FF0); - - return; } static void @@ -1164,15 +1349,27 @@ return *data; } -static int -e1000_ethtool_test(struct e1000_adapter *adapter, +static int +e1000_diag_test_count(struct net_device *netdev) +{ + return E1000_TEST_LEN; +} + +static void +e1000_diag_test(struct net_device *netdev, struct ethtool_test *eth_test, uint64_t *data) { - boolean_t if_running = netif_running(adapter->netdev); + struct e1000_adapter *adapter = netdev->priv; + boolean_t if_running = netif_running(netdev); if(eth_test->flags == ETH_TEST_FL_OFFLINE) { /* Offline tests */ + /* save speed, duplex, autoneg settings */ + uint16_t autoneg_advertised = adapter->hw.autoneg_advertised; + uint8_t forced_speed_duplex = adapter->hw.forced_speed_duplex; + uint8_t autoneg = adapter->hw.autoneg; + /* Link test performed before hardware reset so autoneg doesn't * interfere with test result */ if(e1000_link_test(adapter, &data[4])) @@ -1198,6 +1395,10 @@ if(e1000_loopback_test(adapter, &data[3])) eth_test->flags |= ETH_TEST_FL_FAILED; + /* restore Autoneg/speed/duplex settings */ + adapter->hw.autoneg_advertised = autoneg_advertised; + adapter->hw.forced_speed_duplex = forced_speed_duplex; + adapter->hw.autoneg = autoneg; e1000_reset(adapter); if(if_running) e1000_up(adapter); @@ -1212,12 +1413,12 @@ data[2] = 0; data[3] = 0; } - return 0; } static void -e1000_ethtool_gwol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) +e1000_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; switch(adapter->hw.device_id) { @@ -1257,8 +1458,9 @@ } static int -e1000_ethtool_swol(struct e1000_adapter *adapter, struct ethtool_wolinfo *wol) +e1000_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { + struct e1000_adapter *adapter = netdev->priv; struct e1000_hw *hw = &adapter->hw; switch(adapter->hw.device_id) { @@ -1294,7 +1496,6 @@ return 0; } - /* toggle LED 4 times per second = 2 "blinks" per second */ #define E1000_ID_INTERVAL (HZ/4) @@ -1315,8 +1516,13 @@ } static int -e1000_ethtool_led_blink(struct e1000_adapter *adapter, struct ethtool_value *id) +e1000_phys_id(struct net_device *netdev, uint32_t data) { + struct e1000_adapter *adapter = netdev->priv; + + if(!data || data > (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ)) + data = (uint32_t)(MAX_SCHEDULE_TIMEOUT / HZ); + if(!adapter->blink_timer.function) { init_timer(&adapter->blink_timer); adapter->blink_timer.function = e1000_led_blink_callback; @@ -1327,11 +1533,8 @@ mod_timer(&adapter->blink_timer, jiffies); set_current_state(TASK_INTERRUPTIBLE); - if(id->data) - schedule_timeout(id->data * HZ); - else - schedule_timeout(MAX_SCHEDULE_TIMEOUT); + schedule_timeout(data * HZ); del_timer_sync(&adapter->blink_timer); e1000_led_off(&adapter->hw); clear_bit(E1000_LED_ON, &adapter->led_status); @@ -1340,329 +1543,96 @@ return 0; } -int -e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr) +static int +e1000_nway_reset(struct net_device *netdev) { struct e1000_adapter *adapter = netdev->priv; - void *addr = ifr->ifr_data; - uint32_t cmd; - - if(get_user(cmd, (uint32_t *) addr)) - return -EFAULT; - - switch(cmd) { - case ETHTOOL_GSET: { - struct ethtool_cmd ecmd = {ETHTOOL_GSET}; - e1000_ethtool_gset(adapter, &ecmd); - if(copy_to_user(addr, &ecmd, sizeof(ecmd))) - return -EFAULT; - return 0; - } - case ETHTOOL_SSET: { - struct ethtool_cmd ecmd; - if(copy_from_user(&ecmd, addr, sizeof(ecmd))) - return -EFAULT; - return e1000_ethtool_sset(adapter, &ecmd); - } - case ETHTOOL_GDRVINFO: { - struct ethtool_drvinfo drvinfo = {ETHTOOL_GDRVINFO}; - e1000_ethtool_gdrvinfo(adapter, &drvinfo); - if(copy_to_user(addr, &drvinfo, sizeof(drvinfo))) - return -EFAULT; - return 0; - } - case ETHTOOL_GSTRINGS: { - struct ethtool_gstrings gstrings = { ETHTOOL_GSTRINGS }; - char *strings = NULL; - int err = 0; - - if(copy_from_user(&gstrings, addr, sizeof(gstrings))) - return -EFAULT; - switch(gstrings.string_set) { - case ETH_SS_TEST: - gstrings.len = E1000_TEST_LEN; - strings = kmalloc(E1000_TEST_LEN * ETH_GSTRING_LEN, - GFP_KERNEL); - if(!strings) - return -ENOMEM; - memcpy(strings, e1000_gstrings_test, E1000_TEST_LEN * - ETH_GSTRING_LEN); - break; - case ETH_SS_STATS: { - int i; - gstrings.len = E1000_STATS_LEN; - strings = kmalloc(E1000_STATS_LEN * ETH_GSTRING_LEN, - GFP_KERNEL); - if(!strings) - return -ENOMEM; - for(i=0; i < E1000_STATS_LEN; i++) { - memcpy(&strings[i * ETH_GSTRING_LEN], - e1000_gstrings_stats[i].stat_string, - ETH_GSTRING_LEN); - } - break; - } - default: - return -EOPNOTSUPP; - } - if(copy_to_user(addr, &gstrings, sizeof(gstrings))) - err = -EFAULT; - addr += offsetof(struct ethtool_gstrings, data); - if(!err && copy_to_user(addr, strings, - gstrings.len * ETH_GSTRING_LEN)) - err = -EFAULT; - - kfree(strings); - return err; - } - case ETHTOOL_GREGS: { - struct ethtool_regs regs = {ETHTOOL_GREGS}; - uint32_t regs_buff[E1000_REGS_LEN]; - - if(copy_from_user(®s, addr, sizeof(regs))) - return -EFAULT; - e1000_ethtool_gregs(adapter, ®s, regs_buff); - if(copy_to_user(addr, ®s, sizeof(regs))) - return -EFAULT; - - addr += offsetof(struct ethtool_regs, data); - if(copy_to_user(addr, regs_buff, regs.len)) - return -EFAULT; - - return 0; - } - case ETHTOOL_NWAY_RST: { - if(netif_running(netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } - return 0; - } - case ETHTOOL_PHYS_ID: { - struct ethtool_value id; - if(copy_from_user(&id, addr, sizeof(id))) - return -EFAULT; - return e1000_ethtool_led_blink(adapter, &id); - } - case ETHTOOL_GLINK: { - struct ethtool_value link = {ETHTOOL_GLINK}; - link.data = netif_carrier_ok(netdev); - if(copy_to_user(addr, &link, sizeof(link))) - return -EFAULT; - return 0; - } - case ETHTOOL_GWOL: { - struct ethtool_wolinfo wol = {ETHTOOL_GWOL}; - e1000_ethtool_gwol(adapter, &wol); - if(copy_to_user(addr, &wol, sizeof(wol)) != 0) - return -EFAULT; - return 0; - } - case ETHTOOL_SWOL: { - struct ethtool_wolinfo wol; - if(copy_from_user(&wol, addr, sizeof(wol)) != 0) - return -EFAULT; - return e1000_ethtool_swol(adapter, &wol); - } - case ETHTOOL_GEEPROM: { - struct ethtool_eeprom eeprom = {ETHTOOL_GEEPROM}; - struct e1000_hw *hw = &adapter->hw; - uint16_t *eeprom_buff; - void *ptr; - int err = 0; - - if(copy_from_user(&eeprom, addr, sizeof(eeprom))) - return -EFAULT; - - eeprom_buff = kmalloc(hw->eeprom.word_size * 2, GFP_KERNEL); - - if(!eeprom_buff) - return -ENOMEM; - - if((err = e1000_ethtool_geeprom(adapter, &eeprom, - eeprom_buff))) - goto err_geeprom_ioctl; - - if(copy_to_user(addr, &eeprom, sizeof(eeprom))) { - err = -EFAULT; - goto err_geeprom_ioctl; - } - - addr += offsetof(struct ethtool_eeprom, data); - ptr = ((void *)eeprom_buff) + (eeprom.offset & 1); - - if(copy_to_user(addr, ptr, eeprom.len)) - err = -EFAULT; - -err_geeprom_ioctl: - kfree(eeprom_buff); - return err; - } - case ETHTOOL_SEEPROM: { - struct ethtool_eeprom eeprom; - - if(copy_from_user(&eeprom, addr, sizeof(eeprom))) - return -EFAULT; - - addr += offsetof(struct ethtool_eeprom, data); - return e1000_ethtool_seeprom(adapter, &eeprom, addr); - } - case ETHTOOL_GPAUSEPARAM: { - struct ethtool_pauseparam epause = {ETHTOOL_GPAUSEPARAM}; - e1000_ethtool_gpause(adapter, &epause); - if(copy_to_user(addr, &epause, sizeof(epause))) - return -EFAULT; - return 0; - } - case ETHTOOL_SPAUSEPARAM: { - struct ethtool_pauseparam epause; - if(copy_from_user(&epause, addr, sizeof(epause))) - return -EFAULT; - return e1000_ethtool_spause(adapter, &epause); - } - case ETHTOOL_GSTATS: { - struct { - struct ethtool_stats eth_stats; - uint64_t data[E1000_STATS_LEN]; - } stats = { {ETHTOOL_GSTATS, E1000_STATS_LEN} }; - int i; - - e1000_update_stats(adapter); - for(i = 0; i < E1000_STATS_LEN; i++) - stats.data[i] = (e1000_gstrings_stats[i].sizeof_stat == - sizeof(uint64_t)) ? - *(uint64_t *)((char *)adapter + - e1000_gstrings_stats[i].stat_offset) : - *(uint32_t *)((char *)adapter + - e1000_gstrings_stats[i].stat_offset); - if(copy_to_user(addr, &stats, sizeof(stats))) - return -EFAULT; - return 0; - } - case ETHTOOL_TEST: { - struct { - struct ethtool_test eth_test; - uint64_t data[E1000_TEST_LEN]; - } test = { {ETHTOOL_TEST} }; - int err; - - if(copy_from_user(&test.eth_test, addr, sizeof(test.eth_test))) - return -EFAULT; - - test.eth_test.len = E1000_TEST_LEN; - - if((err = e1000_ethtool_test(adapter, &test.eth_test, - test.data))) - return err; - - if(copy_to_user(addr, &test, sizeof(test)) != 0) - return -EFAULT; - return 0; + if(netif_running(netdev)) { + e1000_down(adapter); + e1000_up(adapter); } - case ETHTOOL_GRXCSUM: { - struct ethtool_value edata = { ETHTOOL_GRXCSUM }; + return 0; +} - edata.data = adapter->rx_csum; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_SRXCSUM: { - struct ethtool_value edata; +static int +e1000_get_stats_count(struct net_device *netdev) +{ + return E1000_STATS_LEN; +} - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - adapter->rx_csum = edata.data; - if(netif_running(netdev)) { - e1000_down(adapter); - e1000_up(adapter); - } else - e1000_reset(adapter); - return 0; - } - case ETHTOOL_GTXCSUM: { - struct ethtool_value edata = { ETHTOOL_GTXCSUM }; +static void +e1000_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, uint64_t *data) +{ + struct e1000_adapter *adapter = netdev->priv; + int i; - edata.data = - (netdev->features & NETIF_F_HW_CSUM) != 0; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; + e1000_update_stats(adapter); + for(i = 0; i < E1000_STATS_LEN; i++) { + char *p = (char *)adapter+e1000_gstrings_stats[i].stat_offset; + data[i] = (e1000_gstrings_stats[i].sizeof_stat == sizeof(uint64_t)) + ? *(uint64_t *)p : *(uint32_t *)p; } - case ETHTOOL_STXCSUM: { - struct ethtool_value edata; +} - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; +static void +e1000_get_strings(struct net_device *netdev, uint32_t stringset, uint8_t *data) +{ + int i; - if(adapter->hw.mac_type < e1000_82543) { - if (edata.data != 0) - return -EINVAL; - return 0; + switch(stringset) { + case ETH_SS_TEST: + memcpy(data, *e1000_gstrings_test, + E1000_TEST_LEN*ETH_GSTRING_LEN); + break; + case ETH_SS_STATS: + for (i=0; i < E1000_STATS_LEN; i++) { + memcpy(data + i * ETH_GSTRING_LEN, + e1000_gstrings_stats[i].stat_string, + ETH_GSTRING_LEN); } - - if (edata.data) - netdev->features |= NETIF_F_HW_CSUM; - else - netdev->features &= ~NETIF_F_HW_CSUM; - - return 0; - } - case ETHTOOL_GSG: { - struct ethtool_value edata = { ETHTOOL_GSG }; - - edata.data = - (netdev->features & NETIF_F_SG) != 0; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; + break; } - case ETHTOOL_SSG: { - struct ethtool_value edata; - - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - - if (edata.data) - netdev->features |= NETIF_F_SG; - else - netdev->features &= ~NETIF_F_SG; +} - return 0; - } +struct ethtool_ops e1000_ethtool_ops = { + .get_settings = e1000_get_settings, + .set_settings = e1000_set_settings, + .get_drvinfo = e1000_get_drvinfo, + .get_regs_len = e1000_get_regs_len, + .get_regs = e1000_get_regs, + .get_wol = e1000_get_wol, + .set_wol = e1000_set_wol, + .get_msglevel = e1000_get_msglevel, + .set_msglevel = e1000_set_msglevel, + .nway_reset = e1000_nway_reset, + .get_link = ethtool_op_get_link, + .get_eeprom_len = e1000_get_eeprom_len, + .get_eeprom = e1000_get_eeprom, + .set_eeprom = e1000_set_eeprom, + .get_ringparam = e1000_get_ringparam, + .set_ringparam = e1000_set_ringparam, + .get_pauseparam = e1000_get_pauseparam, + .set_pauseparam = e1000_set_pauseparam, + .get_rx_csum = e1000_get_rx_csum, + .set_rx_csum = e1000_set_rx_csum, + .get_tx_csum = e1000_get_tx_csum, + .set_tx_csum = e1000_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = ethtool_op_set_sg, #ifdef NETIF_F_TSO - case ETHTOOL_GTSO: { - struct ethtool_value edata = { ETHTOOL_GTSO }; - - edata.data = (netdev->features & NETIF_F_TSO) != 0; - if (copy_to_user(addr, &edata, sizeof(edata))) - return -EFAULT; - return 0; - } - case ETHTOOL_STSO: { - struct ethtool_value edata; - - if (copy_from_user(&edata, addr, sizeof(edata))) - return -EFAULT; - - if ((adapter->hw.mac_type < e1000_82544) || - (adapter->hw.mac_type == e1000_82547)) { - if (edata.data != 0) - return -EINVAL; - return 0; - } - - if (edata.data) - netdev->features |= NETIF_F_TSO; - else - netdev->features &= ~NETIF_F_TSO; - - return 0; - } + .get_tso = ethtool_op_get_tso, + .set_tso = e1000_set_tso, #endif - default: - return -EOPNOTSUPP; - } -} - + .self_test_count = e1000_diag_test_count, + .self_test = e1000_diag_test, + .get_strings = e1000_get_strings, + .phys_id = e1000_phys_id, + .get_stats_count = e1000_get_stats_count, + .get_ethtool_stats = e1000_get_ethtool_stats, +}; +void set_ethtool_ops(struct net_device *netdev) +{ + SET_ETHTOOL_OPS(netdev, &e1000_ethtool_ops); +} diff -urN linux-2.4.26/drivers/net/e1000/e1000_hw.c linux-2.4.27/drivers/net/e1000/e1000_hw.c --- linux-2.4.26/drivers/net/e1000/e1000_hw.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000_hw.c 2004-08-07 16:26:05.101365145 -0700 @@ -65,6 +65,7 @@ static void e1000_standby_eeprom(struct e1000_hw *hw); static int32_t e1000_id_led_init(struct e1000_hw * hw); static int32_t e1000_set_vco_speed(struct e1000_hw *hw); +static int32_t e1000_set_phy_mode(struct e1000_hw *hw); /* IGP cable length table */ static const @@ -264,6 +265,17 @@ return -E1000_ERR_MAC_TYPE; } + switch(hw->mac_type) { + case e1000_82541: + case e1000_82547: + case e1000_82541_rev_2: + case e1000_82547_rev_2: + hw->asf_firmware_present = TRUE; + break; + default: + break; + } + return E1000_SUCCESS; } @@ -469,11 +481,11 @@ uint16_t pcix_stat_hi_word; uint16_t cmd_mmrbc; uint16_t stat_mmrbc; - DEBUGFUNC("e1000_init_hw"); /* Initialize Identification LED */ - if((ret_val = e1000_id_led_init(hw))) { + ret_val = e1000_id_led_init(hw); + if(ret_val) { DEBUGOUT("Error Initializing Identification LED\n"); return ret_val; } @@ -594,16 +606,16 @@ return E1000_SUCCESS; } - if ((ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, - &eeprom_data))) { + ret_val = e1000_read_eeprom(hw, EEPROM_SERDES_AMPLITUDE, 1, &eeprom_data); + if (ret_val) { return ret_val; } if(eeprom_data != EEPROM_RESERVED_WORD) { /* Adjust SERDES output amplitude only. */ eeprom_data &= EEPROM_SERDES_AMPLITUDE_MASK; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, - eeprom_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_EXT_CTRL, eeprom_data); + if(ret_val) return ret_val; } @@ -752,14 +764,16 @@ if(hw->media_type == e1000_media_type_fiber) signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; - if((ret_val = e1000_adjust_serdes_amplitude(hw))) + ret_val = e1000_adjust_serdes_amplitude(hw); + if(ret_val) return ret_val; /* Take the link out of reset */ ctrl &= ~(E1000_CTRL_LRST); /* Adjust VCO speed to improve BER performance */ - if((ret_val = e1000_set_vco_speed(hw))) + ret_val = e1000_set_vco_speed(hw); + if(ret_val) return ret_val; e1000_config_collision_dist(hw); @@ -846,7 +860,8 @@ * we detect a signal. This will allow us to communicate with * non-autonegotiating link partners. */ - if((ret_val = e1000_check_for_link(hw))) { + ret_val = e1000_check_for_link(hw); + if(ret_val) { DEBUGOUT("Error while checking for link\n"); return ret_val; } @@ -893,12 +908,24 @@ } /* Make sure we have a valid PHY */ - if((ret_val = e1000_detect_gig_phy(hw))) { + ret_val = e1000_detect_gig_phy(hw); + if(ret_val) { DEBUGOUT("Error, did not detect valid phy.\n"); return ret_val; } DEBUGOUT1("Phy ID = %x \n", hw->phy_id); + /* Set PHY to class A mode (if necessary) */ + ret_val = e1000_set_phy_mode(hw); + if(ret_val) + return ret_val; + + if(hw->mac_type == e1000_82545_rev_3) { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + phy_data |= 0x00000008; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + } + if(hw->mac_type <= e1000_82543 || hw->mac_type == e1000_82541 || hw->mac_type == e1000_82547 || hw->mac_type == e1000_82541_rev_2 || hw->mac_type == e1000_82547_rev_2) @@ -907,7 +934,8 @@ if(!hw->phy_reset_disable) { if (hw->phy_type == e1000_phy_igp) { - if((ret_val = e1000_phy_reset(hw))) { + ret_val = e1000_phy_reset(hw); + if(ret_val) { DEBUGOUT("Error Resetting the PHY\n"); return ret_val; } @@ -922,14 +950,16 @@ E1000_WRITE_REG(hw, LEDCTL, led_ctrl); /* disable lplu d3 during driver init */ - if((ret_val = e1000_set_d3_lplu_state(hw, FALSE))) { + ret_val = e1000_set_d3_lplu_state(hw, FALSE); + if(ret_val) { DEBUGOUT("Error Disabling LPLU D3\n"); return ret_val; } /* Configure mdi-mdix settings */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, + &phy_data); + if(ret_val) return ret_val; if((hw->mac_type == e1000_82541) || (hw->mac_type == e1000_82547)) { @@ -956,8 +986,9 @@ break; } } - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, + phy_data); + if(ret_val) return ret_val; /* set auto-master slave resolution settings */ @@ -975,27 +1006,28 @@ * resolution as hardware default. */ if(hw->autoneg_advertised == ADVERTISE_1000_FULL) { /* Disable SmartSpeed */ - if((ret_val = e1000_read_phy_reg(hw, - IGP01E1000_PHY_PORT_CONFIG, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, + &phy_data); + if(ret_val) return ret_val; phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - if((ret_val = e1000_write_phy_reg(hw, - IGP01E1000_PHY_PORT_CONFIG, - phy_data))) + ret_val = e1000_write_phy_reg(hw, + IGP01E1000_PHY_PORT_CONFIG, + phy_data); + if(ret_val) return ret_val; /* Set auto Master/Slave resolution process */ - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if(ret_val) return ret_val; phy_data &= ~CR_1000T_MS_ENABLE; - if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if(ret_val) return ret_val; } - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &phy_data); + if(ret_val) return ret_val; /* load defaults for future use */ @@ -1018,14 +1050,15 @@ default: break; } - if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, phy_data); + if(ret_val) return ret_val; } } else { /* Enable CRS on TX. This must be set for half-duplex operation. */ - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) return ret_val; phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; @@ -1064,15 +1097,17 @@ phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; if(hw->disable_polarity_correction == 1) phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + if(ret_val) return ret_val; /* Force TX_CLK in the Extended PHY Specific Control Register * to 25MHz clock. */ - if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) return ret_val; phy_data |= M88E1000_EPSCR_TX_CLK_25; @@ -1083,14 +1118,15 @@ M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); - if((ret_val = e1000_write_phy_reg(hw, - M88E1000_EXT_PHY_SPEC_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if(ret_val) return ret_val; } /* SW Reset the PHY so all changes take effect */ - if((ret_val = e1000_phy_reset(hw))) { + ret_val = e1000_phy_reset(hw); + if(ret_val) { DEBUGOUT("Error Resetting the PHY\n"); return ret_val; } @@ -1124,7 +1160,8 @@ hw->autoneg_advertised = AUTONEG_ADVERTISE_SPEED_DEFAULT; DEBUGOUT("Reconfiguring auto-neg advertisement params\n"); - if((ret_val = e1000_phy_setup_autoneg(hw))) { + ret_val = e1000_phy_setup_autoneg(hw); + if(ret_val) { DEBUGOUT("Error Setting up Auto-Negotiation\n"); return ret_val; } @@ -1133,18 +1170,21 @@ /* Restart auto-negotiation by setting the Auto Neg Enable bit and * the Auto Neg Restart bit in the PHY control register. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if(ret_val) return ret_val; phy_data |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); - if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data))) + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if(ret_val) return ret_val; /* Does the user want to wait for Auto-Neg to complete here, or * check at a later time (for example, callback routine). */ if(hw->wait_autoneg_complete) { - if((ret_val = e1000_wait_autoneg(hw))) { + ret_val = e1000_wait_autoneg(hw); + if(ret_val) { DEBUGOUT("Error while waiting for autoneg to complete\n"); return ret_val; } @@ -1152,7 +1192,8 @@ hw->get_link_status = TRUE; } else { DEBUGOUT("Forcing speed and duplex\n"); - if((ret_val = e1000_phy_force_speed_duplex(hw))) { + ret_val = e1000_phy_force_speed_duplex(hw); + if(ret_val) { DEBUGOUT("Error Forcing Speed and Duplex\n"); return ret_val; } @@ -1163,9 +1204,11 @@ * valid. */ for(i = 0; i < 10; i++) { - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; if(phy_data & MII_SR_LINK_STATUS) { @@ -1180,19 +1223,22 @@ if(hw->mac_type >= e1000_82544) { e1000_config_collision_dist(hw); } else { - if((ret_val = e1000_config_mac_to_phy(hw))) { + ret_val = e1000_config_mac_to_phy(hw); + if(ret_val) { DEBUGOUT("Error configuring MAC to PHY settings\n"); return ret_val; } } - if((ret_val = e1000_config_fc_after_link_up(hw))) { + ret_val = e1000_config_fc_after_link_up(hw); + if(ret_val) { DEBUGOUT("Error Configuring Flow Control\n"); return ret_val; } DEBUGOUT("Valid link established!!!\n"); if(hw->phy_type == e1000_phy_igp) { - if((ret_val = e1000_config_dsp_after_link_change(hw, TRUE))) { + ret_val = e1000_config_dsp_after_link_change(hw, TRUE); + if(ret_val) { DEBUGOUT("Error Configuring DSP after link up\n"); return ret_val; } @@ -1222,12 +1268,13 @@ DEBUGFUNC("e1000_phy_setup_autoneg"); /* Read the MII Auto-Neg Advertisement Register (Address 4). */ - if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, - &mii_autoneg_adv_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg); + if(ret_val) return ret_val; /* Read the MII 1000Base-T Control Register (Address 9). */ - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_CTRL, &mii_1000t_ctrl_reg); + if(ret_val) return ret_val; /* Need to parse both autoneg_advertised and fc and set up @@ -1334,13 +1381,14 @@ return -E1000_ERR_CONFIG; } - if((ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, - mii_autoneg_adv_reg))) + ret_val = e1000_write_phy_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg); + if(ret_val) return ret_val; DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg); - if((ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg))) + ret_val = e1000_write_phy_reg(hw, PHY_1000T_CTRL, mii_1000t_ctrl_reg); + if(ret_val) return ret_val; return E1000_SUCCESS; @@ -1379,7 +1427,8 @@ ctrl &= ~E1000_CTRL_ASDE; /* Read the MII Control Register. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &mii_ctrl_reg); + if(ret_val) return ret_val; /* We need to disable autoneg in order to force link and duplex. */ @@ -1426,16 +1475,16 @@ E1000_WRITE_REG(hw, CTRL, ctrl); if (hw->phy_type == e1000_phy_m88) { - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) return ret_val; /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI * forced whenever speed are duplex are forced. */ phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if(ret_val) return ret_val; DEBUGOUT1("M88E1000 PSCR: %x \n", phy_data); @@ -1446,20 +1495,21 @@ /* Clear Auto-Crossover to force MDI manually. IGP requires MDI * forced whenever speed or duplex are forced. */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); + if(ret_val) return ret_val; phy_data &= ~IGP01E1000_PSCR_AUTO_MDIX; phy_data &= ~IGP01E1000_PSCR_FORCE_MDI_MDIX; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CTRL, phy_data); + if(ret_val) return ret_val; } /* Write back the modified PHY MII control register. */ - if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg))) + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, mii_ctrl_reg); + if(ret_val) return ret_val; udelay(1); @@ -1481,10 +1531,12 @@ /* Read the MII Status Register and wait for Auto-Neg Complete bit * to be set. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) return ret_val; if(mii_status_reg & MII_SR_LINK_STATUS) break; @@ -1492,7 +1544,8 @@ } if((i == 0) && (hw->phy_type == e1000_phy_m88)) { /* We didn't get link. Reset the DSP and wait again for link. */ - if((ret_val = e1000_phy_reset_dsp(hw))) { + ret_val = e1000_phy_reset_dsp(hw); + if(ret_val) { DEBUGOUT("Error Resetting PHY DSP\n"); return ret_val; } @@ -1504,10 +1557,12 @@ /* Read the MII Status Register and wait for Auto-Neg Complete bit * to be set. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) return ret_val; } } @@ -1517,46 +1572,26 @@ * Extended PHY Specific Control Register to 25MHz clock. This value * defaults back to a 2.5MHz clock when the PHY is reset. */ - if((ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); + if(ret_val) return ret_val; phy_data |= M88E1000_EPSCR_TX_CLK_25; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_EXT_PHY_SPEC_CTRL, phy_data); + if(ret_val) return ret_val; /* In addition, because of the s/w reset above, we need to enable CRS on * TX. This must be set for both full and half duplex operation. */ - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) return ret_val; phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, - phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if(ret_val) return ret_val; - - /* Polarity reversal workaround for forced 10F/10H links. */ - if(hw->mac_type <= e1000_82544 && - (hw->forced_speed_duplex == e1000_10_full || - hw->forced_speed_duplex == e1000_10_half)) { - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, - 0x0019))) - return ret_val; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, - 0x8F0F))) - return ret_val; - /* IEEE requirement is 150ms */ - msec_delay(200); - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, - 0x0019))) - return ret_val; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, - 0x8F00))) - return ret_val; - } } return E1000_SUCCESS; } @@ -1614,8 +1649,9 @@ * registers depending on negotiated values. */ if (hw->phy_type == e1000_phy_igp) { - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, + &phy_data); + if(ret_val) return ret_val; if(phy_data & IGP01E1000_PSSR_FULL_DUPLEX) ctrl |= E1000_CTRL_FD; @@ -1633,8 +1669,9 @@ IGP01E1000_PSSR_SPEED_100MBPS) ctrl |= E1000_CTRL_SPD_100; } else { - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) return ret_val; if(phy_data & M88E1000_PSSR_DPLX) ctrl |= E1000_CTRL_FD; @@ -1752,7 +1789,8 @@ if(((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) || ((hw->media_type == e1000_media_type_internal_serdes) && (hw->autoneg_failed)) || ((hw->media_type == e1000_media_type_copper) && (!hw->autoneg))) { - if((ret_val = e1000_force_mac_fc(hw))) { + ret_val = e1000_force_mac_fc(hw); + if(ret_val) { DEBUGOUT("Error forcing flow control settings\n"); return ret_val; } @@ -1768,9 +1806,11 @@ * has completed. We read this twice because this reg has * some "sticky" (latched) bits. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &mii_status_reg); + if(ret_val) return ret_val; if(mii_status_reg & MII_SR_AUTONEG_COMPLETE) { @@ -1780,11 +1820,13 @@ * Register (Address 5) to determine how flow control was * negotiated. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, - &mii_nway_adv_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_ADV, + &mii_nway_adv_reg); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, - &mii_nway_lp_ability_reg))) + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, + &mii_nway_lp_ability_reg); + if(ret_val) return ret_val; /* Two bits in the Auto Negotiation Advertisement Register @@ -1901,7 +1943,8 @@ * negotiated to HALF DUPLEX, flow control should not be * enabled per IEEE 802.3 spec. */ - if((ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex))) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if(ret_val) { DEBUGOUT("Error getting link speed and duplex\n"); return ret_val; } @@ -1912,7 +1955,8 @@ /* Now we call a subroutine to actually force the MAC * controller to use the correct flow control settings. */ - if((ret_val = e1000_force_mac_fc(hw))) { + ret_val = e1000_force_mac_fc(hw); + if(ret_val) { DEBUGOUT("Error forcing flow control settings\n"); return ret_val; } @@ -1933,7 +1977,7 @@ int32_t e1000_check_for_link(struct e1000_hw *hw) { - uint32_t rxcw; + uint32_t rxcw = 0; uint32_t ctrl; uint32_t status; uint32_t rctl; @@ -1943,16 +1987,23 @@ DEBUGFUNC("e1000_check_for_link"); + ctrl = E1000_READ_REG(hw, CTRL); + status = E1000_READ_REG(hw, STATUS); + /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be * set when the optics detect a signal. On older adapters, it will be * cleared when there is a signal. This applies to fiber media only. */ - if(hw->media_type == e1000_media_type_fiber) - signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + if((hw->media_type == e1000_media_type_fiber) || + (hw->media_type == e1000_media_type_internal_serdes)) { + rxcw = E1000_READ_REG(hw, RXCW); - ctrl = E1000_READ_REG(hw, CTRL); - status = E1000_READ_REG(hw, STATUS); - rxcw = E1000_READ_REG(hw, RXCW); + if(hw->media_type == e1000_media_type_fiber) { + signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; + if(status & E1000_STATUS_LU) + hw->get_link_status = FALSE; + } + } /* If we have a copper PHY then we only want to go out to the PHY * registers to see if Auto-Neg has completed and/or if our link @@ -1966,9 +2017,11 @@ * of the PHY. * Read the register twice since the link bit is sticky. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; if(phy_data & MII_SR_LINK_STATUS) { @@ -2002,7 +2055,8 @@ if(hw->mac_type >= e1000_82544) e1000_config_collision_dist(hw); else { - if((ret_val = e1000_config_mac_to_phy(hw))) { + ret_val = e1000_config_mac_to_phy(hw); + if(ret_val) { DEBUGOUT("Error configuring MAC to PHY settings\n"); return ret_val; } @@ -2012,7 +2066,8 @@ * need to restore the desired flow control settings because we may * have had to re-autoneg with a different link partner. */ - if((ret_val = e1000_config_fc_after_link_up(hw))) { + ret_val = e1000_config_fc_after_link_up(hw); + if(ret_val) { DEBUGOUT("Error configuring flow control\n"); return ret_val; } @@ -2061,8 +2116,8 @@ * in. The autoneg_failed flag does this. */ else if((((hw->media_type == e1000_media_type_fiber) && - ((ctrl & E1000_CTRL_SWDPIN1) == signal)) || - (hw->media_type == e1000_media_type_internal_serdes)) && + ((ctrl & E1000_CTRL_SWDPIN1) == signal)) || + (hw->media_type == e1000_media_type_internal_serdes)) && (!(status & E1000_STATUS_LU)) && (!(rxcw & E1000_RXCW_C))) { if(hw->autoneg_failed == 0) { @@ -2080,7 +2135,8 @@ E1000_WRITE_REG(hw, CTRL, ctrl); /* Configure Flow Control after forcing link up. */ - if((ret_val = e1000_config_fc_after_link_up(hw))) { + ret_val = e1000_config_fc_after_link_up(hw); + if(ret_val) { DEBUGOUT("Error configuring flow control\n"); return ret_val; } @@ -2092,8 +2148,7 @@ */ else if(((hw->media_type == e1000_media_type_fiber) || (hw->media_type == e1000_media_type_internal_serdes)) && - (ctrl & E1000_CTRL_SLU) && - (rxcw & E1000_RXCW_C)) { + (ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { DEBUGOUT("RXing /C/, enable AutoNeg and stop forcing link.\r\n"); E1000_WRITE_REG(hw, TXCW, hw->txcw); E1000_WRITE_REG(hw, CTRL, (ctrl & ~E1000_CTRL_SLU)); @@ -2173,13 +2228,15 @@ * match the duplex in the link partner's capabilities. */ if(hw->phy_type == e1000_phy_igp && hw->speed_downgraded) { - if((ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_AUTONEG_EXP, &phy_data); + if(ret_val) return ret_val; if(!(phy_data & NWAY_ER_LP_NWAY_CAPS)) *duplex = HALF_DUPLEX; else { - if((ret_val == e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_LP_ABILITY, &phy_data); + if(ret_val) return ret_val; if((*speed == SPEED_100 && !(phy_data & NWAY_LPAR_100TX_FD_CAPS)) || (*speed == SPEED_10 && !(phy_data & NWAY_LPAR_10T_FD_CAPS))) @@ -2210,9 +2267,11 @@ /* Read the MII Status Register and wait for Auto-Neg * Complete bit to be set. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; if(phy_data & MII_SR_AUTONEG_COMPLETE) { return E1000_SUCCESS; @@ -2377,8 +2436,9 @@ if(hw->phy_type == e1000_phy_igp && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { - if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, - (uint16_t)reg_addr))) + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr); + if(ret_val) return ret_val; } @@ -2480,8 +2540,9 @@ if(hw->phy_type == e1000_phy_igp && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { - if((ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, - (uint16_t)reg_addr))) + ret_val = e1000_write_phy_reg_ex(hw, IGP01E1000_PHY_PAGE_SELECT, + (uint16_t)reg_addr); + if(ret_val) return ret_val; } @@ -2620,11 +2681,13 @@ DEBUGFUNC("e1000_phy_reset"); if(hw->mac_type != e1000_82541_rev_2) { - if((ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data); + if(ret_val) return ret_val; phy_data |= MII_CR_RESET; - if((ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data))) + ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data); + if(ret_val) return ret_val; udelay(1); @@ -2651,12 +2714,14 @@ DEBUGFUNC("e1000_detect_gig_phy"); /* Read the PHY ID Registers to identify which PHY is onboard. */ - if((ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high))) + ret_val = e1000_read_phy_reg(hw, PHY_ID1, &phy_id_high); + if(ret_val) return ret_val; hw->phy_id = (uint32_t) (phy_id_high << 16); udelay(20); - if((ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low))) + ret_val = e1000_read_phy_reg(hw, PHY_ID2, &phy_id_low); + if(ret_val) return ret_val; hw->phy_id |= (uint32_t) (phy_id_low & PHY_REVISION_MASK); @@ -2708,9 +2773,12 @@ DEBUGFUNC("e1000_phy_reset_dsp"); do { - if((ret_val = e1000_write_phy_reg(hw, 29, 0x001d))) break; - if((ret_val = e1000_write_phy_reg(hw, 30, 0x00c1))) break; - if((ret_val = e1000_write_phy_reg(hw, 30, 0x0000))) break; + ret_val = e1000_write_phy_reg(hw, 29, 0x001d); + if(ret_val) break; + ret_val = e1000_write_phy_reg(hw, 30, 0x00c1); + if(ret_val) break; + ret_val = e1000_write_phy_reg(hw, 30, 0x0000); + if(ret_val) break; ret_val = E1000_SUCCESS; } while(0); @@ -2743,13 +2811,14 @@ phy_info->polarity_correction = e1000_polarity_reversal_enabled; /* Check polarity status */ - if((ret_val = e1000_check_polarity(hw, &polarity))) + ret_val = e1000_check_polarity(hw, &polarity); + if(ret_val) return ret_val; phy_info->cable_polarity = polarity; - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, &phy_data); + if(ret_val) return ret_val; phy_info->mdix_mode = (phy_data & IGP01E1000_PSSR_MDIX) >> @@ -2758,7 +2827,8 @@ if((phy_data & IGP01E1000_PSSR_SPEED_MASK) == IGP01E1000_PSSR_SPEED_1000MBPS) { /* Local/Remote Receiver Information are only valid at 1000 Mbps */ - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if(ret_val) return ret_val; phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >> @@ -2767,7 +2837,8 @@ SR_1000T_REMOTE_RX_STATUS_SHIFT; /* Get cable length */ - if((ret_val = e1000_get_cable_length(hw, &min_length, &max_length))) + ret_val = e1000_get_cable_length(hw, &min_length, &max_length); + if(ret_val) return ret_val; /* transalte to old method */ @@ -2807,7 +2878,8 @@ * and it stored in the hw->speed_downgraded parameter. */ phy_info->downshift = hw->speed_downgraded; - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) return ret_val; phy_info->extended_10bt_distance = @@ -2818,12 +2890,14 @@ M88E1000_PSCR_POLARITY_REVERSAL_SHIFT; /* Check polarity status */ - if((ret_val = e1000_check_polarity(hw, &polarity))) + ret_val = e1000_check_polarity(hw, &polarity); + if(ret_val) return ret_val; phy_info->cable_polarity = polarity; - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if(ret_val) return ret_val; phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >> @@ -2836,7 +2910,8 @@ phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >> M88E1000_PSSR_CABLE_LENGTH_SHIFT); - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data); + if(ret_val) return ret_val; phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >> @@ -2878,10 +2953,12 @@ return -E1000_ERR_CONFIG; } - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); + if(ret_val) return ret_val; if((phy_data & MII_SR_LINK_STATUS) != MII_SR_LINK_STATUS) { @@ -3344,6 +3421,7 @@ udelay(5); retry_count += 5; + e1000_standby_eeprom(hw); } while(retry_count < EEPROM_MAX_RETRY_SPI); /* ATMEL SPI write time could vary from 0-20mSec on 3.3V devices (and @@ -4112,12 +4190,14 @@ case e1000_82541_rev_2: case e1000_82547_rev_2: /* Turn off PHY Smart Power Down (if enabled) */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, - &hw->phy_spd_default))) - return ret_val; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, - (uint16_t)(hw->phy_spd_default & - ~IGP01E1000_GMII_SPD)))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, + &hw->phy_spd_default); + if(ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + (uint16_t)(hw->phy_spd_default & + ~IGP01E1000_GMII_SPD)); + if(ret_val) return ret_val; /* Fall Through */ default: @@ -4164,8 +4244,9 @@ case e1000_82541_rev_2: case e1000_82547_rev_2: /* Turn on PHY Smart Power Down (if previously enabled) */ - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, - hw->phy_spd_default))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, + hw->phy_spd_default); + if(ret_val) return ret_val; /* Fall Through */ default: @@ -4612,8 +4693,9 @@ /* Use old method for Phy older than IGP */ if(hw->phy_type == e1000_phy_m88) { - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) return ret_val; /* Convert the enum value to ranged values */ @@ -4652,7 +4734,8 @@ /* Read the AGC registers for all channels */ for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - if((ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data))) + ret_val = e1000_read_phy_reg(hw, agc_reg_array[i], &phy_data); + if(ret_val) return ret_val; cur_agc = phy_data >> IGP01E1000_AGC_LENGTH_SHIFT; @@ -4719,15 +4802,17 @@ if(hw->phy_type == e1000_phy_m88) { /* return the Polarity bit in the Status register. */ - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) return ret_val; *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >> M88E1000_PSSR_REV_POLARITY_SHIFT; } else if(hw->phy_type == e1000_phy_igp) { /* Read the Status register to check the speed */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_STATUS, + &phy_data); + if(ret_val) return ret_val; /* If speed is 1000 Mbps, must read the IGP01E1000_PHY_PCS_INIT_REG to @@ -4736,8 +4821,9 @@ IGP01E1000_PSSR_SPEED_1000MBPS) { /* Read the GIG initialization PCS register (0x00B4) */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PCS_INIT_REG, + &phy_data); + if(ret_val) return ret_val; /* Check the polarity bits */ @@ -4775,15 +4861,17 @@ DEBUGFUNC("e1000_check_downshift"); if(hw->phy_type == e1000_phy_igp) { - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_LINK_HEALTH, + &phy_data); + if(ret_val) return ret_val; hw->speed_downgraded = (phy_data & IGP01E1000_PLHR_SS_DOWNGRADE) ? 1 : 0; } else if(hw->phy_type == e1000_phy_m88) { - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) return ret_val; hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> @@ -4823,7 +4911,8 @@ return E1000_SUCCESS; if(link_up) { - if((ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex))) { + ret_val = e1000_get_speed_and_duplex(hw, &speed, &duplex); + if(ret_val) { DEBUGOUT("Error getting link speed and duplex\n"); return ret_val; } @@ -4836,14 +4925,16 @@ min_length >= e1000_igp_cable_length_50) { for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - if((ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], - &phy_data))) + ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], + &phy_data); + if(ret_val) return ret_val; phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; - if((ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i], - phy_data))) + ret_val = e1000_write_phy_reg(hw, dsp_reg_array[i], + phy_data); + if(ret_val) return ret_val; } hw->dsp_config_state = e1000_dsp_config_activated; @@ -4856,23 +4947,26 @@ uint32_t idle_errs = 0; /* clear previous idle error counts */ - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if(ret_val) return ret_val; for(i = 0; i < ffe_idle_err_timeout; i++) { udelay(1000); - if((ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, + &phy_data); + if(ret_val) return ret_val; idle_errs += (phy_data & SR_1000T_IDLE_ERROR_CNT); if(idle_errs > SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT) { hw->ffe_config_state = e1000_ffe_config_active; - if((ret_val = e1000_write_phy_reg(hw, + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, - IGP01E1000_PHY_DSP_FFE_CM_CP))) + IGP01E1000_PHY_DSP_FFE_CM_CP); + if(ret_val) return ret_val; break; } @@ -4884,43 +4978,87 @@ } } else { if(hw->dsp_config_state == e1000_dsp_config_activated) { - if((ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_FORCE_GIGA))) + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if(ret_val) return ret_val; for(i = 0; i < IGP01E1000_PHY_CHANNEL_NUM; i++) { - if((ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], - &phy_data))) + ret_val = e1000_read_phy_reg(hw, dsp_reg_array[i], &phy_data); + if(ret_val) return ret_val; phy_data &= ~IGP01E1000_PHY_EDAC_MU_INDEX; phy_data |= IGP01E1000_PHY_EDAC_SIGN_EXT_9_BITS; - if((ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i], - phy_data))) + ret_val = e1000_write_phy_reg(hw,dsp_reg_array[i], phy_data); + if(ret_val) return ret_val; } - if((ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_RESTART_AUTONEG))) + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if(ret_val) return ret_val; hw->dsp_config_state = e1000_dsp_config_enabled; } if(hw->ffe_config_state == e1000_ffe_config_active) { - if((ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_FORCE_GIGA))) + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_FORCE_GIGA); + if(ret_val) + return ret_val; + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, + IGP01E1000_PHY_DSP_FFE_DEFAULT); + if(ret_val) return ret_val; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_DSP_FFE, - IGP01E1000_PHY_DSP_FFE_DEFAULT))) + + ret_val = e1000_write_phy_reg(hw, 0x0000, + IGP01E1000_IEEE_RESTART_AUTONEG); + if(ret_val) return ret_val; + hw->ffe_config_state = e1000_ffe_config_enabled; + } + } + return E1000_SUCCESS; +} + +/***************************************************************************** + * Set PHY to class A mode + * Assumes the following operations will follow to enable the new class mode. + * 1. Do a PHY soft reset + * 2. Restart auto-negotiation or force link. + * + * hw - Struct containing variables accessed by shared code + ****************************************************************************/ +static int32_t +e1000_set_phy_mode(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t eeprom_data; + + DEBUGFUNC("e1000_set_phy_mode"); + + if((hw->mac_type == e1000_82545_rev_3) && + (hw->media_type == e1000_media_type_copper)) { + ret_val = e1000_read_eeprom(hw, EEPROM_PHY_CLASS_WORD, 1, &eeprom_data); + if(ret_val) { + return ret_val; + } - if((ret_val = e1000_write_phy_reg(hw, 0x0000, - IGP01E1000_IEEE_RESTART_AUTONEG))) + if((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) { + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x000B); + if(ret_val) return ret_val; - hw->ffe_config_state = e1000_ffe_config_enabled; + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, 0x8104); + if(ret_val) + return ret_val; + + hw->phy_reset_disable = FALSE; } } + return E1000_SUCCESS; } @@ -4953,25 +5091,27 @@ /* During driver activity LPLU should not be used or it will attain link * from the lowest speeds starting from 10Mbps. The capability is used for * Dx transitions and states */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_GMII_FIFO, &phy_data); + if(ret_val) return ret_val; if(!active) { phy_data &= ~IGP01E1000_GMII_FLEX_SPD; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); + if(ret_val) return ret_val; /* LPLU and SmartSpeed are mutually exclusive. LPLU is used during * Dx states where the power conservation is most important. During * driver activity we should enable SmartSpeed, so performance is * maintained. */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if(ret_val) return ret_val; phy_data |= IGP01E1000_PSCFR_SMART_SPEED; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); + if(ret_val) return ret_val; } else if((hw->autoneg_advertised == AUTONEG_ADVERTISE_SPEED_DEFAULT) || @@ -4979,17 +5119,18 @@ (hw->autoneg_advertised == AUTONEG_ADVERTISE_10_100_ALL)) { phy_data |= IGP01E1000_GMII_FLEX_SPD; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_GMII_FIFO, phy_data); + if(ret_val) return ret_val; /* When LPLU is enabled we should disable SmartSpeed */ - if((ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - &phy_data))) + ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, &phy_data); + if(ret_val) return ret_val; phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED; - if((ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, - phy_data))) + ret_val = e1000_write_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG, phy_data); + if(ret_val) return ret_val; } @@ -5020,36 +5161,66 @@ /* Set PHY register 30, page 5, bit 8 to 0 */ - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, - &default_page))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, &default_page); + if(ret_val) return ret_val; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0005); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if(ret_val) return ret_val; phy_data &= ~M88E1000_PHY_VCO_REG_BIT8; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if(ret_val) return ret_val; /* Set PHY register 30, page 4, bit 11 to 1 */ - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, 0x0004); + if(ret_val) return ret_val; - if((ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data))) + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, &phy_data); + if(ret_val) return ret_val; phy_data |= M88E1000_PHY_VCO_REG_BIT11; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_GEN_CONTROL, phy_data); + if(ret_val) return ret_val; - if((ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, - default_page))) + ret_val = e1000_write_phy_reg(hw, M88E1000_PHY_PAGE_SELECT, default_page); + if(ret_val) return ret_val; return E1000_SUCCESS; } +/****************************************************************************** + * Verifies the hardware needs to allow ARPs to be processed by the host + * + * hw - Struct containing variables accessed by shared code + * + * returns: - TRUE/FALSE + * + *****************************************************************************/ +uint32_t +e1000_enable_mng_pass_thru(struct e1000_hw *hw) +{ + uint32_t manc; + + if (hw->asf_firmware_present) { + manc = E1000_READ_REG(hw, MANC); + + if (!(manc & E1000_MANC_RCV_TCO_EN) || + !(manc & E1000_MANC_EN_MAC_ADDR_FILTER)) + return FALSE; + if ((manc & E1000_MANC_SMBUS_EN) && !(manc & E1000_MANC_ASF_EN)) + return TRUE; + } + return FALSE; +} diff -urN linux-2.4.26/drivers/net/e1000/e1000_hw.h linux-2.4.27/drivers/net/e1000/e1000_hw.h --- linux-2.4.26/drivers/net/e1000/e1000_hw.h 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000_hw.h 2004-08-07 16:26:05.104365268 -0700 @@ -307,6 +307,7 @@ /* Adaptive IFS Functions */ /* Everything else */ +uint32_t e1000_enable_mng_pass_thru(struct e1000_hw *hw); void e1000_clear_hw_cntrs(struct e1000_hw *hw); void e1000_reset_adaptive(struct e1000_hw *hw); void e1000_update_adaptive(struct e1000_hw *hw); @@ -369,6 +370,9 @@ #define E1000_82542_2_0_REV_ID 2 #define E1000_82542_2_1_REV_ID 3 +#define E1000_REVISION_0 0 +#define E1000_REVISION_1 1 +#define E1000_REVISION_2 2 #define SPEED_10 10 #define SPEED_100 100 @@ -763,6 +767,7 @@ #define E1000_WUPL 0x05900 /* Wakeup Packet Length - RW */ #define E1000_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */ #define E1000_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */ +#define E1000_HOST_IF 0x08800 /* Host Interface */ #define E1000_FFMT 0x09000 /* Flexible Filter Mask Table - RW Array */ #define E1000_FFVT 0x09800 /* Flexible Filter Value Table - RW Array */ @@ -899,6 +904,7 @@ #define E1000_82542_TDFT 0x08018 #define E1000_82542_FFMT E1000_FFMT #define E1000_82542_FFVT E1000_FFVT +#define E1000_82542_HOST_IF E1000_HOST_IF /* Statistics counters collected by the MAC */ struct e1000_hw_stats { @@ -978,6 +984,7 @@ e1000_ms_type master_slave; e1000_ms_type original_master_slave; e1000_ffe_config ffe_config_state; + uint32_t asf_firmware_present; unsigned long io_base; uint32_t phy_id; uint32_t phy_revision; @@ -1434,6 +1441,10 @@ #define E1000_MANC_TCO_RESET 0x00010000 /* TCO Reset Occurred */ #define E1000_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */ #define E1000_MANC_REPORT_STATUS 0x00040000 /* Status Reporting Enabled */ +#define E1000_MANC_EN_MAC_ADDR_FILTER 0x00100000 /* Enable MAC address + * filtering */ +#define E1000_MANC_EN_MNG2HOST 0x00200000 /* Enable MNG packets to host + * memory */ #define E1000_MANC_SMB_REQ 0x01000000 /* SMBus Request */ #define E1000_MANC_SMB_GNT 0x02000000 /* SMBus Grant */ #define E1000_MANC_SMB_CLK_IN 0x04000000 /* SMBus Clock In */ @@ -1480,6 +1491,7 @@ #define EEPROM_COMPAT 0x0003 #define EEPROM_ID_LED_SETTINGS 0x0004 #define EEPROM_SERDES_AMPLITUDE 0x0006 /* For SERDES output amplitude adjustment. */ +#define EEPROM_PHY_CLASS_WORD 0x0007 #define EEPROM_INIT_CONTROL1_REG 0x000A #define EEPROM_INIT_CONTROL2_REG 0x000F #define EEPROM_INIT_CONTROL3_PORT_B 0x0014 @@ -1513,6 +1525,9 @@ /* Mask bits for SERDES amplitude adjustment in Word 6 of the EEPROM */ #define EEPROM_SERDES_AMPLITUDE_MASK 0x000F +/* Mask bit for PHY class in Word 7 of the EEPROM */ +#define EEPROM_PHY_CLASS_A 0x8000 + /* Mask bits for fields in Word 0x0a of the EEPROM */ #define EEPROM_WORD0A_ILOS 0x0010 #define EEPROM_WORD0A_SWDPIO 0x01E0 @@ -1540,7 +1555,7 @@ #define PBA_SIZE 4 /* Collision related configuration parameters */ -#define E1000_COLLISION_THRESHOLD 16 +#define E1000_COLLISION_THRESHOLD 15 #define E1000_CT_SHIFT 4 #define E1000_COLLISION_DISTANCE 64 #define E1000_FDX_COLLISION_DISTANCE E1000_COLLISION_DISTANCE @@ -2006,7 +2021,7 @@ #define IGP01E1000_PSSR_MDIX_SHIFT 0x000B /* shift right 11 */ /* IGP01E1000 Specific Port Control Register - R/W */ -#define IGP01E1000_PSCR_TP_LOOPBACK 0x0001 +#define IGP01E1000_PSCR_TP_LOOPBACK 0x0010 #define IGP01E1000_PSCR_CORRECT_NC_SCMBLR 0x0200 #define IGP01E1000_PSCR_TEN_CRS_SELECT 0x0400 #define IGP01E1000_PSCR_FLIP_CHIP 0x0800 @@ -2016,16 +2031,18 @@ /* IGP01E1000 Specific Port Link Health Register */ #define IGP01E1000_PLHR_SS_DOWNGRADE 0x8000 #define IGP01E1000_PLHR_GIG_SCRAMBLER_ERROR 0x4000 +#define IGP01E1000_PLHR_MASTER_FAULT 0x2000 +#define IGP01E1000_PLHR_MASTER_RESOLUTION 0x1000 #define IGP01E1000_PLHR_GIG_REM_RCVR_NOK 0x0800 /* LH */ #define IGP01E1000_PLHR_IDLE_ERROR_CNT_OFLOW 0x0400 /* LH */ #define IGP01E1000_PLHR_DATA_ERR_1 0x0200 /* LH */ #define IGP01E1000_PLHR_DATA_ERR_0 0x0100 -#define IGP01E1000_PLHR_AUTONEG_FAULT 0x0010 -#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0008 -#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0004 -#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0002 -#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0001 -#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0000 +#define IGP01E1000_PLHR_AUTONEG_FAULT 0x0040 +#define IGP01E1000_PLHR_AUTONEG_ACTIVE 0x0010 +#define IGP01E1000_PLHR_VALID_CHANNEL_D 0x0008 +#define IGP01E1000_PLHR_VALID_CHANNEL_C 0x0004 +#define IGP01E1000_PLHR_VALID_CHANNEL_B 0x0002 +#define IGP01E1000_PLHR_VALID_CHANNEL_A 0x0001 /* IGP01E1000 Channel Quality Register */ #define IGP01E1000_MSE_CHANNEL_D 0x000F diff -urN linux-2.4.26/drivers/net/e1000/e1000_main.c linux-2.4.27/drivers/net/e1000/e1000_main.c --- linux-2.4.26/drivers/net/e1000/e1000_main.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000_main.c 2004-08-07 16:26:05.107365391 -0700 @@ -27,47 +27,32 @@ *******************************************************************************/ #include "e1000.h" +#include /* Change Log * - * 5.2.30.1 1/29/03 - * o Set VLAN filtering to IEEE 802.1Q after reset so we don't break - * SoL connections that use VLANs. - * o Allow 1000/Full setting for AutoNeg param for Fiber connections - * Jon D Mason [jonmason@us.ibm.com]. - * o Race between Tx queue and Tx clean fixed with a spin lock. - * o Added netpoll support. - * o Fixed endianess bug causing ethtool loopback diags to fail on ppc. - * o Use pdev->irq rather than netdev->irq in preparation for MSI support. - * o Report driver message on user override of InterruptThrottleRate - * module parameter. - * o Change I/O address storage from uint32_t to unsigned long. - * - * 5.2.22 10/15/03 - * o Bug fix: SERDES devices might be connected to a back-plane - * switch that doesn't support auto-neg, so add the capability - * to force 1000/Full. Also, since forcing 1000/Full, sample - * RxSynchronize bit to detect link state. - * o Bug fix: Flow control settings for hi/lo watermark didn't - * consider changes in the Rx FIFO size, which could occur with - * Jumbo Frames or with the reduced FIFO in 82547. - * o Better propagation of error codes. [Janice Girouard - * (janiceg@us.ibm.com)]. - * o Bug fix: hang under heavy Tx stress when running out of Tx - * descriptors; wasn't clearing context descriptor when backing - * out of send because of no-resource condition. - * o Bug fix: check netif_running in dev->poll so we don't have to - * hang in dev->close until all polls are finished. [Robert - * Ollson (robert.olsson@data.slu.se)]. - * o Revert TxDescriptor ring size back to 256 since change to 1024 - * wasn't accepted into the kernel. - * - * 5.2.16 8/8/03 + * 5.2.51 5/14/04 + * o set default configuration to 'NAPI disabled'. NAPI enabled driver + * causes kernel panic when the interface is shutdown while data is being + * transferred. + * 5.2.47 5/04/04 + * o fixed ethtool -t implementation + * 5.2.45 4/29/04 + * o fixed ethtool -e implementation + * o Support for ethtool ops [Stephen Hemminger (shemminger@osdl.org)] + * 5.2.42 4/26/04 + * o Added support for the DPRINTK macro for enhanced error logging. Some + * parts of the patch were supplied by Jon Mason. + * o Move the register_netdevice() donw in the probe routine due to a + * loading/unloading test issue. + * o Added a long RX byte count the the extra ethtool data members for BER + * testing purposes. + * 5.2.39 3/12/04 */ char e1000_driver_name[] = "e1000"; char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; -char e1000_driver_version[] = "5.2.30.1-k1"; +char e1000_driver_version[] = "5.2.52-k3"; char e1000_copyright[] = "Copyright (c) 1999-2004 Intel Corporation."; /* e1000_pci_tbl - PCI Device ID Table @@ -162,6 +147,7 @@ static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); +void set_ethtool_ops(struct net_device *netdev); static void e1000_enter_82542_rst(struct e1000_adapter *adapter); static void e1000_leave_82542_rst(struct e1000_adapter *adapter); static inline void e1000_rx_checksum(struct e1000_adapter *adapter, @@ -198,7 +184,7 @@ /* Exported from other modules */ extern void e1000_check_options(struct e1000_adapter *adapter); -extern int e1000_ethtool_ioctl(struct net_device *netdev, struct ifreq *ifr); + static struct pci_driver e1000_driver = { .name = e1000_driver_name, @@ -216,6 +202,10 @@ MODULE_DESCRIPTION("Intel(R) PRO/1000 Network Driver"); MODULE_LICENSE("GPL"); +static int debug = 3; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + /** * e1000_init_module - Driver Registration Routine * @@ -309,7 +299,7 @@ void e1000_reset(struct e1000_adapter *adapter) { - uint32_t pba; + uint32_t pba, manc; /* Repartition Pba for greater than 9k mtu * To take effect CTRL.RST is required. */ @@ -327,14 +317,16 @@ adapter->tx_fifo_head = 0; adapter->tx_head_addr = pba << E1000_TX_HEAD_ADDR_SHIFT; adapter->tx_fifo_size = - (E1000_PBA_40K - pba) << E1000_TX_FIFO_SIZE_SHIFT; + (E1000_PBA_40K - pba) << E1000_PBA_BYTES_SHIFT; atomic_set(&adapter->tx_fifo_stall, 0); } E1000_WRITE_REG(&adapter->hw, PBA, pba); /* flow control settings */ - adapter->hw.fc_high_water = pba - E1000_FC_HIGH_DIFF; - adapter->hw.fc_low_water = pba - E1000_FC_LOW_DIFF; + adapter->hw.fc_high_water = + (pba << E1000_PBA_BYTES_SHIFT) - E1000_FC_HIGH_DIFF; + adapter->hw.fc_low_water = + (pba << E1000_PBA_BYTES_SHIFT) - E1000_FC_LOW_DIFF; adapter->hw.fc_pause_time = E1000_FC_PAUSE_TIME; adapter->hw.fc_send_xon = 1; adapter->hw.fc = adapter->hw.original_fc; @@ -349,6 +341,12 @@ e1000_reset_adaptive(&adapter->hw); e1000_phy_get_info(&adapter->hw, &adapter->phy_info); + + if(adapter->en_mng_pt) { + manc = E1000_READ_REG(&adapter->hw, MANC); + manc |= (E1000_MANC_ARP_EN | E1000_MANC_EN_MNG2HOST); + E1000_WRITE_REG(&adapter->hw, MANC, manc); + } } /** @@ -402,12 +400,19 @@ } SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); pci_set_drvdata(pdev, netdev); adapter = netdev->priv; adapter->netdev = netdev; adapter->pdev = pdev; adapter->hw.back = adapter; + adapter->msg_enable = (1 << debug) - 1; + + rtnl_lock(); + /* we need to set the name early since the DPRINTK macro needs it set */ + if (dev_alloc_name(netdev, netdev->name) < 0) + goto err_free_unlock; mmio_start = pci_resource_start(pdev, BAR_0); mmio_len = pci_resource_len(pdev, BAR_0); @@ -435,6 +440,7 @@ netdev->set_mac_address = &e1000_set_mac; netdev->change_mtu = &e1000_change_mtu; netdev->do_ioctl = &e1000_ioctl; + set_ethtool_ops(netdev); netdev->tx_timeout = &e1000_tx_timeout; netdev->watchdog_timeo = 5 * HZ; #ifdef CONFIG_E1000_NAPI @@ -470,14 +476,21 @@ } #ifdef NETIF_F_TSO +#ifdef BROKEN_ON_NON_IA_ARCHS + /* Disbaled for now until root-cause is found for + * hangs reported against non-IA archs. TSO can be + * enabled using ethtool -K eth tso on */ if((adapter->hw.mac_type >= e1000_82544) && (adapter->hw.mac_type != e1000_82547)) netdev->features |= NETIF_F_TSO; #endif +#endif if(pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; + adapter->en_mng_pt = e1000_enable_mng_pass_thru(&adapter->hw); + /* before reading the EEPROM, reset the controller to * put the device in a known good starting state */ @@ -486,7 +499,7 @@ /* make sure the EEPROM is good */ if(e1000_validate_eeprom_checksum(&adapter->hw) < 0) { - printk(KERN_ERR "The EEPROM Checksum Is Not Valid\n"); + DPRINTK(PROBE, ERR, "The EEPROM Checksum Is Not Valid\n"); err = -EIO; goto err_eeprom; } @@ -520,15 +533,12 @@ INIT_TQUEUE(&adapter->tx_timeout_task, (void (*)(void *))e1000_tx_timeout_task, netdev); - register_netdev(netdev); - /* we're going to reset, so assume we have no link for now */ netif_carrier_off(netdev); netif_stop_queue(netdev); - printk(KERN_INFO "%s: Intel(R) PRO/1000 Network Connection\n", - netdev->name); + DPRINTK(PROBE, INFO, "Intel(R) PRO/1000 Network Connection\n"); e1000_check_options(adapter); /* Initial Wake on LAN setting @@ -562,13 +572,21 @@ e1000_reset(adapter); + /* since we are holding the rtnl lock already, call the no-lock version */ + if((err = register_netdevice(netdev))) + goto err_register; + cards_found++; + rtnl_unlock(); return 0; +err_register: err_sw_init: err_eeprom: iounmap(adapter->hw.hw_addr); err_ioremap: +err_free_unlock: + rtnl_unlock(); free_netdev(netdev); err_alloc_etherdev: pci_release_regions(pdev); @@ -646,7 +664,7 @@ /* identify the MAC */ if (e1000_set_mac_type(hw)) { - E1000_ERR("Unknown MAC Type\n"); + DPRINTK(PROBE, ERR, "Unknown MAC Type\n"); return -EIO; } @@ -1373,9 +1391,8 @@ &adapter->link_speed, &adapter->link_duplex); - printk(KERN_INFO - "e1000: %s NIC Link is Up %d Mbps %s\n", - netdev->name, adapter->link_speed, + DPRINTK(LINK, INFO, "NIC Link is Up %d Mbps %s\n", + adapter->link_speed, adapter->link_duplex == FULL_DUPLEX ? "Full Duplex" : "Half Duplex"); @@ -1388,9 +1405,7 @@ if(netif_carrier_ok(netdev)) { adapter->link_speed = 0; adapter->link_duplex = 0; - printk(KERN_INFO - "e1000: %s NIC Link is Down\n", - netdev->name); + DPRINTK(LINK, INFO, "NIC Link is Down\n"); netif_carrier_off(netdev); netif_stop_queue(netdev); mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); @@ -1542,33 +1557,17 @@ static inline int e1000_tx_map(struct e1000_adapter *adapter, struct sk_buff *skb, - unsigned int first) + unsigned int first, unsigned int max_per_txd, + unsigned int nr_frags, unsigned int mss) { struct e1000_desc_ring *tx_ring = &adapter->tx_ring; - struct e1000_tx_desc *tx_desc; struct e1000_buffer *buffer_info; - unsigned int len = skb->len, max_per_txd = E1000_MAX_DATA_PER_TXD; + unsigned int len = skb->len; unsigned int offset = 0, size, count = 0, i; -#ifdef NETIF_F_TSO - unsigned int mss; -#endif - unsigned int nr_frags; unsigned int f; - -#ifdef NETIF_F_TSO - mss = skb_shinfo(skb)->tso_size; - /* The controller does a simple calculation to - * make sure there is enough room in the FIFO before - * initiating the DMA for each buffer. The calc is: - * 4 = ceil(buffer len/mss). To make sure we don't - * overrun the FIFO, adjust the max buffer len if mss - * drops. */ - if(mss) - max_per_txd = min(mss << 2, max_per_txd); -#endif - nr_frags = skb_shinfo(skb)->nr_frags; len -= skb->data_len; + i = tx_ring->next_to_use; while(len) { @@ -1640,46 +1639,6 @@ if(++i == tx_ring->count) i = 0; } } - - if(E1000_DESC_UNUSED(&adapter->tx_ring) < count + 2) { - - /* There aren't enough descriptors available to queue up - * this send (need: count + 1 context desc + 1 desc gap - * to keep tail from touching head), so undo the mapping - * and abort the send. We could have done the check before - * we mapped the skb, but because of all the workarounds - * (above), it's too difficult to predict how many we're - * going to need.*/ - i = adapter->tx_ring.next_to_use; - - if(i == first) { - /* Cleanup after e1000_tx_[csum|tso] scribbling - * on descriptors. */ - tx_desc = E1000_TX_DESC(*tx_ring, first); - tx_desc->buffer_addr = 0; - tx_desc->lower.data = 0; - tx_desc->upper.data = 0; - } - - while(count--) { - buffer_info = &tx_ring->buffer_info[i]; - - if(buffer_info->dma) { - pci_unmap_page(adapter->pdev, - buffer_info->dma, - buffer_info->length, - PCI_DMA_TODEVICE); - buffer_info->dma = 0; - } - - if(++i == tx_ring->count) i = 0; - } - - adapter->tx_ring.next_to_use = first; - - return 0; - } - i = (i == 0) ? tx_ring->count - 1 : i - 1; tx_ring->buffer_info[i].skb = skb; tx_ring->buffer_info[first].next_to_watch = i; @@ -1774,27 +1733,72 @@ return 0; } +#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1 ) static int e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct e1000_adapter *adapter = netdev->priv; - unsigned int first; + unsigned int first, max_per_txd = E1000_MAX_DATA_PER_TXD; + unsigned int max_txd_pwr = E1000_MAX_TXD_PWR; unsigned int tx_flags = 0; unsigned long flags; - int count; - + unsigned int len = skb->len; + int count = 0; + unsigned int mss = 0; + unsigned int nr_frags = 0; + unsigned int f; + nr_frags = skb_shinfo(skb)->nr_frags; + len -= skb->data_len; if(skb->len <= 0) { dev_kfree_skb_any(skb); return 0; } +#ifdef NETIF_F_TSO + mss = skb_shinfo(skb)->tso_size; + /* The controller does a simple calculation to + * make sure there is enough room in the FIFO before + * initiating the DMA for each buffer. The calc is: + * 4 = ceil(buffer len/mss). To make sure we don't + * overrun the FIFO, adjust the max buffer len if mss + * drops. */ + if(mss) { + max_per_txd = min(mss << 2, max_per_txd); + max_txd_pwr = fls(max_per_txd) - 1; + } + if((mss) || (skb->ip_summed == CHECKSUM_HW)) + count++; + count++; /*for sentinel desc*/ +#else + if(skb->ip_summed == CHECKSUM_HW) + count++; +#endif + + count += TXD_USE_COUNT(len, max_txd_pwr); + if(adapter->pcix_82544) + count++; + + nr_frags = skb_shinfo(skb)->nr_frags; + for(f = 0; f < nr_frags; f++) + count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size, + max_txd_pwr); + if(adapter->pcix_82544) + count += nr_frags; + spin_lock_irqsave(&adapter->tx_lock, flags); + /* need: count + 2 desc gap to keep tail from touching + * head, otherwise try next time */ + if(E1000_DESC_UNUSED(&adapter->tx_ring) < count + 2 ) { + netif_stop_queue(netdev); + spin_unlock_irqrestore(&adapter->tx_lock, flags); + return 1; + } + spin_unlock_irqrestore(&adapter->tx_lock, flags); if(adapter->hw.mac_type == e1000_82547) { if(e1000_82547_fifo_workaround(adapter, skb)) { netif_stop_queue(netdev); mod_timer(&adapter->tx_fifo_stall_timer, jiffies); - spin_unlock_irqrestore(&adapter->tx_lock, flags); return 1; } } @@ -1811,18 +1815,12 @@ else if(e1000_tx_csum(adapter, skb)) tx_flags |= E1000_TX_FLAGS_CSUM; - if((count = e1000_tx_map(adapter, skb, first))) - e1000_tx_queue(adapter, count, tx_flags); - else { - netif_stop_queue(netdev); - spin_unlock_irqrestore(&adapter->tx_lock, flags); - return 1; - } + e1000_tx_queue(adapter, + e1000_tx_map(adapter, skb, first, max_per_txd, nr_frags, mss), + tx_flags); netdev->trans_start = jiffies; - spin_unlock_irqrestore(&adapter->tx_lock, flags); - return 0; } @@ -1885,7 +1883,7 @@ if((max_frame < MINIMUM_ETHERNET_FRAME_SIZE) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { - E1000_ERR("Invalid MTU setting\n"); + DPRINTK(PROBE, ERR, "Invalid MTU setting\n"); return -EINVAL; } @@ -1893,7 +1891,7 @@ adapter->rx_buffer_len = E1000_RXBUFFER_2048; } else if(adapter->hw.mac_type < e1000_82543) { - E1000_ERR("Jumbo Frames not supported on 82542\n"); + DPRINTK(PROBE, ERR, "Jumbo Frames not supported on 82542\n"); return -EINVAL; } else if(max_frame <= E1000_RXBUFFER_4096) { @@ -2122,26 +2120,10 @@ __netif_rx_schedule(netdev); } #else - /* Writing IMC and IMS is needed for 82547. - Due to Hub Link bus being occupied, an interrupt - de-assertion message is not able to be sent. - When an interrupt assertion message is generated later, - two messages are re-ordered and sent out. - That causes APIC to think 82547 is in de-assertion - state, while 82547 is in assertion state, resulting - in dead lock. Writing IMC forces 82547 into - de-assertion state. - */ - if(hw->mac_type == e1000_82547 || hw->mac_type == e1000_82547_rev_2) - e1000_irq_disable(adapter); - for(i = 0; i < E1000_MAX_INTR; i++) if(!e1000_clean_rx_irq(adapter) & !e1000_clean_tx_irq(adapter)) break; - - if(hw->mac_type == e1000_82547 || hw->mac_type == e1000_82547_rev_2) - e1000_irq_enable(adapter); #endif return IRQ_HANDLED; @@ -2169,6 +2151,7 @@ if(work_done < work_to_do || !netif_running(netdev)) { netif_rx_complete(netdev); e1000_irq_enable(adapter); + return 0; } return (work_done >= work_to_do); @@ -2191,7 +2174,6 @@ unsigned int i, eop; boolean_t cleaned = FALSE; - spin_lock(&adapter->tx_lock); i = tx_ring->next_to_clean; eop = tx_ring->buffer_info[i].next_to_watch; @@ -2234,6 +2216,8 @@ tx_ring->next_to_clean = i; + spin_lock(&adapter->tx_lock); + if(cleaned && netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) netif_wake_queue(netdev); @@ -2294,7 +2278,8 @@ /* All receives must fit into a single buffer */ - E1000_DBG("Receive packet consumed multiple buffers\n"); + E1000_DBG("%s: Receive packet consumed multiple buffers\n", + netdev->name); dev_kfree_skb_irq(skb); rx_desc->status = 0; @@ -2511,8 +2496,6 @@ case SIOCGMIIREG: case SIOCSMIIREG: return e1000_mii_ioctl(netdev, ifr, cmd); - case SIOCETHTOOL: - return e1000_ethtool_ioctl(netdev, ifr); default: return -EOPNOTSUPP; } diff -urN linux-2.4.26/drivers/net/e1000/e1000_osdep.h linux-2.4.27/drivers/net/e1000/e1000_osdep.h --- linux-2.4.26/drivers/net/e1000/e1000_osdep.h 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000_osdep.h 2004-08-07 16:26:05.107365391 -0700 @@ -47,7 +47,7 @@ BUG(); \ } else { \ set_current_state(TASK_UNINTERRUPTIBLE); \ - schedule_timeout((x * HZ)/1000); \ + schedule_timeout((x * HZ)/1000 + 2); \ } } while(0) #endif diff -urN linux-2.4.26/drivers/net/e1000/e1000_param.c linux-2.4.27/drivers/net/e1000/e1000_param.c --- linux-2.4.26/drivers/net/e1000/e1000_param.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/e1000/e1000_param.c 2004-08-07 16:26:05.109365473 -0700 @@ -107,7 +107,7 @@ /* Auto-negotiation Advertisement Override * - * Valid Range: 0x01-0x0F, 0x20-0x2F + * Valid Range: 0x01-0x0F, 0x20-0x2F (copper); 0x20 (fiber) * * The AutoNeg value is a bit mask describing which speed and duplex * combinations should be advertised during auto-negotiation. @@ -117,7 +117,7 @@ * Speed (Mbps) N/A N/A 1000 N/A 100 100 10 10 * Duplex Full Full Half Full Half * - * Default Value: 0x2F + * Default Value: 0x2F (copper); 0x20 (fiber) */ E1000_PARAM(AutoNeg, "Advertised auto-negotiation setting"); @@ -234,7 +234,8 @@ }; static int __devinit -e1000_validate_option(int *value, struct e1000_option *opt) +e1000_validate_option(int *value, struct e1000_option *opt, + struct e1000_adapter *adapter) { if(*value == OPTION_UNSET) { *value = opt->def; @@ -245,16 +246,17 @@ case enable_option: switch (*value) { case OPTION_ENABLED: - printk(KERN_INFO "%s Enabled\n", opt->name); + DPRINTK(PROBE, INFO, "%s Enabled\n", opt->name); return 0; case OPTION_DISABLED: - printk(KERN_INFO "%s Disabled\n", opt->name); + DPRINTK(PROBE, INFO, "%s Disabled\n", opt->name); return 0; } break; case range_option: if(*value >= opt->arg.r.min && *value <= opt->arg.r.max) { - printk(KERN_INFO "%s set to %i\n", opt->name, *value); + DPRINTK(PROBE, INFO, + "%s set to %i\n", opt->name, *value); return 0; } break; @@ -266,7 +268,7 @@ ent = &opt->arg.l.p[i]; if(*value == ent->i) { if(ent->str[0] != '\0') - printk(KERN_INFO "%s\n", ent->str); + DPRINTK(PROBE, INFO, "%s\n", ent->str); return 0; } } @@ -276,7 +278,7 @@ BUG(); } - printk(KERN_INFO "Invalid %s specified (%i) %s\n", + DPRINTK(PROBE, INFO, "Invalid %s specified (%i) %s\n", opt->name, *value, opt->err); *value = opt->def; return -1; @@ -300,9 +302,9 @@ { int bd = adapter->bd_number; if(bd >= E1000_MAX_NIC) { - printk(KERN_NOTICE + DPRINTK(PROBE, NOTICE, "Warning: no configuration for board #%i\n", bd); - printk(KERN_NOTICE "Using defaults for all values\n"); + DPRINTK(PROBE, NOTICE, "Using defaults for all values\n"); bd = E1000_MAX_NIC; } @@ -321,7 +323,7 @@ E1000_MAX_TXD : E1000_MAX_82544_TXD; tx_ring->count = TxDescriptors[bd]; - e1000_validate_option(&tx_ring->count, &opt); + e1000_validate_option(&tx_ring->count, &opt, adapter); E1000_ROUNDUP(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE); } { /* Receive Descriptor Count */ @@ -339,7 +341,7 @@ E1000_MAX_82544_RXD; rx_ring->count = RxDescriptors[bd]; - e1000_validate_option(&rx_ring->count, &opt); + e1000_validate_option(&rx_ring->count, &opt, adapter); E1000_ROUNDUP(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE); } { /* Checksum Offload Enable/Disable */ @@ -351,7 +353,7 @@ }; int rx_csum = XsumRX[bd]; - e1000_validate_option(&rx_csum, &opt); + e1000_validate_option(&rx_csum, &opt, adapter); adapter->rx_csum = rx_csum; } { /* Flow Control */ @@ -373,7 +375,7 @@ }; int fc = FlowControl[bd]; - e1000_validate_option(&fc, &opt); + e1000_validate_option(&fc, &opt, adapter); adapter->hw.fc = adapter->hw.original_fc = fc; } { /* Transmit Interrupt Delay */ @@ -387,7 +389,7 @@ }; adapter->tx_int_delay = TxIntDelay[bd]; - e1000_validate_option(&adapter->tx_int_delay, &opt); + e1000_validate_option(&adapter->tx_int_delay, &opt, adapter); } { /* Transmit Absolute Interrupt Delay */ struct e1000_option opt = { @@ -400,7 +402,7 @@ }; adapter->tx_abs_int_delay = TxAbsIntDelay[bd]; - e1000_validate_option(&adapter->tx_abs_int_delay, &opt); + e1000_validate_option(&adapter->tx_abs_int_delay, &opt, adapter); } { /* Receive Interrupt Delay */ struct e1000_option opt = { @@ -413,7 +415,7 @@ }; adapter->rx_int_delay = RxIntDelay[bd]; - e1000_validate_option(&adapter->rx_int_delay, &opt); + e1000_validate_option(&adapter->rx_int_delay, &opt, adapter); } { /* Receive Absolute Interrupt Delay */ struct e1000_option opt = { @@ -426,7 +428,7 @@ }; adapter->rx_abs_int_delay = RxAbsIntDelay[bd]; - e1000_validate_option(&adapter->rx_abs_int_delay, &opt); + e1000_validate_option(&adapter->rx_abs_int_delay, &opt, adapter); } { /* Interrupt Throttling Rate */ struct e1000_option opt = { @@ -444,13 +446,14 @@ adapter->itr = 1; break; case 0: - printk(KERN_INFO "%s turned off\n", opt.name); + DPRINTK(PROBE, INFO, "%s turned off\n", opt.name); break; case 1: - printk(KERN_INFO "%s set to dynamic mode\n", opt.name); + DPRINTK(PROBE, INFO, + "%s set to dynamic mode\n", opt.name); break; default: - e1000_validate_option(&adapter->itr, &opt); + e1000_validate_option(&adapter->itr, &opt, adapter); break; } } @@ -482,15 +485,15 @@ bd = bd > E1000_MAX_NIC ? E1000_MAX_NIC : bd; if((Speed[bd] != OPTION_UNSET)) { - printk(KERN_INFO "Speed not valid for fiber adapters, " + DPRINTK(PROBE, INFO, "Speed not valid for fiber adapters, " "parameter ignored\n"); } if((Duplex[bd] != OPTION_UNSET)) { - printk(KERN_INFO "Duplex not valid for fiber adapters, " + DPRINTK(PROBE, INFO, "Duplex not valid for fiber adapters, " "parameter ignored\n"); } if((AutoNeg[bd] != OPTION_UNSET) && (AutoNeg[bd] != 0x20)) { - printk(KERN_INFO "AutoNeg other than Full/1000 is " + DPRINTK(PROBE, INFO, "AutoNeg other than Full/1000 is " "not valid for fiber adapters, parameter ignored\n"); } } @@ -525,7 +528,7 @@ }; speed = Speed[bd]; - e1000_validate_option(&speed, &opt); + e1000_validate_option(&speed, &opt, adapter); } { /* Duplex */ struct e1000_opt_list dplx_list[] = {{ 0, "" }, @@ -542,11 +545,11 @@ }; dplx = Duplex[bd]; - e1000_validate_option(&dplx, &opt); + e1000_validate_option(&dplx, &opt, adapter); } if(AutoNeg[bd] != OPTION_UNSET && (speed != 0 || dplx != 0)) { - printk(KERN_INFO + DPRINTK(PROBE, INFO, "AutoNeg specified along with Speed or Duplex, " "parameter ignored\n"); adapter->hw.autoneg_advertised = AUTONEG_ADV_DEFAULT; @@ -595,7 +598,7 @@ }; int an = AutoNeg[bd]; - e1000_validate_option(&an, &opt); + e1000_validate_option(&an, &opt, adapter); adapter->hw.autoneg_advertised = an; } @@ -603,78 +606,85 @@ case 0: adapter->hw.autoneg = adapter->fc_autoneg = 1; if(Speed[bd] != OPTION_UNSET || Duplex[bd] != OPTION_UNSET) - printk(KERN_INFO + DPRINTK(PROBE, INFO, "Speed and duplex autonegotiation enabled\n"); break; case HALF_DUPLEX: - printk(KERN_INFO "Half Duplex specified without Speed\n"); - printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n"); + DPRINTK(PROBE, INFO, "Half Duplex specified without Speed\n"); + DPRINTK(PROBE, INFO, + "Using Autonegotiation at Half Duplex only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_10_HALF | ADVERTISE_100_HALF; break; case FULL_DUPLEX: - printk(KERN_INFO "Full Duplex specified without Speed\n"); - printk(KERN_INFO "Using Autonegotiation at Full Duplex only\n"); + DPRINTK(PROBE, INFO, "Full Duplex specified without Speed\n"); + DPRINTK(PROBE, INFO, + "Using Autonegotiation at Full Duplex only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_10_FULL | ADVERTISE_100_FULL | ADVERTISE_1000_FULL; break; case SPEED_10: - printk(KERN_INFO "10 Mbps Speed specified without Duplex\n"); - printk(KERN_INFO "Using Autonegotiation at 10 Mbps only\n"); + DPRINTK(PROBE, INFO, + "10 Mbps Speed specified without Duplex\n"); + DPRINTK(PROBE, INFO, "Using Autonegotiation at 10 Mbps only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_10_HALF | ADVERTISE_10_FULL; break; case SPEED_10 + HALF_DUPLEX: - printk(KERN_INFO "Forcing to 10 Mbps Half Duplex\n"); + DPRINTK(PROBE, INFO, "Forcing to 10 Mbps Half Duplex\n"); adapter->hw.autoneg = adapter->fc_autoneg = 0; adapter->hw.forced_speed_duplex = e1000_10_half; adapter->hw.autoneg_advertised = 0; break; case SPEED_10 + FULL_DUPLEX: - printk(KERN_INFO "Forcing to 10 Mbps Full Duplex\n"); + DPRINTK(PROBE, INFO, "Forcing to 10 Mbps Full Duplex\n"); adapter->hw.autoneg = adapter->fc_autoneg = 0; adapter->hw.forced_speed_duplex = e1000_10_full; adapter->hw.autoneg_advertised = 0; break; case SPEED_100: - printk(KERN_INFO "100 Mbps Speed specified without Duplex\n"); - printk(KERN_INFO "Using Autonegotiation at 100 Mbps only\n"); + DPRINTK(PROBE, INFO, + "100 Mbps Speed specified without Duplex\n"); + DPRINTK(PROBE, INFO, + "Using Autonegotiation at 100 Mbps only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_100_HALF | ADVERTISE_100_FULL; break; case SPEED_100 + HALF_DUPLEX: - printk(KERN_INFO "Forcing to 100 Mbps Half Duplex\n"); + DPRINTK(PROBE, INFO, "Forcing to 100 Mbps Half Duplex\n"); adapter->hw.autoneg = adapter->fc_autoneg = 0; adapter->hw.forced_speed_duplex = e1000_100_half; adapter->hw.autoneg_advertised = 0; break; case SPEED_100 + FULL_DUPLEX: - printk(KERN_INFO "Forcing to 100 Mbps Full Duplex\n"); + DPRINTK(PROBE, INFO, "Forcing to 100 Mbps Full Duplex\n"); adapter->hw.autoneg = adapter->fc_autoneg = 0; adapter->hw.forced_speed_duplex = e1000_100_full; adapter->hw.autoneg_advertised = 0; break; case SPEED_1000: - printk(KERN_INFO "1000 Mbps Speed specified without Duplex\n"); - printk(KERN_INFO + DPRINTK(PROBE, INFO, + "1000 Mbps Speed specified without Duplex\n"); + DPRINTK(PROBE, INFO, "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; break; case SPEED_1000 + HALF_DUPLEX: - printk(KERN_INFO "Half Duplex is not supported at 1000 Mbps\n"); - printk(KERN_INFO + DPRINTK(PROBE, INFO, + "Half Duplex is not supported at 1000 Mbps\n"); + DPRINTK(PROBE, INFO, "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; break; case SPEED_1000 + FULL_DUPLEX: - printk(KERN_INFO + DPRINTK(PROBE, INFO, "Using Autonegotiation at 1000 Mbps Full Duplex only\n"); adapter->hw.autoneg = adapter->fc_autoneg = 1; adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL; @@ -685,7 +695,8 @@ /* Speed, AutoNeg and MDI/MDI-X must all play nice */ if (e1000_validate_mdi_setting(&(adapter->hw)) < 0) { - printk(KERN_INFO "Speed, AutoNeg and MDI-X specifications are " + DPRINTK(PROBE, INFO, + "Speed, AutoNeg and MDI-X specifications are " "incompatible. Setting MDI-X to a compatible value.\n"); } } diff -urN linux-2.4.26/drivers/net/eql.c linux-2.4.27/drivers/net/eql.c --- linux-2.4.26/drivers/net/eql.c 2001-09-30 12:26:06.000000000 -0700 +++ linux-2.4.27/drivers/net/eql.c 2004-08-07 16:26:05.110365515 -0700 @@ -409,6 +409,8 @@ #endif master_dev = dev; /* for "clarity" */ slave_dev = __dev_get_by_name (srq.slave_name); + if (!slave_dev) + return -ENODEV; if (master_dev != 0 && slave_dev != 0) { @@ -463,6 +465,8 @@ #endif master_dev = dev; /* for "clarity" */ slave_dev = __dev_get_by_name (srq.slave_name); + if (!slave_dev) + return -ENODEV; if ( eql_is_slave (slave_dev) ) /* really is a slave */ { @@ -491,6 +495,8 @@ #endif eql = (equalizer_t *) dev->priv; slave_dev = __dev_get_by_name (sc.slave_name); + if (!slave_dev) + return -ENODEV; if ( eql_is_slave (slave_dev) ) { @@ -525,6 +531,8 @@ eql = (equalizer_t *) dev->priv; slave_dev = __dev_get_by_name (sc.slave_name); + if (!slave_dev) + return -ENODEV; if ( eql_is_slave (slave_dev) ) { diff -urN linux-2.4.26/drivers/net/fealnx.c linux-2.4.27/drivers/net/fealnx.c --- linux-2.4.26/drivers/net/fealnx.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/net/fealnx.c 2004-08-07 16:26:05.113365638 -0700 @@ -85,6 +85,7 @@ #include #include #include +#include #include /* Processor type for cache alignment. */ #include @@ -234,15 +235,29 @@ RxErr = 0x00000002, /* receive error */ }; -/* Bits in the NetworkConfig register. */ +/* Bits in the NetworkConfig register, W for writing, R for reading */ +/* FIXME: some names are invented by me. Marked with (name?) */ +/* If you have docs and know bit names, please fix 'em */ enum rx_mode_bits { - RxModeMask = 0xe0, - PROM = 0x80, /* promiscuous mode */ - AB = 0x40, /* accept broadcast */ - AM = 0x20, /* accept mutlicast */ - ARP = 0x08, /* receive runt pkt */ - ALP = 0x04, /* receive long pkt */ - SEP = 0x02, /* receive error pkt */ + CR_W_ENH = 0x02000000, /* enhanced mode (name?) */ + CR_W_FD = 0x00100000, /* full duplex */ + CR_W_PS10 = 0x00080000, /* 10 mbit */ + CR_W_TXEN = 0x00040000, /* tx enable (name?) */ + CR_W_PS1000 = 0x00010000, /* 1000 mbit */ + /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */ + CR_W_RXMODEMASK = 0x000000e0, + CR_W_PROM = 0x00000080, /* promiscuous mode */ + CR_W_AB = 0x00000040, /* accept broadcast */ + CR_W_AM = 0x00000020, /* accept mutlicast */ + CR_W_ARP = 0x00000008, /* receive runt pkt */ + CR_W_ALP = 0x00000004, /* receive long pkt */ + CR_W_SEP = 0x00000002, /* receive error pkt */ + CR_W_RXEN = 0x00000001, /* rx enable (unicast?) (name?) */ + + CR_R_TXSTOP = 0x04000000, /* tx stopped (name?) */ + CR_R_FD = 0x00100000, /* full duplex detected */ + CR_R_PS10 = 0x00080000, /* 10 mbit detected */ + CR_R_RXSTOP = 0x00008000, /* rx stopped (name?) */ }; /* The Tulip Rx and Tx buffer descriptors. */ @@ -376,10 +391,7 @@ #define LXT1000_Full 0x200 // 89/12/29 add, for phy specific status register, levelone phy, (end) -/* for 3-in-1 case */ -#define PS10 0x00080000 -#define FD 0x00100000 -#define PS1000 0x00010000 +/* for 3-in-1 case, BMCRSR register */ #define LinkIsUp2 0x00040000 /* for PHY */ @@ -401,6 +413,12 @@ /* Media monitoring timer. */ struct timer_list timer; + /* Reset timer */ + struct timer_list reset_timer; + int reset_timer_armed; + unsigned long crvalue_sv; + unsigned long imrvalue_sv; + /* Frequently used values: keep some adjacent for cache effect. */ int flags; struct pci_dev *pci_dev; @@ -436,49 +454,44 @@ static void getlinktype(struct net_device *dev); static void getlinkstatus(struct net_device *dev); static void netdev_timer(unsigned long data); +static void reset_timer(unsigned long data); static void tx_timeout(struct net_device *dev); static void init_ring(struct net_device *dev); static int start_tx(struct sk_buff *skb, struct net_device *dev); static irqreturn_t intr_handler(int irq, void *dev_instance, struct pt_regs *regs); static int netdev_rx(struct net_device *dev); static void set_rx_mode(struct net_device *dev); +static void __set_rx_mode(struct net_device *dev); static struct net_device_stats *get_stats(struct net_device *dev); static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static struct ethtool_ops netdev_ethtool_ops; static int netdev_close(struct net_device *dev); static void reset_rx_descriptors(struct net_device *dev); +static void reset_tx_descriptors(struct net_device *dev); -void stop_nic_tx(long ioaddr, long crvalue) +static void stop_nic_rx(long ioaddr, long crvalue) { - writel(crvalue & (~0x40000), ioaddr + TCRRCR); - - /* wait for tx stop */ - { - int i = 0, delay = 0x1000; - - while ((!(readl(ioaddr + TCRRCR) & 0x04000000)) && (i < delay)) { - ++i; - } + int delay = 0x1000; + writel(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR); + while (--delay) { + if ( (readl(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP) + break; } } -void stop_nic_rx(long ioaddr, long crvalue) +static void stop_nic_rxtx(long ioaddr, long crvalue) { - writel(crvalue & (~0x1), ioaddr + TCRRCR); - - /* wait for rx stop */ - { - int i = 0, delay = 0x1000; - - while ((!(readl(ioaddr + TCRRCR) & 0x00008000)) && (i < delay)) { - ++i; - } + int delay = 0x1000; + writel(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR); + while (--delay) { + if ( (readl(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP)) + == (CR_R_RXSTOP+CR_R_TXSTOP) ) + break; } } - static int __devinit fealnx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -496,7 +509,7 @@ #ifndef MODULE static int printed_version; if (!printed_version++) - printk (version); + printk(version); #endif card_idx++; @@ -622,7 +635,7 @@ np->phys[0] = 32; /* 89/6/23 add, (begin) */ /* get phy type */ - if (readl(dev->base_addr + PHYIDENTIFIER) == MysonPHYID) + if (readl(ioaddr + PHYIDENTIFIER) == MysonPHYID) np->PHYType = MysonPHY; else np->PHYType = OtherPHY; @@ -657,7 +670,7 @@ if (np->flags == HAS_MII_XCVR) mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL); else - writel(ADVERTISE_FULL, dev->base_addr + ANARANLPAR); + writel(ADVERTISE_FULL, ioaddr + ANARANLPAR); np->mii.force_media = 1; } @@ -669,7 +682,7 @@ dev->set_multicast_list = &set_rx_mode; dev->do_ioctl = &mii_ioctl; dev->ethtool_ops = &netdev_ethtool_ops; - dev->tx_timeout = tx_timeout; + dev->tx_timeout = &tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; err = register_netdev(dev); @@ -699,6 +712,7 @@ return err; } + static void __devexit fealnx_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -721,42 +735,6 @@ printk(KERN_ERR "fealnx: remove for unknown device\n"); } -unsigned int m80x_read_tick(void) -/* function: Reads the Timer tick count register which decrements by 2 from */ -/* 65536 to 0 every 1/36.414 of a second. Each 2 decrements of the *//* count represents 838 nsec's. */ -/* input : none. */ -/* output : none. */ -{ - unsigned char tmp; - int value; - - writeb((char) 0x06, 0x43); // Command 8254 to latch T0's count - - // now read the count. - tmp = (unsigned char) readb(0x40); - value = ((int) tmp) << 8; - tmp = (unsigned char) readb(0x40); - value |= (((int) tmp) & 0xff); - return (value); -} - - -void m80x_delay(unsigned int interval) -/* function: to wait for a specified time. */ -/* input : interval ... the specified time. */ -/* output : none. */ -{ - unsigned int interval1, interval2, i = 0; - - interval1 = m80x_read_tick(); // get initial value - do { - interval2 = m80x_read_tick(); - if (interval1 < interval2) - interval1 = interval2; - ++i; - } while (((interval1 - interval2) < (ushort) interval) && (i < 65535)); -} - static ulong m80x_send_cmd_to_phy(long miiport, int opcode, int phyad, int regad) { @@ -796,7 +774,7 @@ /* high MDC */ miir |= MASK_MIIR_MII_MDC; writel(miir, miiport); - m80x_delay(30); + udelay(30); /* next */ mask >>= 1; @@ -831,7 +809,7 @@ /* high MDC, and wait */ miir |= MASK_MIIR_MII_MDC; writel(miir, miiport); - m80x_delay((int) 30); + udelay(30); /* next */ mask >>= 1; @@ -873,8 +851,6 @@ /* low MDC */ miir &= ~MASK_MIIR_MII_MDC; writel(miir, miiport); - - return; } @@ -949,7 +925,7 @@ np->imrvalue = TUNF | CNTOVF | RBU | TI | RI; if (np->pci_dev->device == 0x891) { np->bcrvalue |= 0x200; /* set PROG bit */ - np->crvalue |= 0x02000000; /* set enhanced bit */ + np->crvalue |= CR_W_ENH; /* set enhanced bit */ np->imrvalue |= ETI; } writel(np->bcrvalue, ioaddr + BCR); @@ -957,7 +933,7 @@ if (dev->if_port == 0) dev->if_port = np->default_port; - writel(0, dev->base_addr + RXPDR); + writel(0, ioaddr + RXPDR); // 89/9/1 modify, // np->crvalue = 0x00e40001; /* tx store and forward, tx/rx enable */ np->crvalue |= 0x00e40001; /* tx store and forward, tx/rx enable */ @@ -965,7 +941,7 @@ getlinkstatus(dev); if (np->linkok) getlinktype(dev); - set_rx_mode(dev); + __set_rx_mode(dev); netif_start_queue(dev); @@ -985,6 +961,11 @@ /* timer handler */ add_timer(&np->timer); + init_timer(&np->reset_timer); + np->reset_timer.data = (unsigned long) dev; + np->reset_timer.function = &reset_timer; + np->reset_timer_armed = 0; + return 0; } @@ -1005,8 +986,7 @@ np->linkok = 1; return; } - // delay - m80x_delay(100); + udelay(100); } } else { for (i = 0; i < DelayTime; ++i) { @@ -1014,8 +994,7 @@ np->linkok = 1; return; } - // delay - m80x_delay(100); + udelay(100); } } } @@ -1026,11 +1005,11 @@ struct netdev_private *np = dev->priv; if (np->PHYType == MysonPHY) { /* 3-in-1 case */ - if (readl(dev->base_addr + TCRRCR) & FD) + if (readl(dev->base_addr + TCRRCR) & CR_R_FD) np->duplexmode = 2; /* full duplex */ else np->duplexmode = 1; /* half duplex */ - if (readl(dev->base_addr + TCRRCR) & PS10) + if (readl(dev->base_addr + TCRRCR) & CR_R_PS10) np->line_speed = 1; /* 10M */ else np->line_speed = 2; /* 100M */ @@ -1112,19 +1091,18 @@ else np->line_speed = 1; /* 10M */ } - // chage crvalue - // np->crvalue&=(~PS10)&(~FD); - np->crvalue &= (~PS10) & (~FD) & (~PS1000); + np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000); if (np->line_speed == 1) - np->crvalue |= PS10; + np->crvalue |= CR_W_PS10; else if (np->line_speed == 3) - np->crvalue |= PS1000; + np->crvalue |= CR_W_PS1000; if (np->duplexmode == 2) - np->crvalue |= FD; + np->crvalue |= CR_W_FD; } } +/* Take lock before calling this */ static void allocate_rx_buffers(struct net_device *dev) { struct netdev_private *np = dev->priv; @@ -1134,15 +1112,17 @@ struct sk_buff *skb; skb = dev_alloc_skb(np->rx_buf_sz); - np->lack_rxbuf->skbuff = skb; - if (skb == NULL) break; /* Better luck next round. */ + while (np->lack_rxbuf->skbuff) + np->lack_rxbuf = np->lack_rxbuf->next_desc_logical; + skb->dev = dev; /* Mark as being used by this device. */ + np->lack_rxbuf->skbuff = skb; np->lack_rxbuf->buffer = pci_map_single(np->pci_dev, skb->tail, np->rx_buf_sz, PCI_DMA_FROMDEVICE); - np->lack_rxbuf = np->lack_rxbuf->next_desc_logical; + np->lack_rxbuf->status = RXOWN; ++np->really_rx_count; } } @@ -1153,22 +1133,23 @@ struct net_device *dev = (struct net_device *) data; struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; - int next_tick = 10 * HZ; int old_crvalue = np->crvalue; unsigned int old_linkok = np->linkok; + unsigned long flags; if (debug) printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x " "config %8.8x.\n", dev->name, readl(ioaddr + ISR), readl(ioaddr + TCRRCR)); + spin_lock_irqsave(&np->lock, flags); + if (np->flags == HAS_MII_XCVR) { getlinkstatus(dev); if ((old_linkok == 0) && (np->linkok == 1)) { /* we need to detect the media type again */ getlinktype(dev); if (np->crvalue != old_crvalue) { - stop_nic_tx(ioaddr, np->crvalue); - stop_nic_rx(ioaddr, np->crvalue & (~0x40000)); + stop_nic_rxtx(ioaddr, np->crvalue); writel(np->crvalue, ioaddr + TCRRCR); } } @@ -1176,69 +1157,120 @@ allocate_rx_buffers(dev); - np->timer.expires = RUN_AT(next_tick); + spin_unlock_irqrestore(&np->lock, flags); + + np->timer.expires = RUN_AT(10 * HZ); add_timer(&np->timer); } -static void tx_timeout(struct net_device *dev) +/* Take lock before calling */ +/* Reset chip and disable rx, tx and interrupts */ +static void reset_and_disable_rxtx(struct net_device *dev) { - struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; - int i; - - printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," - " resetting...\n", dev->name, readl(ioaddr + ISR)); - - { - - printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); - for (i = 0; i < RX_RING_SIZE; i++) - printk(" %8.8x", (unsigned int) np->rx_ring[i].status); - printk("\n" KERN_DEBUG " Tx ring %p: ", np->tx_ring); - for (i = 0; i < TX_RING_SIZE; i++) - printk(" %4.4x", np->tx_ring[i].status); - printk("\n"); - } - - /* Reinit. Gross */ + int delay=51; /* Reset the chip's Tx and Rx processes. */ - stop_nic_tx(ioaddr, 0); - reset_rx_descriptors(dev); + stop_nic_rxtx(ioaddr, 0); /* Disable interrupts by clearing the interrupt mask. */ - writel(0x0000, ioaddr + IMR); + writel(0, ioaddr + IMR); /* Reset the chip to erase previous misconfiguration. */ writel(0x00000001, ioaddr + BCR); /* Ueimor: wait for 50 PCI cycles (and flush posted writes btw). - We surely wait too long (address+data phase). Who cares ? */ - for (i = 0; i < 50; i++) { + We surely wait too long (address+data phase). Who cares? */ + while (--delay) { readl(ioaddr + BCR); rmb(); } +} + + +/* Take lock before calling */ +/* Restore chip after reset */ +static void enable_rxtx(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + + reset_rx_descriptors(dev); - writel((np->cur_tx - np->tx_ring)*sizeof(struct fealnx_desc) + - np->tx_ring_dma, ioaddr + TXLBA); - writel((np->cur_rx - np->rx_ring)*sizeof(struct fealnx_desc) + - np->rx_ring_dma, ioaddr + RXLBA); + writel(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring), + ioaddr + TXLBA); + writel(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), + ioaddr + RXLBA); writel(np->bcrvalue, ioaddr + BCR); - writel(0, dev->base_addr + RXPDR); - set_rx_mode(dev); + writel(0, ioaddr + RXPDR); + __set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */ + /* Clear and Enable interrupts by setting the interrupt mask. */ writel(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR); writel(np->imrvalue, ioaddr + IMR); - writel(0, dev->base_addr + TXPDR); + writel(0, ioaddr + TXPDR); +} + + +static void reset_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct netdev_private *np = dev->priv; + unsigned long flags; + + printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name); + + spin_lock_irqsave(&np->lock, flags); + np->crvalue = np->crvalue_sv; + np->imrvalue = np->imrvalue_sv; + + reset_and_disable_rxtx(dev); + /* works for me without this: + reset_tx_descriptors(dev); */ + enable_rxtx(dev); + netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */ + + np->reset_timer_armed = 0; + + spin_unlock_irqrestore(&np->lock, flags); +} + + +static void tx_timeout(struct net_device *dev) +{ + struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; + unsigned long flags; + int i; + + printk(KERN_WARNING "%s: Transmit timed out, status %8.8x," + " resetting...\n", dev->name, readl(ioaddr + ISR)); + + { + printk(KERN_DEBUG " Rx ring %p: ", np->rx_ring); + for (i = 0; i < RX_RING_SIZE; i++) + printk(" %8.8x", (unsigned int) np->rx_ring[i].status); + printk("\n" KERN_DEBUG " Tx ring %p: ", np->tx_ring); + for (i = 0; i < TX_RING_SIZE; i++) + printk(" %4.4x", np->tx_ring[i].status); + printk("\n"); + } + + spin_lock_irqsave(&np->lock, flags); + + reset_and_disable_rxtx(dev); + reset_tx_descriptors(dev); + enable_rxtx(dev); + + spin_unlock_irqrestore(&np->lock, flags); dev->trans_start = jiffies; np->stats.tx_errors++; - - return; + netif_wake_queue(dev); /* or .._start_.. ?? */ } @@ -1251,7 +1283,7 @@ /* initialize rx variables */ np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); np->cur_rx = &np->rx_ring[0]; - np->lack_rxbuf = NULL; + np->lack_rxbuf = np->rx_ring; np->really_rx_count = 0; /* initial rx descriptors. */ @@ -1294,6 +1326,7 @@ for (i = 0; i < TX_RING_SIZE; i++) { np->tx_ring[i].status = 0; + /* do we need np->tx_ring[i].control = XXX; ?? */ np->tx_ring[i].next_desc = np->tx_ring_dma + (i + 1)*sizeof(struct fealnx_desc); np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1]; @@ -1303,8 +1336,6 @@ /* for the last tx descriptor */ np->tx_ring[i - 1].next_desc = np->tx_ring_dma; np->tx_ring[i - 1].next_desc_logical = &np->tx_ring[0]; - - return; } @@ -1340,7 +1371,7 @@ np->cur_tx_copy->control |= (BPT << TBSShift); /* buffer size */ /* for the last descriptor */ - next = (struct fealnx *) np->cur_tx_copy.next_desc_logical; + next = np->cur_tx_copy->next_desc_logical; next->skbuff = skb; next->control = TXIC | TXLD | CRCEnable | PADEnable; next->control |= (skb->len << PKTSShift); /* pkt size */ @@ -1381,35 +1412,59 @@ } -void free_one_rx_descriptor(struct netdev_private *np) +/* Take lock before calling */ +/* Chip probably hosed tx ring. Clean up. */ +static void reset_tx_descriptors(struct net_device *dev) { - if (np->really_rx_count == RX_RING_SIZE) - np->cur_rx->status = RXOWN; - else { - np->lack_rxbuf->skbuff = np->cur_rx->skbuff; - np->lack_rxbuf->buffer = np->cur_rx->buffer; - np->lack_rxbuf->status = RXOWN; - ++np->really_rx_count; - np->lack_rxbuf = np->lack_rxbuf->next_desc_logical; + struct netdev_private *np = dev->priv; + struct fealnx_desc *cur; + int i; + + /* initialize tx variables */ + np->cur_tx = &np->tx_ring[0]; + np->cur_tx_copy = &np->tx_ring[0]; + np->really_tx_count = 0; + np->free_tx_count = TX_RING_SIZE; + + for (i = 0; i < TX_RING_SIZE; i++) { + cur = &np->tx_ring[i]; + if (cur->skbuff) { + pci_unmap_single(np->pci_dev, cur->buffer, + cur->skbuff->len, PCI_DMA_TODEVICE); + dev_kfree_skb(cur->skbuff); + /* or dev_kfree_skb_irq(cur->skbuff); ? */ + cur->skbuff = NULL; + } + cur->status = 0; + cur->control = 0; /* needed? */ + /* probably not needed. We do it for purely paranoid reasons */ + cur->next_desc = np->tx_ring_dma + + (i + 1)*sizeof(struct fealnx_desc); + cur->next_desc_logical = &np->tx_ring[i + 1]; } - np->cur_rx = np->cur_rx->next_desc_logical; + /* for the last tx descriptor */ + np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma; + np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0]; } -void reset_rx_descriptors(struct net_device *dev) +/* Take lock and stop rx before calling this */ +static void reset_rx_descriptors(struct net_device *dev) { struct netdev_private *np = dev->priv; - - stop_nic_rx(dev->base_addr, np->crvalue); - - while (!(np->cur_rx->status & RXOWN)) - free_one_rx_descriptor(np); + struct fealnx_desc *cur = np->cur_rx; + int i; allocate_rx_buffers(dev); - writel(np->rx_ring_dma + (np->cur_rx - np->rx_ring), + for (i = 0; i < RX_RING_SIZE; i++) { + if (cur->skbuff) + cur->status = RXOWN; + cur = cur->next_desc_logical; + } + + writel(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring), dev->base_addr + RXLBA); - writel(np->crvalue, dev->base_addr + TCRRCR); } @@ -1419,14 +1474,14 @@ { struct net_device *dev = (struct net_device *) dev_instance; struct netdev_private *np = dev->priv; - long ioaddr, boguscnt = max_interrupt_work; + long ioaddr = dev->base_addr; + long boguscnt = max_interrupt_work; unsigned int num_tx = 0; int handled = 0; - writel(0, dev->base_addr + IMR); + spin_lock(&np->lock); - ioaddr = dev->base_addr; - np = (struct netdev_private *) dev->priv; + writel(0, ioaddr + IMR); do { u32 intr_status = readl(ioaddr + ISR); @@ -1467,8 +1522,11 @@ if (intr_status & (RI | RBU)) { if (intr_status & RI) netdev_rx(dev); - else + else { + stop_nic_rx(ioaddr, np->crvalue); reset_rx_descriptors(dev); + writel(np->crvalue, ioaddr + TCRRCR); + } } while (np->really_tx_count) { @@ -1486,7 +1544,7 @@ if (tx_status & TXOWN) break; - if (!(np->crvalue & 0x02000000)) { + if (!(np->crvalue & CR_W_ENH)) { if (tx_status & (CSL | LC | EC | UDF | HF)) { np->stats.tx_errors++; if (tx_status & EC) @@ -1535,7 +1593,7 @@ netif_wake_queue(dev); /* read transmit status for enhanced mode only */ - if (np->crvalue & 0x02000000) { + if (np->crvalue & CR_W_ENH) { long data; data = readl(ioaddr + TSR); @@ -1548,6 +1606,20 @@ if (--boguscnt < 0) { printk(KERN_WARNING "%s: Too much work at interrupt, " "status=0x%4.4x.\n", dev->name, intr_status); + if (!np->reset_timer_armed) { + np->reset_timer_armed = 1; + np->reset_timer.expires = RUN_AT(HZ/2); + add_timer(&np->reset_timer); + stop_nic_rxtx(ioaddr, 0); + netif_stop_queue(dev); + /* or netif_tx_disable(dev); ?? */ + /* Prevent other paths from enabling tx,rx,intrs */ + np->crvalue_sv = np->crvalue; + np->imrvalue_sv = np->imrvalue; + np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */ + np->imrvalue = 0; + } + break; } } while (1); @@ -1565,6 +1637,8 @@ writel(np->imrvalue, ioaddr + IMR); + spin_unlock(&np->lock); + return IRQ_RETVAL(handled); } @@ -1574,9 +1648,10 @@ static int netdev_rx(struct net_device *dev) { struct netdev_private *np = dev->priv; + long ioaddr = dev->base_addr; /* If EOP is set on the next entry, it's a new packet. Send it up. */ - while (!(np->cur_rx->status & RXOWN)) { + while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) { s32 rx_status = np->cur_rx->status; if (np->really_rx_count == 0) @@ -1628,11 +1703,20 @@ np->stats.rx_length_errors++; /* free all rx descriptors related this long pkt */ - for (i = 0; i < desno; ++i) - free_one_rx_descriptor(np); + for (i = 0; i < desno; ++i) { + if (!np->cur_rx->skbuff) { + printk(KERN_DEBUG + "%s: I'm scared\n", dev->name); + break; + } + np->cur_rx->status = RXOWN; + np->cur_rx = np->cur_rx->next_desc_logical; + } continue; - } else { /* something error, need to reset this chip */ + } else { /* rx error, need to reset this chip */ + stop_nic_rx(ioaddr, np->crvalue); reset_rx_descriptors(dev); + writel(np->crvalue, ioaddr + TCRRCR); } break; /* exit the while loop */ } @@ -1671,8 +1755,6 @@ } else { skb_put(skb = np->cur_rx->skbuff, pkt_len); np->cur_rx->skbuff = NULL; - if (np->really_rx_count == RX_RING_SIZE) - np->lack_rxbuf = np->cur_rx; --np->really_rx_count; } skb->protocol = eth_type_trans(skb, dev); @@ -1682,22 +1764,7 @@ np->stats.rx_bytes += pkt_len; } - if (np->cur_rx->skbuff == NULL) { - struct sk_buff *skb; - - skb = dev_alloc_skb(np->rx_buf_sz); - - if (skb != NULL) { - skb->dev = dev; /* Mark as being used by this device. */ - np->cur_rx->buffer = pci_map_single(np->pci_dev, skb->tail, - np->rx_buf_sz, PCI_DMA_FROMDEVICE); - np->cur_rx->skbuff = skb; - ++np->really_rx_count; - } - } - - if (np->cur_rx->skbuff != NULL) - free_one_rx_descriptor(np); + np->cur_rx = np->cur_rx->next_desc_logical; } /* end of while loop */ /* allocate skb for rx buffers */ @@ -1721,8 +1788,21 @@ return &np->stats; } + +/* for dev->set_multicast_list */ static void set_rx_mode(struct net_device *dev) { + spinlock_t *lp = &((struct netdev_private *)dev->priv)->lock; + unsigned long flags; + spin_lock_irqsave(lp, flags); + __set_rx_mode(dev); + spin_unlock_irqrestore(&lp, flags); +} + + +/* Take lock before calling */ +static void __set_rx_mode(struct net_device *dev) +{ struct netdev_private *np = dev->priv; long ioaddr = dev->base_addr; u32 mc_filter[2]; /* Multicast hash filter */ @@ -1732,12 +1812,12 @@ /* Unconditionally log net taps. */ printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = PROM | AB | AM; + rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM; } else if ((dev->mc_count > multicast_filter_limit) || (dev->flags & IFF_ALLMULTI)) { /* Too many to match, or accept all multicasts. */ memset(mc_filter, 0xff, sizeof(mc_filter)); - rx_mode = AB | AM; + rx_mode = CR_W_AB | CR_W_AM; } else { struct dev_mc_list *mclist; int i; @@ -1749,26 +1829,25 @@ bit = (ether_crc(ETH_ALEN, mclist->dmi_addr) >> 26) ^ 0x3F; mc_filter[bit >> 5] |= (1 << bit); } - rx_mode = AB | AM; + rx_mode = CR_W_AB | CR_W_AM; } - stop_nic_tx(ioaddr, np->crvalue); - stop_nic_rx(ioaddr, np->crvalue & (~0x40000)); + stop_nic_rxtx(ioaddr, np->crvalue); writel(mc_filter[0], ioaddr + MAR0); writel(mc_filter[1], ioaddr + MAR1); - np->crvalue &= ~RxModeMask; + np->crvalue &= ~CR_W_RXMODEMASK; np->crvalue |= rx_mode; writel(np->crvalue, ioaddr + TCRRCR); } -static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) +static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct netdev_private *np = dev->priv; - strcpy (info->driver, DRV_NAME); - strcpy (info->version, DRV_VERSION); - strcpy (info->bus_info, pci_name(np->pci_dev)); + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(np->pci_dev)); } static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) @@ -1858,10 +1937,10 @@ writel(0x0000, ioaddr + IMR); /* Stop the chip's Tx and Rx processes. */ - stop_nic_tx(ioaddr, 0); - stop_nic_rx(ioaddr, 0); + stop_nic_rxtx(ioaddr, 0); del_timer_sync(&np->timer); + del_timer_sync(&np->reset_timer); free_irq(dev->irq, dev); @@ -1912,7 +1991,7 @@ { /* when a module, this is printed whether or not devices are found in probe */ #ifdef MODULE - printk (version); + printk(version); #endif return pci_module_init(&fealnx_driver); diff -urN linux-2.4.26/drivers/net/hamradio/6pack.c linux-2.4.27/drivers/net/hamradio/6pack.c --- linux-2.4.26/drivers/net/hamradio/6pack.c 2002-02-25 11:37:59.000000000 -0800 +++ linux-2.4.27/drivers/net/hamradio/6pack.c 2004-08-07 16:26:05.114365679 -0700 @@ -6,6 +6,9 @@ * Version: @(#)6pack.c 0.3.0 04/07/98 * * Authors: Andreas Könsgen + * Changes for SuSE Kernel 2.4.21-99 (stolen from 2.6.0-test8) + * to avoid the "resyncing TNC" messages: + * Tim Fischer * * Quite a lot of stuff "stolen" by Jörg Reuter from slip.c, written by * @@ -67,11 +70,11 @@ #define SIXP_DAMA_OFF 0 /* default level 2 parameters */ -#define SIXP_TXDELAY 25 /* in 10 ms */ +#define SIXP_TXDELAY (HZ/4) /* in 1 s */ #define SIXP_PERSIST 50 /* in 256ths */ -#define SIXP_SLOTTIME 10 /* in 10 ms */ -#define SIXP_INIT_RESYNC_TIMEOUT 150 /* in 10 ms */ -#define SIXP_RESYNC_TIMEOUT 500 /* in 10 ms */ +#define SIXP_SLOTTIME (HZ/10) /* in 1 s */ +#define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */ +#define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */ /* 6pack configuration. */ #define SIXP_NRUNIT 31 /* MAX number of 6pack channels */ diff -urN linux-2.4.26/drivers/net/ibmveth.c linux-2.4.27/drivers/net/ibmveth.c --- linux-2.4.26/drivers/net/ibmveth.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/net/ibmveth.c 2004-08-07 16:26:05.117365802 -0700 @@ -0,0 +1,1121 @@ +/**************************************************************************/ +/* */ +/* IBM eServer i/pSeries Virtual Ethernet Device Driver */ +/* Copyright (C) 2003 Dave Larson (larson1@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 IBM i/pSeries LPAR Linux. It utilizes the logical LAN */ +/* option of the RS/6000 Platform Architechture to interface with virtual */ +/* ethernet NICs that are presented to the partition by the hypervisor. */ +/* */ +/**************************************************************************/ +/* + TODO: + - remove frag processing code - no longer needed + - add support for sysfs + - possibly remove procfs support +*/ + +#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 "ibmveth.h" + +#define DEBUG 1 + +#define ibmveth_printk(fmt, args...) \ + printk(KERN_INFO "%s: " fmt, __FILE__, ## args) + +#define ibmveth_error_printk(fmt, args...) \ + printk(KERN_ERR "(%s:%3.3d ua:%lx) ERROR: " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args) + +#ifdef DEBUG +#define ibmveth_debug_printk_no_adapter(fmt, args...) \ + printk(KERN_DEBUG "(%s:%3.3d): " fmt, __FILE__, __LINE__ , ## args) +#define ibmveth_debug_printk(fmt, args...) \ + printk(KERN_DEBUG "(%s:%3.3d ua:%lx): " fmt, __FILE__, __LINE__ , adapter->vdev->unit_address, ## args) +#define ibmveth_assert(expr) \ + if(!(expr)) { \ + printk(KERN_DEBUG "assertion failed (%s:%3.3d ua:%lx): %s\n", __FILE__, __LINE__, adapter->vdev->unit_address, #expr); \ + BUG(); \ + } +#else +#define ibmveth_debug_printk_no_adapter(fmt, args...) +#define ibmveth_debug_printk(fmt, args...) +#define ibmveth_assert(expr) +#endif + +static int ibmveth_open(struct net_device *dev); +static int ibmveth_close(struct net_device *dev); +static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int ibmveth_poll(struct net_device *dev, int *budget); +static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *dev); +static void ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static struct net_device_stats *ibmveth_get_stats(struct net_device *dev); +static void ibmveth_set_multicast_list(struct net_device *dev); +static void ibmveth_proc_register_driver(void); +static void ibmveth_proc_unregister_driver(void); +static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); +static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); + +#define IBMVETH_PROC_DIR "ibmveth" +static struct proc_dir_entry *ibmveth_proc_dir; + +static const char ibmveth_driver_name[] = "ibmveth"; +static const char ibmveth_driver_string[] = "IBM i/pSeries Virtual Ethernet Driver"; +static const char ibmveth_driver_version[] = "1.0"; + +MODULE_AUTHOR("Dave Larson "); +MODULE_DESCRIPTION("IBM i/pSeries Virtual Ethernet Driver"); +MODULE_LICENSE("GPL"); + +/* simple methods of getting data from the current rxq entry */ +static inline int ibmveth_rxq_pending_buffer(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].toggle == adapter->rx_queue.toggle); +} + +static inline int ibmveth_rxq_buffer_valid(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].valid); +} + +static inline int ibmveth_rxq_frame_offset(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].offset); +} + +static inline int ibmveth_rxq_frame_length(struct ibmveth_adapter *adapter) +{ + return (adapter->rx_queue.queue_addr[adapter->rx_queue.index].length); +} + +/* setup the initial settings for a buffer pool */ +static void ibmveth_init_buffer_pool(struct ibmveth_buff_pool *pool, u32 pool_index, u32 pool_size, u32 buff_size) +{ + pool->size = pool_size; + pool->index = pool_index; + pool->buff_size = buff_size; + pool->threshold = pool_size / 2; +} + +/* allocate and setup an buffer pool - called during open */ +static int ibmveth_alloc_buffer_pool(struct ibmveth_buff_pool *pool) +{ + int i; + + pool->free_map = kmalloc(sizeof(u16) * pool->size, GFP_KERNEL); + + if(!pool->free_map) { + return -1; + } + + pool->dma_addr = kmalloc(sizeof(dma_addr_t) * pool->size, GFP_KERNEL); + if(!pool->dma_addr) { + kfree(pool->free_map); + pool->free_map = NULL; + return -1; + } + + pool->skbuff = kmalloc(sizeof(void*) * pool->size, GFP_KERNEL); + + if(!pool->skbuff) { + kfree(pool->dma_addr); + pool->dma_addr = NULL; + + kfree(pool->free_map); + pool->free_map = NULL; + return -1; + } + + memset(pool->skbuff, 0, sizeof(void*) * pool->size); + memset(pool->dma_addr, 0, sizeof(dma_addr_t) * pool->size); + + for(i = 0; i < pool->size; ++i) { + pool->free_map[i] = i; + } + + atomic_set(&pool->available, 0); + pool->producer_index = 0; + pool->consumer_index = 0; + + return 0; +} + +/* replenish the buffers for a pool. note that we don't need to + * skb_reserve these since they are used for incoming... + */ +static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struct ibmveth_buff_pool *pool) +{ + u32 i; + u32 count = pool->size - atomic_read(&pool->available); + u32 buffers_added = 0; + + mb(); + + for(i = 0; i < count; ++i) { + struct sk_buff *skb; + unsigned int free_index, index; + u64 correlator; + union ibmveth_buf_desc desc; + unsigned long lpar_rc; + dma_addr_t dma_addr; + + skb = alloc_skb(pool->buff_size, GFP_ATOMIC); + + if(!skb) { + ibmveth_debug_printk("replenish: unable to allocate skb\n"); + adapter->replenish_no_mem++; + break; + } + + free_index = pool->consumer_index++ % pool->size; + index = pool->free_map[free_index]; + + ibmveth_assert(index != 0xffff); + ibmveth_assert(pool->skbuff[index] == NULL); + + dma_addr = vio_map_single(adapter->vdev, skb->data, pool->buff_size, PCI_DMA_FROMDEVICE); + + pool->dma_addr[index] = dma_addr; + pool->skbuff[index] = skb; + + correlator = ((u64)pool->index << 32) | index; + *(u64*)skb->data = correlator; + + desc.desc = 0; + desc.fields.valid = 1; + desc.fields.length = pool->buff_size; + desc.fields.address = dma_addr; + + lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); + + if(lpar_rc != H_Success) { + pool->skbuff[index] = NULL; + pool->consumer_index--; + vio_unmap_single(adapter->vdev, pool->dma_addr[index], pool->buff_size, PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + adapter->replenish_add_buff_failure++; + break; + } else { + pool->free_map[free_index] = 0xffff; + buffers_added++; + adapter->replenish_add_buff_success++; + } + } + + atomic_add(buffers_added, &(pool->available)); +} + +/* check if replenishing is needed. */ +static inline int ibmveth_is_replenishing_needed(struct ibmveth_adapter *adapter) +{ + return ((atomic_read(&adapter->rx_buff_pool[0].available) < adapter->rx_buff_pool[0].threshold) || + (atomic_read(&adapter->rx_buff_pool[1].available) < adapter->rx_buff_pool[1].threshold) || + (atomic_read(&adapter->rx_buff_pool[2].available) < adapter->rx_buff_pool[2].threshold)); +} + +/* replenish tasklet routine */ +static void ibmveth_replenish_task(struct ibmveth_adapter *adapter) +{ + adapter->replenish_task_cycles++; + + ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[0]); + ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[1]); + ibmveth_replenish_buffer_pool(adapter, &adapter->rx_buff_pool[2]); + + adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); +} + +/* kick the replenish tasklet if we need replenishing and it isn't already running */ +static inline void ibmveth_schedule_replenishing(struct ibmveth_adapter *adapter) +{ + if(ibmveth_is_replenishing_needed(adapter)) { + tasklet_schedule(&adapter->replenish_task); + } +} + +/* empty and free ana buffer pool - also used to do cleanup in error paths */ +static void ibmveth_free_buffer_pool(struct ibmveth_adapter *adapter, struct ibmveth_buff_pool *pool) +{ + int i; + + if(pool->free_map) { + kfree(pool->free_map); + pool->free_map = NULL; + } + + if(pool->skbuff && pool->dma_addr) { + for(i = 0; i < pool->size; ++i) { + struct sk_buff *skb = pool->skbuff[i]; + if(skb) { + vio_unmap_single(adapter->vdev, + pool->dma_addr[i], + pool->buff_size, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + pool->skbuff[i] = NULL; + } + } + } + + if(pool->dma_addr) { + kfree(pool->dma_addr); + pool->dma_addr = NULL; + } + + if(pool->skbuff) { + kfree(pool->skbuff); + pool->skbuff = NULL; + } +} + +/* remove a buffer from a pool */ +static void ibmveth_remove_buffer_from_pool(struct ibmveth_adapter *adapter, u64 correlator) +{ + unsigned int pool = correlator >> 32; + unsigned int index = correlator & 0xffffffffUL; + unsigned int free_index; + struct sk_buff *skb; + + ibmveth_assert(pool < IbmVethNumBufferPools); + ibmveth_assert(index < adapter->rx_buff_pool[pool].size); + + skb = adapter->rx_buff_pool[pool].skbuff[index]; + + ibmveth_assert(skb != NULL); + + adapter->rx_buff_pool[pool].skbuff[index] = NULL; + + vio_unmap_single(adapter->vdev, + adapter->rx_buff_pool[pool].dma_addr[index], + adapter->rx_buff_pool[pool].buff_size, + PCI_DMA_FROMDEVICE); + + free_index = adapter->rx_buff_pool[pool].producer_index++ % adapter->rx_buff_pool[pool].size; + adapter->rx_buff_pool[pool].free_map[free_index] = index; + + mb(); + + atomic_dec(&(adapter->rx_buff_pool[pool].available)); +} + +/* get the current buffer on the rx queue */ +static inline struct sk_buff *ibmveth_rxq_get_buffer(struct ibmveth_adapter *adapter) +{ + u64 correlator = adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator; + unsigned int pool = correlator >> 32; + unsigned int index = correlator & 0xffffffffUL; + + ibmveth_assert(pool < IbmVethNumBufferPools); + ibmveth_assert(index < adapter->rx_buff_pool[pool].size); + + return adapter->rx_buff_pool[pool].skbuff[index]; +} + +/* recycle the current buffer on the rx queue */ +static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter) +{ + u32 q_index = adapter->rx_queue.index; + u64 correlator = adapter->rx_queue.queue_addr[q_index].correlator; + unsigned int pool = correlator >> 32; + unsigned int index = correlator & 0xffffffffUL; + union ibmveth_buf_desc desc; + unsigned long lpar_rc; + + ibmveth_assert(pool < IbmVethNumBufferPools); + ibmveth_assert(index < adapter->rx_buff_pool[pool].size); + + desc.desc = 0; + desc.fields.valid = 1; + desc.fields.length = adapter->rx_buff_pool[pool].buff_size; + desc.fields.address = adapter->rx_buff_pool[pool].dma_addr[index]; + + lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); + + if(lpar_rc != H_Success) { + ibmveth_debug_printk("h_add_logical_lan_buffer failed during recycle rc=%ld", lpar_rc); + ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator); + } + + if(++adapter->rx_queue.index == adapter->rx_queue.num_slots) { + adapter->rx_queue.index = 0; + adapter->rx_queue.toggle = !adapter->rx_queue.toggle; + } +} + +static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) +{ + ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator); + + if(++adapter->rx_queue.index == adapter->rx_queue.num_slots) { + adapter->rx_queue.index = 0; + adapter->rx_queue.toggle = !adapter->rx_queue.toggle; + } +} + +static void ibmveth_cleanup(struct ibmveth_adapter *adapter) +{ + if(adapter->buffer_list_addr != NULL) { + if(adapter->buffer_list_dma != NO_TCE) { + vio_unmap_single(adapter->vdev, adapter->buffer_list_dma, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->buffer_list_dma = NO_TCE; + } + free_page((unsigned long)adapter->buffer_list_addr); + adapter->buffer_list_addr = NULL; + } + + if(adapter->filter_list_addr != NULL) { + if(adapter->filter_list_dma != NO_TCE) { + vio_unmap_single(adapter->vdev, adapter->filter_list_dma, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->filter_list_dma = NO_TCE; + } + free_page((unsigned long)adapter->filter_list_addr); + adapter->filter_list_addr = NULL; + } + + if(adapter->rx_queue.queue_addr != NULL) { + if(adapter->rx_queue.queue_dma != NO_TCE) { + vio_unmap_single(adapter->vdev, adapter->rx_queue.queue_dma, adapter->rx_queue.queue_len, PCI_DMA_BIDIRECTIONAL); + adapter->rx_queue.queue_dma = NO_TCE; + } + kfree(adapter->rx_queue.queue_addr); + adapter->rx_queue.queue_addr = NULL; + } + + ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[0]); + ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[1]); + ibmveth_free_buffer_pool(adapter, &adapter->rx_buff_pool[2]); +} + +static int ibmveth_open(struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + u64 mac_address = 0; + int rxq_entries; + unsigned long lpar_rc; + int rc; + union ibmveth_buf_desc rxq_desc; + + ibmveth_debug_printk("open starting\n"); + + rxq_entries = + adapter->rx_buff_pool[0].size + + adapter->rx_buff_pool[1].size + + adapter->rx_buff_pool[2].size + 1; + + adapter->buffer_list_addr = (void*) get_zeroed_page(GFP_KERNEL); + adapter->filter_list_addr = (void*) get_zeroed_page(GFP_KERNEL); + + if(!adapter->buffer_list_addr || !adapter->filter_list_addr) { + ibmveth_error_printk("unable to allocate filter or buffer list pages\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + adapter->rx_queue.queue_len = sizeof(struct ibmveth_rx_q_entry) * rxq_entries; + adapter->rx_queue.queue_addr = kmalloc(adapter->rx_queue.queue_len, GFP_KERNEL); + + if(!adapter->rx_queue.queue_addr) { + ibmveth_error_printk("unable to allocate rx queue pages\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + adapter->buffer_list_dma = vio_map_single(adapter->vdev, adapter->buffer_list_addr, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->filter_list_dma = vio_map_single(adapter->vdev, adapter->filter_list_addr, 4096, PCI_DMA_BIDIRECTIONAL); + adapter->rx_queue.queue_dma = vio_map_single(adapter->vdev, adapter->rx_queue.queue_addr, adapter->rx_queue.queue_len, PCI_DMA_BIDIRECTIONAL); + + if((adapter->buffer_list_dma == NO_TCE) || + (adapter->filter_list_dma == NO_TCE) || + (adapter->rx_queue.queue_dma == NO_TCE)) { + ibmveth_error_printk("unable to map filter or buffer list pages\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + adapter->rx_queue.index = 0; + adapter->rx_queue.num_slots = rxq_entries; + adapter->rx_queue.toggle = 1; + + if(ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[0]) || + ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[1]) || + ibmveth_alloc_buffer_pool(&adapter->rx_buff_pool[2])) + { + ibmveth_error_printk("unable to allocate buffer pools\n"); + ibmveth_cleanup(adapter); + return -ENOMEM; + } + + memcpy(&mac_address, netdev->dev_addr, netdev->addr_len); + mac_address = mac_address >> 16; + + rxq_desc.desc = 0; + rxq_desc.fields.valid = 1; + rxq_desc.fields.length = adapter->rx_queue.queue_len; + rxq_desc.fields.address = adapter->rx_queue.queue_dma; + + ibmveth_debug_printk("buffer list @ 0x%p\n", adapter->buffer_list_addr); + ibmveth_debug_printk("filter list @ 0x%p\n", adapter->filter_list_addr); + ibmveth_debug_printk("receive q @ 0x%p\n", adapter->rx_queue.queue_addr); + + + lpar_rc = h_register_logical_lan(adapter->vdev->unit_address, + adapter->buffer_list_dma, + rxq_desc.desc, + adapter->filter_list_dma, + mac_address); + + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_register_logical_lan failed with %ld\n", lpar_rc); + ibmveth_error_printk("buffer TCE:0x%x filter TCE:0x%x rxq desc:0x%lx MAC:0x%lx\n", + adapter->buffer_list_dma, + adapter->filter_list_dma, + rxq_desc.desc, + mac_address); + ibmveth_cleanup(adapter); + return -ENONET; + } + + ibmveth_debug_printk("registering irq 0x%x\n", netdev->irq); + if((rc = request_irq(netdev->irq, &ibmveth_interrupt, 0, netdev->name, netdev)) != 0) { + ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc); + h_free_logical_lan(adapter->vdev->unit_address); + ibmveth_cleanup(adapter); + return rc; + } + + netif_start_queue(netdev); + + ibmveth_debug_printk("scheduling initial replenish cycle\n"); + ibmveth_schedule_replenishing(adapter); + + ibmveth_debug_printk("open complete\n"); + + return 0; +} + +static int ibmveth_close(struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + long lpar_rc; + + ibmveth_debug_printk("close starting\n"); + + netif_stop_queue(netdev); + + free_irq(netdev->irq, netdev); + + tasklet_kill(&adapter->replenish_task); + + lpar_rc = h_free_logical_lan(adapter->vdev->unit_address); + + if(lpar_rc != H_Success) + { + ibmveth_error_printk("h_free_logical_lan failed with %lx, continuing with close\n", + lpar_rc); + } + + adapter->rx_no_buffer = *(u64*)(((char*)adapter->buffer_list_addr) + 4096 - 8); + + ibmveth_cleanup(adapter); + + ibmveth_debug_printk("close complete\n"); + + return 0; +} + +static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { + cmd->supported = (SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_FIBRE); + cmd->advertising = (ADVERTISED_1000baseT_Full | ADVERTISED_Autoneg | ADVERTISED_FIBRE); + cmd->speed = SPEED_1000; + cmd->duplex = DUPLEX_FULL; + cmd->port = PORT_FIBRE; + cmd->phy_address = 0; + cmd->transceiver = XCVR_INTERNAL; + cmd->autoneg = AUTONEG_ENABLE; + cmd->maxtxpkt = 0; + cmd->maxrxpkt = 1; + return 0; +} + +static void netdev_get_drvinfo (struct net_device *dev, struct ethtool_drvinfo *info) { + strncpy(info->driver, ibmveth_driver_name, sizeof(info->driver) - 1); + strncpy(info->version, ibmveth_driver_version, sizeof(info->version) - 1); +} + +static u32 netdev_get_link(struct net_device *dev) { + return 0; +} + +static struct ethtool_ops netdev_ethtool_ops = { + .get_drvinfo = netdev_get_drvinfo, + .get_settings = netdev_get_settings, + .get_link = netdev_get_link, + .get_sg = ethtool_op_get_sg, + .get_tx_csum = ethtool_op_get_tx_csum, +}; + +static int ibmveth_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + return -EOPNOTSUPP; +} + +#define page_offset(v) ((unsigned long)(v) & ((1 << 12) - 1)) + +static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + union ibmveth_buf_desc desc[IbmVethMaxSendFrags]; + unsigned long lpar_rc; + int nfrags = 0, curfrag; + + if ((skb_shinfo(skb)->nr_frags + 1) > IbmVethMaxSendFrags) { + adapter->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + memset(&desc, 0, sizeof(desc)); + + /* nfrags = number of frags after the initial fragment */ + nfrags = skb_shinfo(skb)->nr_frags; + + if(nfrags) + adapter->tx_multidesc_send++; + + /* map the initial fragment */ + desc[0].fields.length = nfrags ? skb->len - skb->data_len : skb->len; + desc[0].fields.address = vio_map_single(adapter->vdev, skb->data, desc[0].fields.length, PCI_DMA_TODEVICE); + desc[0].fields.valid = 1; + + if(desc[0].fields.address == NO_TCE) { + ibmveth_error_printk("tx: unable to map initial fragment\n"); + adapter->tx_map_failed++; + adapter->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + curfrag = nfrags; + + /* map fragments past the initial portion if there are any */ + while(curfrag--) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[curfrag]; + desc[curfrag+1].fields.address = vio_map_single(adapter->vdev, + page_address(frag->page) + frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + desc[curfrag+1].fields.length = frag->size; + desc[curfrag+1].fields.valid = 1; + + if(desc[curfrag+1].fields.address == NO_TCE) { + ibmveth_error_printk("tx: unable to map fragment %d\n", curfrag); + adapter->tx_map_failed++; + adapter->stats.tx_dropped++; + /* Free all the mappings we just created */ + while(curfrag < nfrags) { + vio_unmap_single(adapter->vdev, + desc[curfrag+1].fields.address, + desc[curfrag+1].fields.length, + PCI_DMA_TODEVICE); + curfrag++; + } + dev_kfree_skb(skb); + return 0; + } + } + + /* send the frame. Arbitrarily set retrycount to 1024 */ + unsigned long correlator = 0; + unsigned int retry_count = 1024; + do { + lpar_rc = h_send_logical_lan(adapter->vdev->unit_address, + desc[0].desc, + desc[1].desc, + desc[2].desc, + desc[3].desc, + desc[4].desc, + desc[5].desc, + correlator); + } while ((lpar_rc == H_Busy) && (retry_count--)); + + if(lpar_rc != H_Success && lpar_rc != H_Dropped) { + int i; + ibmveth_error_printk("tx: h_send_logical_lan failed with rc=%ld\n", lpar_rc); + for(i = 0; i < 6; i++) { + ibmveth_error_printk("tx: desc[%i] valid=%d, len=%d, address=0x%d\n", i, + desc[i].fields.valid, desc[i].fields.length, desc[i].fields.address); + } + adapter->tx_send_failed++; + adapter->stats.tx_dropped++; + } else { + adapter->stats.tx_packets++; + adapter->stats.tx_bytes += skb->len; + } + + do { + vio_unmap_single(adapter->vdev, desc[nfrags].fields.address, desc[nfrags].fields.length, PCI_DMA_TODEVICE); + } while(--nfrags >= 0); + + dev_kfree_skb(skb); + return 0; +} + +static int ibmveth_poll(struct net_device *netdev, int *budget) +{ + struct ibmveth_adapter *adapter = netdev->priv; + int max_frames_to_process = netdev->quota; + int frames_processed = 0; + int more_work = 1; + unsigned long lpar_rc; + + restart_poll: + do { + struct net_device *netdev = adapter->netdev; + + if(ibmveth_rxq_pending_buffer(adapter)) { + struct sk_buff *skb; + + if(!ibmveth_rxq_buffer_valid(adapter)) { + wmb(); /* suggested by larson1 */ + adapter->rx_invalid_buffer++; + ibmveth_debug_printk("recycling invalid buffer\n"); + ibmveth_rxq_recycle_buffer(adapter); + } else { + int length = ibmveth_rxq_frame_length(adapter); + int offset = ibmveth_rxq_frame_offset(adapter); + skb = ibmveth_rxq_get_buffer(adapter); + + ibmveth_rxq_harvest_buffer(adapter); + + skb_reserve(skb, offset); + skb_put(skb, length); + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + + netif_receive_skb(skb); /* send it up */ + + adapter->stats.rx_packets++; + adapter->stats.rx_bytes += length; + frames_processed++; + } + } else { + more_work = 0; + } + } while(more_work && (frames_processed < max_frames_to_process)); + + ibmveth_schedule_replenishing(adapter); + + if(more_work) { + /* more work to do - return that we are not done yet */ + netdev->quota -= frames_processed; + *budget -= frames_processed; + return 1; + } + + /* we think we are done - reenable interrupts, then check once more to make sure we are done */ + lpar_rc = h_vio_signal(adapter->vdev->unit_address, IbmVethIntsEnabled); + ibmveth_assert(lpar_rc == H_Success); + + netif_rx_complete(netdev); + + if(ibmveth_rxq_pending_buffer(adapter) && netif_rx_reschedule(netdev, frames_processed)) + { + lpar_rc = h_vio_signal(adapter->vdev->unit_address, IbmVethIntsDisabled); + ibmveth_assert(lpar_rc == H_Success); + more_work = 1; + goto restart_poll; + } + + netdev->quota -= frames_processed; + *budget -= frames_processed; + + /* we really are done */ + return 0; +} + +static void ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct net_device *netdev = dev_instance; + struct ibmveth_adapter *adapter = netdev->priv; + unsigned long lpar_rc; + + if(netif_rx_schedule_prep(netdev)) { + lpar_rc = h_vio_signal(adapter->vdev->unit_address, IbmVethIntsDisabled); + ibmveth_assert(lpar_rc == H_Success); + __netif_rx_schedule(netdev); + } +} + +static struct net_device_stats *ibmveth_get_stats(struct net_device *dev) +{ + struct ibmveth_adapter *adapter = dev->priv; + return &adapter->stats; +} + +static void ibmveth_set_multicast_list(struct net_device *netdev) +{ + struct ibmveth_adapter *adapter = netdev->priv; + unsigned long lpar_rc; + + if((netdev->flags & IFF_PROMISC) || (netdev->mc_count > adapter->mcastFilterSize)) { + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastEnableRecv | + IbmVethMcastDisableFiltering, + 0); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when entering promisc mode\n", lpar_rc); + } + } else { + struct dev_mc_list *mclist = netdev->mc_list; + int i; + /* clear the filter table & disable filtering */ + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastEnableRecv | + IbmVethMcastDisableFiltering | + IbmVethMcastClearFilterTable, + 0); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when attempting to clear filter table\n", lpar_rc); + } + /* add the addresses to the filter table */ + for(i = 0; i < netdev->mc_count; ++i, mclist = mclist->next) { + // add the multicast address to the filter table + unsigned long mcast_addr = 0; + memcpy(((char *)&mcast_addr)+2, mclist->dmi_addr, 6); + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastAddFilter, + mcast_addr); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when adding an entry to the filter table\n", lpar_rc); + } + } + + /* re-enable filtering */ + lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, + IbmVethMcastEnableFiltering, + 0); + if(lpar_rc != H_Success) { + ibmveth_error_printk("h_multicast_ctrl rc=%ld when enabling filtering\n", lpar_rc); + } + } +} + +static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) +{ + int rc; + struct net_device *netdev; + struct ibmveth_adapter *adapter; + + unsigned int *mac_addr_p; + unsigned int *mcastFilterSize_p; + + + ibmveth_debug_printk_no_adapter("entering ibmveth_probe for UA 0x%lx\n", + dev->unit_address); + + mac_addr_p = (unsigned int *) vio_get_attribute(dev, VETH_MAC_ADDR, 0); + if(!mac_addr_p) { + ibmveth_error_printk("Can't find VETH_MAC_ADDR attribute\n"); + return 0; + } + + mcastFilterSize_p= (unsigned int *) vio_get_attribute(dev, VETH_MCAST_FILTER_SIZE, 0); + if(!mcastFilterSize_p) { + ibmveth_error_printk("Can't find VETH_MCAST_FILTER_SIZE attribute\n"); + return 0; + } + + netdev = alloc_etherdev(sizeof(struct ibmveth_adapter)); + + if(!netdev) + return -ENOMEM; + + SET_MODULE_OWNER(netdev); + + adapter = netdev->priv; + memset(adapter, 0, sizeof(adapter)); + dev->driver_data = netdev; + + adapter->vdev = dev; + adapter->netdev = netdev; + adapter->mcastFilterSize= *mcastFilterSize_p; + + /* Some older boxes running PHYP non-natively have an OF that + returns a 8-byte local-mac-address field (and the first + 2 bytes have to be ignored) while newer boxes' OF return + a 6-byte field. Note that IEEE 1275 specifies that + local-mac-address must be a 6-byte field. + The RPA doc specifies that the first byte must be 10b, so + we'll just look for it to solve this 8 vs. 6 byte field issue */ + + while (*((char*)mac_addr_p) != (char)(0x02)) + ((char*)mac_addr_p)++; + + adapter->mac_addr = 0; + memcpy(&adapter->mac_addr, mac_addr_p, 6); + + adapter->liobn = dev->tce_table->index; + + netdev->irq = dev->irq; + netdev->open = ibmveth_open; + netdev->poll = ibmveth_poll; + netdev->weight = 16; + netdev->stop = ibmveth_close; + netdev->hard_start_xmit = ibmveth_start_xmit; + netdev->get_stats = ibmveth_get_stats; + netdev->set_multicast_list = ibmveth_set_multicast_list; + netdev->do_ioctl = ibmveth_ioctl; + netdev->ethtool_ops = &netdev_ethtool_ops; + + memcpy(&netdev->dev_addr, &adapter->mac_addr, netdev->addr_len); + + ibmveth_init_buffer_pool(&adapter->rx_buff_pool[0], 0, IbmVethPool0DftCnt, IbmVethPool0DftSize); + ibmveth_init_buffer_pool(&adapter->rx_buff_pool[1], 1, IbmVethPool1DftCnt, IbmVethPool1DftSize); + ibmveth_init_buffer_pool(&adapter->rx_buff_pool[2], 2, IbmVethPool2DftCnt, IbmVethPool2DftSize); + + ibmveth_debug_printk("adapter @ 0x%p\n", adapter); + + tasklet_init(&adapter->replenish_task, (void*)ibmveth_replenish_task, (unsigned long)adapter); + + adapter->buffer_list_dma = NO_TCE; + adapter->filter_list_dma = NO_TCE; + adapter->rx_queue.queue_dma = NO_TCE; + + ibmveth_debug_printk("registering netdev...\n"); + + rc = register_netdev(netdev); + + if(rc) { + ibmveth_debug_printk("failed to register netdev rc=%d\n", rc); + free_netdev(netdev); + return rc; + } + + ibmveth_debug_printk("registered\n"); + + ibmveth_proc_register_adapter(adapter); + + return 0; +} + +static void __devexit ibmveth_remove(struct vio_dev *dev) +{ + struct net_device *netdev = dev->driver_data; + struct ibmveth_adapter *adapter = netdev->priv; + + unregister_netdev(netdev); + + ibmveth_proc_unregister_adapter(adapter); + + free_netdev(netdev); + return; +} + +#ifdef CONFIG_PROC_FS +static void ibmveth_proc_register_driver(void) +{ + ibmveth_proc_dir = create_proc_entry(IBMVETH_PROC_DIR, S_IFDIR, proc_net); + if (ibmveth_proc_dir) { + SET_MODULE_OWNER(ibmveth_proc_dir); + } +} + +static void ibmveth_proc_unregister_driver(void) +{ + remove_proc_entry(IBMVETH_PROC_DIR, proc_net); +} + +static void *ibmveth_seq_start(struct seq_file *seq, loff_t *pos) +{ + if (*pos == 0) { + return (void *)1; + } else { + return NULL; + } +} + +static void *ibmveth_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void ibmveth_seq_stop(struct seq_file *seq, void *v) +{ +} + +static int ibmveth_seq_show(struct seq_file *seq, void *v) +{ + struct ibmveth_adapter *adapter = seq->private; + char *current_mac = ((char*) &adapter->netdev->dev_addr); + char *firmware_mac = ((char*) &adapter->mac_addr) ; + + seq_printf(seq, "%s %s\n\n", ibmveth_driver_string, ibmveth_driver_version); + + seq_printf(seq, "Unit Address: 0x%lx\n", adapter->vdev->unit_address); + seq_printf(seq, "LIOBN: 0x%lx\n", adapter->liobn); + seq_printf(seq, "Current MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + current_mac[0], current_mac[1], current_mac[2], + current_mac[3], current_mac[4], current_mac[5]); + seq_printf(seq, "Firmware MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + firmware_mac[0], firmware_mac[1], firmware_mac[2], + firmware_mac[3], firmware_mac[4], firmware_mac[5]); + + seq_printf(seq, "\nAdapter Statistics:\n"); + seq_printf(seq, " TX: skbuffs linearized: %ld\n", adapter->tx_linearized); + seq_printf(seq, " multi-descriptor sends: %ld\n", adapter->tx_multidesc_send); + seq_printf(seq, " skb_linearize failures: %ld\n", adapter->tx_linearize_failed); + seq_printf(seq, " vio_map_single failres: %ld\n", adapter->tx_map_failed); + seq_printf(seq, " send failures: %ld\n", adapter->tx_send_failed); + seq_printf(seq, " RX: replenish task cycles: %ld\n", adapter->replenish_task_cycles); + seq_printf(seq, " alloc_skb_failures: %ld\n", adapter->replenish_no_mem); + seq_printf(seq, " add buffer failures: %ld\n", adapter->replenish_add_buff_failure); + seq_printf(seq, " invalid buffers: %ld\n", adapter->rx_invalid_buffer); + seq_printf(seq, " no buffers: %ld\n", adapter->rx_no_buffer); + + return 0; +} +static struct seq_operations ibmveth_seq_ops = { + .start = ibmveth_seq_start, + .next = ibmveth_seq_next, + .stop = ibmveth_seq_stop, + .show = ibmveth_seq_show, +}; + +static int ibmveth_proc_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct proc_dir_entry *proc; + int rc; + + rc = seq_open(file, &ibmveth_seq_ops); + if (!rc) { + /* recover the pointer buried in proc_dir_entry data */ + seq = file->private_data; + proc = PDE(inode); + seq->private = proc->data; + } + return rc; +} + +static struct file_operations ibmveth_proc_fops = { + .owner = THIS_MODULE, + .open = ibmveth_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter) +{ + struct proc_dir_entry *entry; + if (ibmveth_proc_dir) { + entry = create_proc_entry(adapter->netdev->name, S_IFREG, ibmveth_proc_dir); + if (!entry) { + ibmveth_error_printk("Cannot create adapter proc entry"); + } else { + entry->data = (void *) adapter; + entry->proc_fops = &ibmveth_proc_fops; + SET_MODULE_OWNER(entry); + } + } + return; +} + +static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter) +{ + if (ibmveth_proc_dir) { + remove_proc_entry(adapter->netdev->name, ibmveth_proc_dir); + } +} + +#else /* CONFIG_PROC_FS */ +static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter) +{ +} + +static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter) +{ +} +static void ibmveth_proc_register_driver(void) +{ +} + +static void ibmveth_proc_unregister_driver(void) +{ +} +#endif /* CONFIG_PROC_FS */ + +static struct vio_device_id ibmveth_device_table[] __devinitdata= { + { "network", "IBM,l-lan"}, + { 0,} +}; + +MODULE_DEVICE_TABLE(vio, ibmveth_device_table); + +static struct vio_driver ibmveth_driver = { + .name = (char *)ibmveth_driver_name, + .id_table = ibmveth_device_table, + .probe = ibmveth_probe, + .remove = ibmveth_remove +}; + +static int __init ibmveth_module_init(void) +{ + int rc; + + ibmveth_printk("%s: %s %s\n", ibmveth_driver_name, ibmveth_driver_string, ibmveth_driver_version); + + ibmveth_proc_register_driver(); + + rc = vio_module_init(&ibmveth_driver); + + return rc; +} + +static void __exit ibmveth_module_exit(void) +{ + vio_unregister_driver(&ibmveth_driver); + ibmveth_proc_unregister_driver(); +} + +module_init(ibmveth_module_init); +module_exit(ibmveth_module_exit); diff -urN linux-2.4.26/drivers/net/ibmveth.h linux-2.4.27/drivers/net/ibmveth.h --- linux-2.4.26/drivers/net/ibmveth.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/net/ibmveth.h 2004-08-07 16:26:05.118365843 -0700 @@ -0,0 +1,158 @@ +/**************************************************************************/ +/* */ +/* IBM eServer i/[Series Virtual Ethernet Device Driver */ +/* Copyright (C) 2003 Dave Larson (larson1@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 */ +/* */ +/**************************************************************************/ + +#ifndef _IBMVETH_H +#define _IBMVETH_H + +#define IbmVethMaxSendFrags 6 + +/* constants for H_MULTICAST_CTRL */ +#define IbmVethMcastReceptionModifyBit 0x80000UL +#define IbmVethMcastReceptionEnableBit 0x20000UL +#define IbmVethMcastFilterModifyBit 0x40000UL +#define IbmVethMcastFilterEnableBit 0x10000UL + +#define IbmVethMcastEnableRecv (IbmVethMcastReceptionModifyBit | IbmVethMcastReceptionEnableBit) +#define IbmVethMcastDisableRecv (IbmVethMcastReceptionModifyBit) +#define IbmVethMcastEnableFiltering (IbmVethMcastFilterModifyBit | IbmVethMcastFilterEnableBit) +#define IbmVethMcastDisableFiltering (IbmVethMcastFilterModifyBit) +#define IbmVethMcastAddFilter 0x1UL +#define IbmVethMcastRemoveFilter 0x2UL +#define IbmVethMcastClearFilterTable 0x3UL + +/* constants for H_VIO_SIGNAL */ +#define IbmVethIntsDisabled 0x0UL +#define IbmVethIntsEnabled 0x1UL + +/* hcall numbers */ +#define H_VIO_SIGNAL 0x104 +#define H_REGISTER_LOGICAL_LAN 0x114 +#define H_FREE_LOGICAL_LAN 0x118 +#define H_ADD_LOGICAL_LAN_BUFFER 0x11C +#define H_SEND_LOGICAL_LAN 0x120 +#define H_MULTICAST_CTRL 0x130 +#define H_CHANGE_LOGICAL_LAN_MAC 0x14C + +/* hcall macros */ +#define h_vio_signal(ua, mode) \ + plpar_hcall_norets(H_VIO_SIGNAL, ua, mode) + +#define h_register_logical_lan(ua, buflst, rxq, fltlst, mac) \ + plpar_hcall_norets(H_REGISTER_LOGICAL_LAN, ua, buflst, rxq, fltlst, mac) + +#define h_free_logical_lan(ua) \ + plpar_hcall_norets(H_FREE_LOGICAL_LAN, ua) + +#define h_add_logical_lan_buffer(ua, buf) \ + plpar_hcall_norets(H_ADD_LOGICAL_LAN_BUFFER, ua, buf) + +#define h_send_logical_lan(ua, buf1, buf2, buf3, buf4, buf5, buf6, correlator) \ + plpar_hcall_8arg_2ret(H_SEND_LOGICAL_LAN, ua, buf1, buf2, buf3, buf4, buf5, buf6, correlator, &correlator) + +#define h_multicast_ctrl(ua, cmd, mac) \ + plpar_hcall_norets(H_MULTICAST_CTRL, ua, cmd, mac) + +#define h_change_logical_lan_mac(ua, mac) \ + plpar_hcall_norets(H_CHANGE_LOGICAL_LAN_MAC, ua, mac) + +#define IbmVethNumBufferPools 3 +#define IbmVethPool0DftSize (1024 * 2) +#define IbmVethPool1DftSize (1024 * 4) +#define IbmVethPool2DftSize (1024 * 10) +#define IbmVethPool0DftCnt 256 +#define IbmVethPool1DftCnt 256 +#define IbmVethPool2DftCnt 256 + +struct ibmveth_buff_pool { + u32 size; + u32 index; + u32 buff_size; + u32 threshold; + atomic_t available; + u32 consumer_index; + u32 producer_index; + u16 *free_map; + dma_addr_t *dma_addr; + struct sk_buff **skbuff; +}; + +struct ibmveth_rx_q { + u64 index; + u64 num_slots; + u64 toggle; + dma_addr_t queue_dma; + u32 queue_len; + struct ibmveth_rx_q_entry *queue_addr; +}; + +struct ibmveth_adapter { + struct vio_dev *vdev; + struct net_device *netdev; + struct net_device_stats stats; + unsigned int mcastFilterSize; + unsigned long mac_addr; + unsigned long liobn; + void * buffer_list_addr; + void * filter_list_addr; + dma_addr_t buffer_list_dma; + dma_addr_t filter_list_dma; + struct ibmveth_buff_pool rx_buff_pool[IbmVethNumBufferPools]; + struct ibmveth_rx_q rx_queue; + /* helper tasks */ + struct tasklet_struct replenish_task; + /* adapter specific stats */ + u64 replenish_task_cycles; + u64 replenish_no_mem; + u64 replenish_add_buff_failure; + u64 replenish_add_buff_success; + u64 rx_invalid_buffer; + u64 rx_no_buffer; + u64 tx_multidesc_send; + u64 tx_linearized; + u64 tx_linearize_failed; + u64 tx_map_failed; + u64 tx_send_failed; +}; + +struct ibmveth_buf_desc_fields { + u32 valid : 1; + u32 toggle : 1; + u32 reserved : 6; + u32 length : 24; + u32 address; +}; + +union ibmveth_buf_desc { + u64 desc; + struct ibmveth_buf_desc_fields fields; +}; + +struct ibmveth_rx_q_entry { + u16 toggle : 1; + u16 valid : 1; + u16 reserved : 14; + u16 offset; + u32 length; + u64 correlator; +}; + +#endif /* _IBMVETH_H */ diff -urN linux-2.4.26/drivers/net/irda/via-ircc.c linux-2.4.27/drivers/net/irda/via-ircc.c --- linux-2.4.26/drivers/net/irda/via-ircc.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/net/irda/via-ircc.c 2004-08-07 16:26:05.119365884 -0700 @@ -1142,13 +1142,16 @@ st_fifo->head++; st_fifo->len--; - skb = dev_alloc_skb(len + 1 - 4); /* - * if frame size,data ptr,or skb ptr are wrong ,the get next + * if frame size or data ptr are wrong ,then get next * entry. */ - if ((skb == NULL) || (skb->data == NULL) - || (self->rx_buff.data == NULL) || (len < 6)) { + if ((self->rx_buff.data == NULL) || (len < 6)) { + self->stats.rx_dropped++; + return TRUE; + } + skb = dev_alloc_skb(len + 1 - 4); + if (!skb) { self->stats.rx_dropped++; return TRUE; } @@ -1194,8 +1197,12 @@ #ifdef DBGMSG DBG(printk(KERN_INFO "upload_rxdata: len=%x\n", len)); #endif + if ((len - 4) < 2) { + self->stats.rx_dropped++; + return FALSE; + } skb = dev_alloc_skb(len + 1); - if ((skb == NULL) || ((len - 4) < 2)) { + if (!skb) { self->stats.rx_dropped++; return FALSE; } @@ -1258,13 +1265,17 @@ st_fifo->head++; st_fifo->len--; - skb = dev_alloc_skb(len + 1 - 4); /* - * if frame size, data ptr, or skb ptr are wrong, + * if frame size or data ptr are wrong, * then get next entry. */ - if ((skb == NULL) || (skb->data == NULL) - || (self->rx_buff.data == NULL) || (len < 6)) { + if ((self->rx_buff.data == NULL) || (len < 6)) { + self->stats.rx_dropped++; + continue; + } + + skb = dev_alloc_skb(len + 1 - 4); + if (!skb) { self->stats.rx_dropped++; continue; } diff -urN linux-2.4.26/drivers/net/macsonic.c linux-2.4.27/drivers/net/macsonic.c --- linux-2.4.26/drivers/net/macsonic.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/net/macsonic.c 2004-08-07 16:26:05.120365925 -0700 @@ -199,6 +199,7 @@ if ((lp->rba = (char *) kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL | GFP_DMA)) == NULL) { printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name); + kfree(lp); return -ENOMEM; } @@ -635,19 +636,14 @@ } #ifdef MODULE -static char namespace[16] = ""; static struct net_device dev_macsonic; MODULE_PARM(sonic_debug, "i"); MODULE_PARM_DESC(sonic_debug, "macsonic debug level (1-4)"); -MODULE_LICENSE("GPL"); - -EXPORT_NO_SYMBOLS; int init_module(void) { - dev_macsonic.name = namespace; dev_macsonic.init = macsonic_probe; if (register_netdev(&dev_macsonic) != 0) { diff -urN linux-2.4.26/drivers/net/natsemi.c linux-2.4.27/drivers/net/natsemi.c --- linux-2.4.26/drivers/net/natsemi.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/natsemi.c 2004-08-07 16:26:05.121365967 -0700 @@ -387,7 +387,7 @@ IntrStatus = 0x10, IntrMask = 0x14, IntrEnable = 0x18, - IntrHoldoff = 0x16, /* DP83816 only */ + IntrHoldoff = 0x1C, /* DP83816 only */ TxRingPtr = 0x20, TxConfig = 0x24, RxRingPtr = 0x30, diff -urN linux-2.4.26/drivers/net/pcnet32.c linux-2.4.27/drivers/net/pcnet32.c --- linux-2.4.26/drivers/net/pcnet32.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/pcnet32.c 2004-08-07 16:26:05.127366213 -0700 @@ -1,12 +1,12 @@ /* pcnet32.c: An AMD PCnet32 ethernet driver for linux. */ /* * Copyright 1996-1999 Thomas Bogendoerfer - * + * * Derived from the lance driver written 1993,1994,1995 by Donald Becker. - * + * * Copyright 1993 United States Government as represented by the * Director, National Security Agency. - * + * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * @@ -22,19 +22,16 @@ *************************************************************************/ #define DRV_NAME "pcnet32" -#define DRV_VERSION "1.28" -#define DRV_RELDATE "02.20.2004" +#define DRV_VERSION "1.30c" +#define DRV_RELDATE "05.25.2004" #define PFX DRV_NAME ": " static const char *version = DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n"; #include - #include -#include #include -#include #include #include #include @@ -45,16 +42,17 @@ #include #include #include -#include -#include -#include -#include - #include #include #include #include +#include +#include +#include +#include +#include + /* * PCI device identifiers for "new style" Linux PCI Device Drivers */ @@ -74,10 +72,10 @@ static int cards_found __devinitdata; -/* - * VLB I/O addresses +/* + * VLB I/O addresses */ -static unsigned int pcnet32_portlist[] __initdata = +static unsigned int pcnet32_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0 }; @@ -88,7 +86,7 @@ static struct net_device *pcnet32_dev; -static int max_interrupt_work = 80; +static int max_interrupt_work = 2; static int rx_copybreak = 200; #define PCNET32_PORT_AUI 0x00 @@ -103,6 +101,9 @@ #define PCNET32_DMA_MASK 0xffffffff +#define PCNET32_WATCHDOG_TIMEOUT (jiffies + (2 * HZ)) +#define PCNET32_BLINK_TIMEOUT (jiffies + (HZ/4)) + /* * table to translate option values from tulip * to internal options @@ -110,7 +111,7 @@ static unsigned char options_mapping[] = { PCNET32_PORT_ASEL, /* 0 Auto-select */ PCNET32_PORT_AUI, /* 1 BNC/AUI */ - PCNET32_PORT_AUI, /* 2 AUI/BNC */ + PCNET32_PORT_AUI, /* 2 AUI/BNC */ PCNET32_PORT_ASEL, /* 3 not supported */ PCNET32_PORT_10BT | PCNET32_PORT_FD, /* 4 10baseT-FD */ PCNET32_PORT_ASEL, /* 5 not supported */ @@ -131,20 +132,22 @@ }; #define PCNET32_TEST_LEN (sizeof(pcnet32_gstrings_test) / ETH_GSTRING_LEN) +#define PCNET32_NUM_REGS 168 + #define MAX_UNITS 8 /* More are supported, limit only on options */ static int options[MAX_UNITS]; static int full_duplex[MAX_UNITS]; /* * Theory of Operation - * + * * This driver uses the same software structure as the normal lance * driver. So look for a verbose description in lance.c. The differences * to the normal lance driver is the use of the 32bit mode of PCnet32 * and PCnetPCI chips. Because these chips are 32bit chips, there is no * 16MB limitation and we don't need bounce buffers. */ - + /* * History: * v0.01: Initial version @@ -198,7 +201,7 @@ * v1.22: changed pci scanning code to make PPC people happy * fixed switching to 32bit mode in pcnet32_open() (thanks * to Michael Richard for noticing this one) - * added sub vendor/device id matching (thanks again to + * added sub vendor/device id matching (thanks again to * Michael Richard ) * added chip id for 79c973/975 (thanks to Zach Brown ) * v1.23 fixed small bug, when manual selecting MII speed/duplex @@ -216,13 +219,15 @@ * v1.26p Fix oops on rmmod+insmod; plug i/o resource leak - Paul Gortmaker * v1.27 improved CSR/PROM address detection, lots of cleanups, * new pcnet32vlb module option, HP-PARISC support, - * added module parameter descriptions, + * added module parameter descriptions, * initial ethtool support - Helge Deller * v1.27a Sun Feb 10 2002 Go Taniguchi * use alloc_etherdev and register_netdev * fix pci probe not increment cards_found * FD auto negotiate error workaround for xSeries250 * clean up and using new mii module + * v1.27b Sep 30 2002 Kent Yoder + * Added timer for cable connection state changes. * v1.28 20 Feb 2004 Don Fry * Jon Mason , Chinmay Albal * Now uses ethtool_ops, netif_msg_* and generic_mii_ioctl. @@ -230,6 +235,16 @@ * length errors, and transmit hangs. Cleans up after errors in open. * Jim Lewis added ethernet loopback test. * Thomas Munck Steenholdt non-mii ioctl corrections. + * v1.29 6 Apr 2004 Jim Lewis added physical + * identification code (blink led's) and register dump. + * Don Fry added timer for 971/972 so skbufs don't remain on tx ring + * forever. + * v1.30 18 May 2004 Don Fry removed timer and Last Transmit Interrupt + * (ltint) as they added complexity and didn't give good throughput. + * v1.30a 22 May 2004 Don Fry limit frames received during interrupt. + * v1.30b 24 May 2004 Don Fry fix bogus tx carrier errors with 79c973, + * assisted by Bruce Penrod . + * v1.30c 25 May 2004 Don Fry added netif_wake_queue after pcnet32_restart. */ @@ -270,11 +285,11 @@ struct pcnet32_rx_head { u32 base; s16 buf_length; - s16 status; + s16 status; u32 msg_length; u32 reserved; }; - + struct pcnet32_tx_head { u32 base; s16 length; @@ -290,7 +305,7 @@ u8 phys_addr[6]; u16 reserved; u32 filter[2]; - /* Receive and transmit ring base, along with extra bits. */ + /* Receive and transmit ring base, along with extra bits. */ u32 rx_ring; u32 tx_ring; }; @@ -307,7 +322,7 @@ }; /* - * The first three fields of pcnet32_private are read by the ethernet device + * The first three fields of pcnet32_private are read by the ethernet device * so we allocate the structure should be allocated by pci_alloc_consistent(). */ struct pcnet32_private { @@ -315,16 +330,18 @@ struct pcnet32_rx_head rx_ring[RX_RING_SIZE]; struct pcnet32_tx_head tx_ring[TX_RING_SIZE]; struct pcnet32_init_block init_block; - dma_addr_t dma_addr; /* DMA address of beginning of this object, - returned by pci_alloc_consistent */ - struct pci_dev *pci_dev; /* Pointer to the associated pci device structure */ + dma_addr_t dma_addr; /* DMA address of beginning of this + object, returned by + pci_alloc_consistent */ + struct pci_dev *pci_dev; /* Pointer to the associated pci device + structure */ const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff *tx_skbuff[TX_RING_SIZE]; struct sk_buff *rx_skbuff[RX_RING_SIZE]; dma_addr_t tx_dma_addr[TX_RING_SIZE]; dma_addr_t rx_dma_addr[RX_RING_SIZE]; - struct pcnet32_access a; + struct pcnet32_access a; spinlock_t lock; /* Guard lock */ unsigned int cur_rx, cur_tx; /* The next free ring entry */ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */ @@ -332,11 +349,12 @@ char tx_full; int options; int shared_irq:1, /* shared irq possible */ - ltint:1, /* enable TxDone-intr inhibitor */ dxsuflo:1, /* disable transmit stop on uflo */ mii:1; /* mii port available */ struct net_device *next; - struct mii_if_info mii_if; + struct mii_if_info mii_if; + struct timer_list watchdog_timer; + struct timer_list blink_timer; u32 msg_enable; /* debug message level */ }; @@ -351,14 +369,21 @@ static void pcnet32_interrupt(int, void *, struct pt_regs *); static int pcnet32_close(struct net_device *); static struct net_device_stats *pcnet32_get_stats(struct net_device *); +static void pcnet32_load_multicast(struct net_device *dev); static void pcnet32_set_multicast_list(struct net_device *); static int pcnet32_ioctl(struct net_device *, struct ifreq *, int); +static void pcnet32_watchdog(struct net_device *); static int mdio_read(struct net_device *dev, int phy_id, int reg_num); static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val); static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits); static void pcnet32_ethtool_test(struct net_device *dev, struct ethtool_test *eth_test, u64 *data); static int pcnet32_loopback_test(struct net_device *dev, uint64_t *data1); +static int pcnet32_phys_id(struct net_device *dev, u32 data); +static void pcnet32_led_blink_callback(struct net_device *dev); +static int pcnet32_get_regs_len(struct net_device *dev); +static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *ptr); enum pci_flags_bit { PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4, @@ -476,6 +501,14 @@ .reset = pcnet32_dwio_reset }; +#ifdef CONFIG_NET_POLL_CONTROLLER +static void pcnet32_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + pcnet32_interrupt(0, dev, NULL); + enable_irq(dev->irq); +} +#endif static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) @@ -509,14 +542,14 @@ static void pcnet32_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct pcnet32_private *lp = dev->priv; - - strcpy (info->driver, DRV_NAME); - strcpy (info->version, DRV_VERSION); - if (lp->pci_dev) - strcpy (info->bus_info, pci_name(lp->pci_dev)); - else - sprintf(info->bus_info, "VLB 0x%lx", dev->base_addr); + struct pcnet32_private *lp = dev->priv; + + strcpy (info->driver, DRV_NAME); + strcpy (info->version, DRV_VERSION); + if (lp->pci_dev) + strcpy (info->bus_info, pci_name(lp->pci_dev)); + else + sprintf(info->bus_info, "VLB 0x%lx", dev->base_addr); } static u32 pcnet32_get_link(struct net_device *dev) @@ -539,16 +572,16 @@ static u32 pcnet32_get_msglevel(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; - return lp->msg_enable; + struct pcnet32_private *lp = dev->priv; + return lp->msg_enable; } - + static void pcnet32_set_msglevel(struct net_device *dev, u32 value) { - struct pcnet32_private *lp = dev->priv; - lp->msg_enable = value; + struct pcnet32_private *lp = dev->priv; + lp->msg_enable = value; } - + static int pcnet32_nway_reset(struct net_device *dev) { struct pcnet32_private *lp = dev->priv; @@ -565,12 +598,12 @@ static void pcnet32_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = dev->priv; - ering->tx_max_pending = TX_RING_SIZE - 1; - ering->tx_pending = lp->cur_tx - lp->dirty_tx; - ering->rx_max_pending = RX_RING_SIZE - 1; - ering->rx_pending = lp->cur_rx & RX_RING_MOD_MASK; + ering->tx_max_pending = TX_RING_SIZE - 1; + ering->tx_pending = lp->cur_tx - lp->dirty_tx; + ering->rx_max_pending = RX_RING_SIZE - 1; + ering->rx_pending = lp->cur_rx & RX_RING_MOD_MASK; } static void pcnet32_get_strings(struct net_device *dev, u32 stringset, u8 *data) @@ -597,7 +630,7 @@ test->flags |= ETH_TEST_FL_FAILED; } else if (netif_msg_hw(lp)) printk(KERN_DEBUG "%s: Loopback test passed.\n", dev->name); - } else + } else if (netif_msg_hw(lp)) printk(KERN_DEBUG "%s: No tests to run (specify 'Offline' on ethtool).", dev->name); } /* end pcnet32_ethtool_test */ @@ -607,32 +640,40 @@ struct pcnet32_access *a = &lp->a; /* access to registers */ ulong ioaddr = dev->base_addr; /* card base I/O address */ struct sk_buff *skb; /* sk buff */ - int x, y, i; /* counters */ + int x, i; /* counters */ int numbuffs = 4; /* number of TX/RX buffers and descs */ u16 status = 0x8300; /* TX ring status */ + u16 teststatus; /* test of ring status */ int rc; /* return code */ int size; /* size of packets */ unsigned char *packet; /* source packet data */ static int data_len = 60; /* length of source packets */ unsigned long flags; + unsigned long ticks; *data1 = 1; /* status of test, default to fail */ rc = 1; /* default to fail */ + if (netif_running(dev)) + pcnet32_close(dev); + spin_lock_irqsave(&lp->lock, flags); - lp->a.write_csr(ioaddr, 0, 0x7904); - netif_stop_queue(dev); + /* Reset the PCNET32 */ + lp->a.reset (ioaddr); + + /* switch pcnet32 to 32bit mode */ + lp->a.write_bcr (ioaddr, 20, 2); + + lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); + lp->init_block.filter[0] = 0; + lp->init_block.filter[1] = 0; /* purge & init rings but don't actually restart */ pcnet32_restart(dev, 0x0000); lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ - x = a->read_bcr(ioaddr, 32); /* set internal loopback in BSR32 */ - x = x | 0x00000002; - a->write_bcr(ioaddr, 32, x); - /* Initialize Transmit buffers. */ size = data_len + 15; for (x=0; xtx_skbuff[x] = skb; lp->tx_ring[x].length = le16_to_cpu(-skb->len); - lp->tx_ring[x].misc = 0x00000000; + lp->tx_ring[x].misc = 0; - /* put DA and SA into the skb */ - for (i=0; i<12; i++) - *packet++ = 0xff; + /* put DA and SA into the skb */ + for (i=0; i<6; i++) + *packet++ = dev->dev_addr[i]; + for (i=0; i<6; i++) + *packet++ = dev->dev_addr[i]; /* type */ *packet++ = 0x08; *packet++ = 0x06; /* packet number */ *packet++ = x; /* fill packet with data */ - for (y=0; ytx_dma_addr[x] = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); @@ -668,20 +711,41 @@ } } - lp->a.write_csr(ioaddr, 0, 0x0002); /* Set STRT bit */ - spin_unlock_irqrestore(&lp->lock, flags); + x = a->read_bcr(ioaddr, 32); /* set internal loopback in BSR32 */ + x = x | 0x0002; + a->write_bcr(ioaddr, 32, x); - mdelay(50); /* wait a bit */ + lp->a.write_csr (ioaddr, 15, 0x0044); /* set int loopback in CSR15 */ - spin_lock_irqsave(&lp->lock, flags); - lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ + teststatus = le16_to_cpu(0x8000); + lp->a.write_csr(ioaddr, 0, 0x0002); /* Set STRT bit */ + + /* Check status of descriptors */ + for (x=0; xrx_ring[x].status & teststatus) && (ticks < 200)) { + spin_unlock_irqrestore(&lp->lock, flags); + mdelay(1); + spin_lock_irqsave(&lp->lock, flags); + rmb(); + ticks++; + } + if (ticks == 200) { + if (netif_msg_hw(lp)) + printk("%s: Desc %d failed to reset!\n",dev->name,x); + break; + } + } + lp->a.write_csr(ioaddr, 0, 0x0004); /* Set STOP bit */ + wmb(); if (netif_msg_hw(lp) && netif_msg_pktdata(lp)) { printk(KERN_DEBUG "%s: RX loopback packets:\n", dev->name); for (x=0; xname, x); - skb=lp->rx_skbuff[x]; + skb = lp->rx_skbuff[x]; for (i=0; idata+i)); } @@ -714,20 +778,153 @@ a->write_csr(ioaddr, 15, (x & ~0x0044)); /* reset bits 6 and 2 */ x = a->read_bcr(ioaddr, 32); /* reset internal loopback */ - x = x & ~0x00000002; + x = x & ~0x0002; a->write_bcr(ioaddr, 32, x); - pcnet32_restart(dev, 0x0042); /* resume normal operation */ - - netif_wake_queue(dev); - - /* Clear interrupts, and set interrupt enable. */ - lp->a.write_csr(ioaddr, 0, 0x7940); spin_unlock_irqrestore(&lp->lock, flags); + if (netif_running(dev)) { + pcnet32_open(dev); + } else { + lp->a.write_bcr (ioaddr, 20, 4); /* return to 16bit mode */ + } + return(rc); } /* end pcnet32_loopback_test */ +static void pcnet32_led_blink_callback(struct net_device *dev) +{ + struct pcnet32_private *lp = dev->priv; + struct pcnet32_access *a = &lp->a; + ulong ioaddr = dev->base_addr; + unsigned long flags; + int i; + + spin_lock_irqsave(&lp->lock, flags); + for (i=4; i<8; i++) { + a->write_bcr(ioaddr, i, a->read_bcr(ioaddr, i) ^ 0x4000); + } + spin_unlock_irqrestore(&lp->lock, flags); + + mod_timer(&lp->blink_timer, PCNET32_BLINK_TIMEOUT); +} + +static int pcnet32_phys_id(struct net_device *dev, u32 data) +{ + struct pcnet32_private *lp = dev->priv; + struct pcnet32_access *a = &lp->a; + ulong ioaddr = dev->base_addr; + unsigned long flags; + int i, regs[4]; + + if (!lp->blink_timer.function) { + init_timer(&lp->blink_timer); + lp->blink_timer.function = (void *) pcnet32_led_blink_callback; + lp->blink_timer.data = (unsigned long) dev; + } + + /* Save the current value of the bcrs */ + spin_lock_irqsave(&lp->lock, flags); + for (i=4; i<8; i++) { + regs[i-4] = a->read_bcr(ioaddr, i); + } + spin_unlock_irqrestore(&lp->lock, flags); + + mod_timer(&lp->blink_timer, jiffies); + set_current_state(TASK_INTERRUPTIBLE); + + if ((!data) || (data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))) + data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ); + + schedule_timeout(data * HZ); + del_timer_sync(&lp->blink_timer); + + /* Restore the original value of the bcrs */ + spin_lock_irqsave(&lp->lock, flags); + for (i=4; i<8; i++) { + a->write_bcr(ioaddr, i, regs[i-4]); + } + spin_unlock_irqrestore(&lp->lock, flags); + + return 0; +} + +static int pcnet32_get_regs_len(struct net_device *dev) +{ + return(PCNET32_NUM_REGS * sizeof(u16)); +} + +static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *ptr) +{ + int i, csr0; + u16 *buff = ptr; + struct pcnet32_private *lp = dev->priv; + struct pcnet32_access *a = &lp->a; + ulong ioaddr = dev->base_addr; + int ticks; + unsigned long flags; + + spin_lock_irqsave(&lp->lock, flags); + + csr0 = a->read_csr(ioaddr, 0); + if (!(csr0 & 0x0004)) { /* If not stopped */ + /* set SUSPEND (SPND) - CSR5 bit 0 */ + a->write_csr(ioaddr, 5, 0x0001); + + /* poll waiting for bit to be set */ + ticks = 0; + while (!(a->read_csr(ioaddr, 5) & 0x0001)) { + spin_unlock_irqrestore(&lp->lock, flags); + mdelay(1); + spin_lock_irqsave(&lp->lock, flags); + ticks++; + if (ticks > 200) { + if (netif_msg_hw(lp)) + printk(KERN_DEBUG "%s: Error getting into suspend!\n", + dev->name); + break; + } + } + } + + /* read address PROM */ + for (i=0; i<16; i += 2) + *buff++ = inw(ioaddr + i); + + /* read control and status registers */ + for (i=0; i<90; i++) { + *buff++ = a->read_csr(ioaddr, i); + } + + *buff++ = a->read_csr(ioaddr, 112); + *buff++ = a->read_csr(ioaddr, 114); + + /* read bus configuration registers */ + for (i=0; i<36; i++) { + *buff++ = a->read_bcr(ioaddr, i); + } + + /* read mii phy registers */ + if (lp->mii) { + for (i=0; i<32; i++) { + lp->a.write_bcr(ioaddr, 33, ((lp->mii_if.phy_id) << 5) | i); + *buff++ = lp->a.read_bcr(ioaddr, 34); + } + } + + if (!(csr0 & 0x0004)) { /* If not stopped */ + /* clear SUSPEND (SPND) - CSR5 bit 0 */ + a->write_csr(ioaddr, 5, 0x0000); + } + + i = buff - (u16 *)ptr; + for (; i < PCNET32_NUM_REGS; i++) + *buff++ = 0; + + spin_unlock_irqrestore(&lp->lock, flags); +} + static struct ethtool_ops pcnet32_ethtool_ops = { .get_settings = pcnet32_get_settings, .set_settings = pcnet32_set_settings, @@ -742,22 +939,28 @@ .get_strings = pcnet32_get_strings, .self_test_count = pcnet32_self_test_count, .self_test = pcnet32_ethtool_test, + .phys_id = pcnet32_phys_id, + .get_regs_len = pcnet32_get_regs_len, + .get_regs = pcnet32_get_regs, }; -/* only probes for non-PCI devices, the rest are handled by +/* only probes for non-PCI devices, the rest are handled by * pci_register_driver via pcnet32_probe_pci */ static void __devinit pcnet32_probe_vlbus(void) { unsigned int *port, ioaddr; - + /* search for PCnet32 VLB cards at known addresses */ for (port = pcnet32_portlist; (ioaddr = *port); port++) { - if (!check_region(ioaddr, PCNET32_TOTAL_SIZE)) { + if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_vlbus")) { /* check if there is really a pcnet chip on that ioaddr */ - if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) + if ((inb(ioaddr + 14) == 0x57) && (inb(ioaddr + 15) == 0x57)) { pcnet32_probe1(ioaddr, 0, 0, NULL); + } else { + release_region(ioaddr, PCNET32_TOTAL_SIZE); + } } } } @@ -771,28 +974,36 @@ err = pci_enable_device(pdev); if (err < 0) { - printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err); + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_ERR PFX "failed to enable device -- err=%d\n", err); return err; } pci_set_master(pdev); ioaddr = pci_resource_start (pdev, 0); if (!ioaddr) { - printk (KERN_ERR PFX "card has no PCI IO resources, aborting\n"); - return -ENODEV; + if (pcnet32_debug & NETIF_MSG_PROBE) + printk (KERN_ERR PFX "card has no PCI IO resources, aborting\n"); + return -ENODEV; } - + if (!pci_dma_supported(pdev, PCNET32_DMA_MASK)) { - printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n"); + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n"); return -ENODEV; } + if (request_region(ioaddr, PCNET32_TOTAL_SIZE, "pcnet32_probe_pci") == NULL) { + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_ERR PFX "io address range already allocated\n"); + return -EBUSY; + } return pcnet32_probe1(ioaddr, pdev->irq, 1, pdev); } -/* pcnet32_probe1 - * Called from both pcnet32_probe_vlbus and pcnet_probe_pci. +/* pcnet32_probe1 + * Called from both pcnet32_probe_vlbus and pcnet_probe_pci. * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit @@ -802,12 +1013,13 @@ struct pcnet32_private *lp; dma_addr_t lp_dma_addr; int i, media; - int fdx, mii, fset, dxsuflo, ltint; + int fdx, mii, fset, dxsuflo; int chip_version; char *chipname; struct net_device *dev; struct pcnet32_access *a = NULL; u8 promaddr[6]; + int ret = -ENODEV; /* reset the chip */ pcnet32_wio_reset(ioaddr); @@ -820,19 +1032,20 @@ if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { a = &pcnet32_dwio; } else - return -ENODEV; + goto err_release_region; } chip_version = a->read_csr(ioaddr, 88) | (a->read_csr(ioaddr,89) << 16); - if (pcnet32_debug & NETIF_MSG_PROBE) + if ((pcnet32_debug & NETIF_MSG_PROBE) && (pcnet32_debug & NETIF_MSG_HW)) printk(KERN_INFO " PCnet chip version is %#x.\n", chip_version); if ((chip_version & 0xfff) != 0x003) { - printk(KERN_INFO PFX "Unsupported chip version.\n"); - return -ENODEV; + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_INFO PFX "Unsupported chip version.\n"); + goto err_release_region; } - + /* initialize variables */ - fdx = mii = fset = dxsuflo = ltint = 0; + fdx = mii = fset = dxsuflo = 0; chip_version = (chip_version >> 12) & 0xffff; switch (chip_version) { @@ -852,7 +1065,6 @@ case 0x2623: chipname = "PCnet/FAST 79C971"; /* PCI */ fdx = 1; mii = 1; fset = 1; - ltint = 1; break; case 0x2624: chipname = "PCnet/FAST+ 79C972"; /* PCI */ @@ -865,7 +1077,7 @@ case 0x2626: chipname = "PCnet/Home 79C978"; /* PCI */ fdx = 1; - /* + /* * This is based on specs published at www.amd.com. This section * assumes that a card with a 79C978 wants to go into 1Mb HomePNA * mode. The 79C978 can also go into standard ethernet, and there @@ -874,12 +1086,6 @@ */ /* switch to home wiring mode */ media = a->read_bcr(ioaddr, 49); -#if 0 - if (pcnet32_debug > 2) - printk(KERN_DEBUG PFX "media value %#x.\n", media); - media &= ~3; - media |= 1; -#endif if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_DEBUG PFX "media reset to %#x.\n", media); a->write_bcr(ioaddr, 49, media); @@ -888,34 +1094,42 @@ chipname = "PCnet/FAST III 79C975"; /* PCI */ fdx = 1; mii = 1; break; + case 0x2628: + chipname = "PCnet/PRO 79C976"; + fdx = 1; mii = 1; + break; default: - printk(KERN_INFO PFX "PCnet version %#x, no PCnet32 chip.\n", - chip_version); - return -ENODEV; + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_INFO PFX "PCnet version %#x, no PCnet32 chip.\n", + chip_version); + goto err_release_region; } /* * On selected chips turn on the BCR18:NOUFLO bit. This stops transmit * starting until the packet is loaded. Strike one for reliability, lose - * one for latency - although on PCI this isnt a big loss. Older chips + * 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. + * Turn on BCR18:BurstRdEn and BCR18:BurstWrEn. */ - - if(fset) - { - a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0800)); + + if (fset) { + a->write_bcr(ioaddr, 18, (a->read_bcr(ioaddr, 18) | 0x0860)); a->write_csr(ioaddr, 80, (a->read_csr(ioaddr, 80) & 0x0C00) | 0x0c00); dxsuflo = 1; - ltint = 1; } - + dev = alloc_etherdev(0); if (!dev) { - printk(KERN_ERR PFX "Memory allocation failed.\n"); - return -ENOMEM; + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_ERR PFX "Memory allocation failed.\n"); + ret = -ENOMEM; + goto err_release_region; } + SET_NETDEV_DEV(dev, &pdev->dev); - printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr); + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_INFO PFX "%s at %#3lx,", chipname, ioaddr); /* In most chips, after a chip reset, the ethernet address is read from the * station address PROM at the base address and programmed into the @@ -935,60 +1149,63 @@ /* read PROM address and compare with CSR address */ for (i = 0; i < 6; i++) promaddr[i] = inb(ioaddr + i); - - if( memcmp( promaddr, dev->dev_addr, 6) - || !is_valid_ether_addr(dev->dev_addr) ) { + + if (memcmp(promaddr, dev->dev_addr, 6) + || !is_valid_ether_addr(dev->dev_addr)) { #ifndef __powerpc__ - if( is_valid_ether_addr(promaddr) ){ + if (is_valid_ether_addr(promaddr)) { #else - if( !is_valid_ether_addr(dev->dev_addr) + if (!is_valid_ether_addr(dev->dev_addr) && is_valid_ether_addr(promaddr)) { #endif - printk(" warning: CSR address invalid,\n"); - printk(KERN_INFO " using instead PROM address of"); + if (pcnet32_debug & NETIF_MSG_PROBE) { + printk(" warning: CSR address invalid,\n"); + printk(KERN_INFO " using instead PROM address of"); + } memcpy(dev->dev_addr, promaddr, 6); } } /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ - if( !is_valid_ether_addr(dev->dev_addr) ) + if (!is_valid_ether_addr(dev->dev_addr)) memset(dev->dev_addr, 0, sizeof(dev->dev_addr)); - for (i = 0; i < 6; i++) - printk(" %2.2x", dev->dev_addr[i] ); - - if (((chip_version + 1) & 0xfffe) == 0x2624) { /* Version 0x2623 or 0x2624 */ - i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ - printk("\n" KERN_INFO " tx_start_pt(0x%04x):",i); - switch(i>>10) { - case 0: printk(" 20 bytes,"); break; - case 1: printk(" 64 bytes,"); break; - case 2: printk(" 128 bytes,"); break; - case 3: printk("~220 bytes,"); break; - } - i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ - printk(" BCR18(%x):",i&0xffff); - if (i & (1<<5)) printk("BurstWrEn "); - if (i & (1<<6)) printk("BurstRdEn "); - if (i & (1<<7)) printk("DWordIO "); - if (i & (1<<11)) printk("NoUFlow "); - i = a->read_bcr(ioaddr, 25); - printk("\n" KERN_INFO " SRAMSIZE=0x%04x,",i<<8); - i = a->read_bcr(ioaddr, 26); - printk(" SRAM_BND=0x%04x,",i<<8); - i = a->read_bcr(ioaddr, 27); - if (i & (1<<14)) printk("LowLatRx"); + if (pcnet32_debug & NETIF_MSG_PROBE) { + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i]); + + /* Version 0x2623 - 0x2624 */ + if (((chip_version + 1) & 0xfffe) == 0x2624) { + i = a->read_csr(ioaddr, 80) & 0x0C00; /* Check tx_start_pt */ + printk("\n" KERN_INFO " tx_start_pt(0x%04x):",i); + switch(i>>10) { + case 0: printk(" 20 bytes,"); break; + case 1: printk(" 64 bytes,"); break; + case 2: printk(" 128 bytes,"); break; + case 3: printk("~220 bytes,"); break; + } + i = a->read_bcr(ioaddr, 18); /* Check Burst/Bus control */ + printk(" BCR18(%x):",i&0xffff); + if (i & (1<<5)) printk("BurstWrEn "); + if (i & (1<<6)) printk("BurstRdEn "); + if (i & (1<<7)) printk("DWordIO "); + if (i & (1<<11)) printk("NoUFlow "); + i = a->read_bcr(ioaddr, 25); + printk("\n" KERN_INFO " SRAMSIZE=0x%04x,",i<<8); + i = a->read_bcr(ioaddr, 26); + printk(" SRAM_BND=0x%04x,",i<<8); + i = a->read_bcr(ioaddr, 27); + if (i & (1<<14)) printk("LowLatRx"); + } } dev->base_addr = ioaddr; - if (request_region(ioaddr, PCNET32_TOTAL_SIZE, chipname) == NULL) - return -EBUSY; - /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ if ((lp = pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) { - printk(KERN_ERR PFX "Consistent memory allocation failed.\n"); - release_region(ioaddr, PCNET32_TOTAL_SIZE); - return -ENOMEM; + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_ERR PFX "Consistent memory allocation failed.\n"); + ret = -ENOMEM; + goto err_free_netdev; } memset(lp, 0, sizeof(*lp)); @@ -996,7 +1213,9 @@ lp->pci_dev = pdev; spin_lock_init(&lp->lock); - + + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); dev->priv = lp; lp->name = chipname; lp->shared_irq = shared; @@ -1004,7 +1223,6 @@ lp->mii_if.phy_id_mask = 0x1f; lp->mii_if.reg_num_mask = 0x1f; lp->dxsuflo = dxsuflo; - lp->ltint = ltint; lp->mii = mii; lp->msg_enable = pcnet32_debug; if ((cards_found >= MAX_UNITS) || (options[cards_found] > sizeof(options_mapping))) @@ -1014,47 +1232,53 @@ lp->mii_if.dev = dev; lp->mii_if.mdio_read = mdio_read; lp->mii_if.mdio_write = mdio_write; - - if (fdx && !(lp->options & PCNET32_PORT_ASEL) && + + if (fdx && !(lp->options & PCNET32_PORT_ASEL) && ((cards_found>=MAX_UNITS) || full_duplex[cards_found])) lp->options |= PCNET32_PORT_FD; - + if (!a) { - printk(KERN_ERR PFX "No access methods\n"); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); - release_region(ioaddr, PCNET32_TOTAL_SIZE); - return -ENODEV; + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_ERR PFX "No access methods\n"); + ret = -ENODEV; + goto err_free_consistent; } lp->a = *a; - + /* detect special T1/E1 WAN card by checking for MAC address */ - if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 && dev->dev_addr[2] == 0x75) + if (dev->dev_addr[0] == 0x00 && dev->dev_addr[1] == 0xe0 + && dev->dev_addr[2] == 0x75) lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ - lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); + lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); for (i = 0; i < 6; i++) lp->init_block.phys_addr[i] = dev->dev_addr[i]; lp->init_block.filter[0] = 0x00000000; lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = (u32)le32_to_cpu(lp->dma_addr + offsetof(struct pcnet32_private, rx_ring)); - lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr + offsetof(struct pcnet32_private, tx_ring)); - + lp->init_block.rx_ring = (u32)le32_to_cpu(lp->dma_addr + + offsetof(struct pcnet32_private, rx_ring)); + lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr + + offsetof(struct pcnet32_private, tx_ring)); + /* switch pcnet32 to 32bit mode */ - a->write_bcr (ioaddr, 20, 2); + a->write_bcr(ioaddr, 20, 2); + + a->write_csr(ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, + init_block)) & 0xffff); + a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, + init_block)) >> 16); - a->write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) & 0xffff); - a->write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16); - if (irq_line) { dev->irq = irq_line; } - - if (dev->irq >= 2) - printk(" assigned IRQ %d.\n", dev->irq); - else { + + if (dev->irq >= 2) { + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(" assigned IRQ %d.\n", dev->irq); + } else { unsigned long irq_mask = probe_irq_on(); - + /* * To auto-IRQ we enable the initialization-done and DMA error * interrupts. For ISA boards we get a DMA error, but VLB and PCI @@ -1063,22 +1287,26 @@ /* Trigger an initialization just for the interrupt. */ a->write_csr (ioaddr, 0, 0x41); mdelay (1); - + dev->irq = probe_irq_off (irq_mask); - if (dev->irq) - printk(", probed IRQ %d.\n", dev->irq); - else { - printk(", failed to detect IRQ line.\n"); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); - release_region(ioaddr, PCNET32_TOTAL_SIZE); - return -ENODEV; + if (!dev->irq) { + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(", failed to detect IRQ line.\n"); + ret = -ENODEV; + goto err_free_consistent; } + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(", probed IRQ %d.\n", dev->irq); } /* Set the mii phy_id so that we can query the link state */ if (lp->mii) lp->mii_if.phy_id = ((lp->a.read_bcr (ioaddr, 33)) >> 5) & 0x1f; - + + init_timer (&lp->watchdog_timer); + lp->watchdog_timer.data = (unsigned long) dev; + lp->watchdog_timer.function = (void *) &pcnet32_watchdog; + /* The PCNET32-specific entries in the device structure. */ dev->open = &pcnet32_open; dev->hard_start_xmit = &pcnet32_start_xmit; @@ -1090,6 +1318,14 @@ dev->tx_timeout = pcnet32_tx_timeout; dev->watchdog_timeo = (5*HZ); +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = pcnet32_poll_controller; +#endif + + /* Fill in the generic fields of the device structure. */ + if (register_netdev(dev)) + goto err_free_consistent; + if (pdev) { pci_set_drvdata(pdev, dev); } else { @@ -1097,11 +1333,21 @@ pcnet32_dev = dev; } - /* Fill in the generic fields of the device structure. */ - register_netdev(dev); - printk(KERN_INFO "%s: registered as %s\n",dev->name, lp->name); + if (pcnet32_debug & NETIF_MSG_PROBE) + printk(KERN_INFO "%s: registered as %s\n", dev->name, lp->name); cards_found++; + + a->write_bcr(ioaddr, 2, 0x1002); /* enable LED writes */ + return 0; + +err_free_consistent: + pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); +err_free_netdev: + free_netdev(dev); +err_release_region: + release_region(ioaddr, PCNET32_TOTAL_SIZE); + return ret; } @@ -1113,13 +1359,15 @@ u16 val; int i; int rc; + unsigned long flags; if (dev->irq == 0 || request_irq(dev->irq, &pcnet32_interrupt, - lp->shared_irq ? SA_SHIRQ : 0, lp->name, (void *)dev)) { + lp->shared_irq ? SA_SHIRQ : 0, dev->name, (void *)dev)) { return -EAGAIN; } + spin_lock_irqsave(&lp->lock, flags); /* Check for a valid station address */ if (!is_valid_ether_addr(dev->dev_addr)) { rc = -EINVAL; @@ -1138,13 +1386,13 @@ (u32) (lp->dma_addr + offsetof(struct pcnet32_private, tx_ring)), (u32) (lp->dma_addr + offsetof(struct pcnet32_private, rx_ring)), (u32) (lp->dma_addr + offsetof(struct pcnet32_private, init_block))); - + /* set/reset autoselect bit */ val = lp->a.read_bcr (ioaddr, 2) & ~2; if (lp->options & PCNET32_PORT_ASEL) val |= 2; lp->a.write_bcr (ioaddr, 2, val); - + /* handle full duplex setting */ if (lp->mii_if.full_duplex) { val = lp->a.read_bcr (ioaddr, 9) & ~3; @@ -1154,34 +1402,38 @@ val |= 2; } else if (lp->options & PCNET32_PORT_ASEL) { /* workaround of xSeries250, turn on for 79C975 only */ - i = ((lp->a.read_csr(ioaddr, 88) | (lp->a.read_csr(ioaddr,89) << 16)) >> 12) & 0xffff; - if (i == 0x2627) val |= 3; + i = ((lp->a.read_csr(ioaddr, 88) | + (lp->a.read_csr(ioaddr,89) << 16)) >> 12) & 0xffff; + if (i == 0x2627) + val |= 3; } lp->a.write_bcr (ioaddr, 9, val); } - + /* set/reset GPSI bit in test register */ val = lp->a.read_csr (ioaddr, 124) & ~0x10; if ((lp->options & PCNET32_PORT_PORTSEL) == PCNET32_PORT_GPSI) val |= 0x10; lp->a.write_csr (ioaddr, 124, val); - + if (lp->mii && !(lp->options & PCNET32_PORT_ASEL)) { - val = lp->a.read_bcr (ioaddr, 32) & ~0x38; /* disable Auto Negotiation, set 10Mpbs, HD */ + /* disable Auto Negotiation, set 10Mpbs, HD */ + val = lp->a.read_bcr (ioaddr, 32) & ~0x38; if (lp->options & PCNET32_PORT_FD) val |= 0x10; if (lp->options & PCNET32_PORT_100) val |= 0x08; lp->a.write_bcr (ioaddr, 32, val); } else { - if (lp->options & PCNET32_PORT_ASEL) { /* enable auto negotiate, setup, disable fd */ - val = lp->a.read_bcr(ioaddr, 32) & ~0x98; - val |= 0x20; - lp->a.write_bcr(ioaddr, 32, val); + if (lp->options & PCNET32_PORT_ASEL) { + /* enable auto negotiate, setup, disable fd */ + val = lp->a.read_bcr(ioaddr, 32) & ~0x98; + val |= 0x20; + lp->a.write_bcr(ioaddr, 32, val); } } -#ifdef DO_DXSUFLO +#ifdef DO_DXSUFLO if (lp->dxsuflo) { /* Disable transmit stop on underflow */ val = lp->a.read_csr (ioaddr, 3); val |= 0x40; @@ -1189,34 +1441,36 @@ } #endif - if (lp->ltint) { /* Enable TxDone-intr inhibitor */ - val = lp->a.read_csr (ioaddr, 5); - val |= (1<<14); - lp->a.write_csr (ioaddr, 5, val); - } - lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; + pcnet32_load_multicast(dev); + if (pcnet32_init_ring(dev)) { rc = -ENOMEM; goto err_free_ring; } - + /* 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); + 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); lp->a.write_csr (ioaddr, 4, 0x0915); lp->a.write_csr (ioaddr, 0, 0x0001); netif_start_queue(dev); + /* If we have mii, print the link status and start the watchdog */ + if (lp->mii) { + mii_check_media (&lp->mii_if, netif_msg_link(lp), 1); + mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT); + } + i = 0; while (i++ < 100) if (lp->a.read_csr (ioaddr, 0) & 0x0100) break; - /* + /* * We used to clear the InitDone bit, 0x0100, here but Mark Stockton * reports that doing so triggers a bug in the '974. */ @@ -1224,33 +1478,34 @@ if (netif_msg_ifup(lp)) printk(KERN_DEBUG "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n", - dev->name, i, (u32) (lp->dma_addr + offsetof(struct pcnet32_private, init_block)), - lp->a.read_csr(ioaddr, 0)); + dev->name, i, (u32) (lp->dma_addr + + offsetof(struct pcnet32_private, init_block)), + lp->a.read_csr(ioaddr, 0)); + spin_unlock_irqrestore(&lp->lock, flags); - MOD_INC_USE_COUNT; - return 0; /* Always succeed */ err_free_ring: /* free any allocated skbuffs */ for (i = 0; i < RX_RING_SIZE; i++) { - lp->rx_ring[i].status = 0; + lp->rx_ring[i].status = 0; if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, + 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; - lp->rx_dma_addr[i] = 0; + lp->rx_dma_addr[i] = 0; } /* - * Switch back to 16bit mode to avoid problems with dumb + * Switch back to 16bit mode to avoid problems with dumb * DOS packet driver after a warm reboot */ lp->a.write_bcr (ioaddr, 20, 4); err_free_irq: + spin_unlock_irqrestore(&lp->lock, flags); free_irq(dev->irq, dev); return rc; } @@ -1268,7 +1523,7 @@ * restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com */ -static void +static void pcnet32_purge_tx_ring(struct net_device *dev) { struct pcnet32_private *lp = dev->priv; @@ -1276,10 +1531,11 @@ for (i = 0; i < TX_RING_SIZE; i++) { if (lp->tx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i], lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE); - dev_kfree_skb_any(lp->tx_skbuff[i]); + pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i], + lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE); + dev_kfree_skb_any(lp->tx_skbuff[i]); lp->tx_skbuff[i] = NULL; - lp->tx_dma_addr[i] = 0; + lp->tx_dma_addr[i] = 0; } } } @@ -1297,37 +1553,41 @@ lp->dirty_rx = lp->dirty_tx = 0; for (i = 0; i < RX_RING_SIZE; i++) { - struct sk_buff *rx_skbuff = lp->rx_skbuff[i]; + struct sk_buff *rx_skbuff = lp->rx_skbuff[i]; if (rx_skbuff == NULL) { if (!(rx_skbuff = lp->rx_skbuff[i] = dev_alloc_skb (PKT_BUF_SZ))) { /* there is not much, we can do at this point */ - printk(KERN_ERR "%s: pcnet32_init_ring dev_alloc_skb failed.\n",dev->name); + if (pcnet32_debug & NETIF_MSG_DRV) + printk(KERN_ERR "%s: pcnet32_init_ring dev_alloc_skb failed.\n", + dev->name); return -1; } skb_reserve (rx_skbuff, 2); } - if (lp->rx_dma_addr[i] == 0) - lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, - rx_skbuff->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); + if (lp->rx_dma_addr[i] == 0) + 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(2-PKT_BUF_SZ); lp->rx_ring[i].status = le16_to_cpu(0x8000); } /* The Tx buffer address is filled in as needed, but we do need to clear - the upper ownership bit. */ + * the upper ownership bit. */ for (i = 0; i < TX_RING_SIZE; i++) { lp->tx_ring[i].base = 0; lp->tx_ring[i].status = 0; - lp->tx_dma_addr[i] = 0; + lp->tx_dma_addr[i] = 0; } wmb(); /* Make sure all changes are visible */ lp->init_block.tlen_rlen = le16_to_cpu(TX_RING_LEN_BITS | RX_RING_LEN_BITS); for (i = 0; i < 6; i++) lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.rx_ring = (u32)le32_to_cpu(lp->dma_addr + offsetof(struct pcnet32_private, rx_ring)); - lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr + offsetof(struct pcnet32_private, tx_ring)); + lp->init_block.rx_ring = (u32)le32_to_cpu(lp->dma_addr + + offsetof(struct pcnet32_private, rx_ring)); + lp->init_block.tx_ring = (u32)le32_to_cpu(lp->dma_addr + + offsetof(struct pcnet32_private, tx_ring)); return 0; } @@ -1337,11 +1597,11 @@ struct pcnet32_private *lp = dev->priv; unsigned long ioaddr = dev->base_addr; int i; - + pcnet32_purge_tx_ring(dev); if (pcnet32_init_ring(dev)) return; - + /* ReInit Ring */ lp->a.write_csr (ioaddr, 0, 1); i = 0; @@ -1361,31 +1621,36 @@ spin_lock_irqsave(&lp->lock, flags); /* Transmitter timeout, serious problems. */ + if (pcnet32_debug & NETIF_MSG_DRV) printk(KERN_ERR "%s: transmit timed out, status %4.4x, resetting.\n", - dev->name, lp->a.read_csr(ioaddr, 0)); - lp->a.write_csr (ioaddr, 0, 0x0004); - lp->stats.tx_errors++; - if (netif_msg_tx_err(lp)) { - int i; - printk(KERN_DEBUG " Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", - lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", - lp->cur_rx); - for (i = 0 ; i < RX_RING_SIZE; i++) - printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", - lp->rx_ring[i].base, -lp->rx_ring[i].buf_length, - lp->rx_ring[i].msg_length, (unsigned)lp->rx_ring[i].status); - for (i = 0 ; i < TX_RING_SIZE; i++) - printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", - lp->tx_ring[i].base, -lp->tx_ring[i].length, - lp->tx_ring[i].misc, (unsigned)lp->tx_ring[i].status); - printk("\n"); - } - pcnet32_restart(dev, 0x0042); + dev->name, lp->a.read_csr(ioaddr, 0)); + lp->a.write_csr (ioaddr, 0, 0x0004); + lp->stats.tx_errors++; + if (netif_msg_tx_err(lp)) { + int i; + printk(KERN_DEBUG " Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.", + lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "", + lp->cur_rx); + for (i = 0 ; i < RX_RING_SIZE; i++) + printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", + le32_to_cpu(lp->rx_ring[i].base), + (-le16_to_cpu(lp->rx_ring[i].buf_length)) & 0xffff, + le32_to_cpu(lp->rx_ring[i].msg_length), + le16_to_cpu(lp->rx_ring[i].status)); + for (i = 0 ; i < TX_RING_SIZE; i++) + printk("%s %08x %04x %08x %04x", i & 1 ? "" : "\n ", + le32_to_cpu(lp->tx_ring[i].base), + (-le16_to_cpu(lp->tx_ring[i].length)) & 0xffff, + le32_to_cpu(lp->tx_ring[i].misc), + le16_to_cpu(lp->tx_ring[i].status)); + printk("\n"); + } + pcnet32_restart(dev, 0x0042); - dev->trans_start = jiffies; - netif_wake_queue(dev); + dev->trans_start = jiffies; + netif_wake_queue(dev); - spin_unlock_irqrestore(&lp->lock, flags); + spin_unlock_irqrestore(&lp->lock, flags); } @@ -1398,45 +1663,33 @@ int entry; unsigned long flags; + spin_lock_irqsave(&lp->lock, flags); + if (netif_msg_tx_queued(lp)) { printk(KERN_DEBUG "%s: pcnet32_start_xmit() called, csr0 %4.4x.\n", dev->name, lp->a.read_csr(ioaddr, 0)); } - spin_lock_irqsave(&lp->lock, flags); - /* Default status -- will not enable Successful-TxDone * interrupt when that option is available to us. */ status = 0x8300; - entry = (lp->cur_tx - lp->dirty_tx) & TX_RING_MOD_MASK; - if ((lp->ltint) && - ((entry == TX_RING_SIZE/3) || - (entry == (TX_RING_SIZE*2)/3) || - (entry >= TX_RING_SIZE-2))) - { - /* Enable Successful-TxDone interrupt if we have - * 1/3, 2/3 or nearly all of, our ring buffer Tx'd - * but not yet cleaned up. Thus, most of the time, - * we will not enable Successful-TxDone interrupts. - */ - status = 0x9300; - } - + /* Fill in a Tx ring entry */ - + /* Mask to ring buffer boundary. */ entry = lp->cur_tx & TX_RING_MOD_MASK; - + /* Caution: the write order is important here, set the status - with the "ownership" bits last. */ + * with the "ownership" bits last. */ lp->tx_ring[entry].length = le16_to_cpu(-skb->len); lp->tx_ring[entry].misc = 0x00000000; lp->tx_skbuff[entry] = skb; - lp->tx_dma_addr[entry] = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + lp->tx_dma_addr[entry] = pci_map_single(lp->pci_dev, skb->data, skb->len, + PCI_DMA_TODEVICE); lp->tx_ring[entry].base = (u32)le32_to_cpu(lp->tx_dma_addr[entry]); wmb(); /* Make sure owner changes after all others are visible */ lp->tx_ring[entry].status = le16_to_cpu(status); @@ -1469,16 +1722,17 @@ int must_restart; if (!dev) { - printk (KERN_DEBUG "%s(): irq %d for unknown device\n", + if (pcnet32_debug & NETIF_MSG_INTR) + printk (KERN_DEBUG "%s(): irq %d for unknown device\n", __FUNCTION__, irq); return; } ioaddr = dev->base_addr; lp = dev->priv; - + spin_lock(&lp->lock); - + rap = lp->a.read_rap(ioaddr); while ((csr0 = lp->a.read_csr (ioaddr, 0)) & 0x8600 && --boguscnt >= 0) { if (csr0 == 0xffff) { @@ -1503,7 +1757,7 @@ while (dirty_tx != lp->cur_tx) { int entry = dirty_tx & TX_RING_MOD_MASK; int status = (short)le16_to_cpu(lp->tx_ring[entry].status); - + if (status < 0) break; /* It still hasn't been Txed */ @@ -1513,6 +1767,9 @@ /* There was an major error, log it. */ int err_status = le32_to_cpu(lp->tx_ring[entry].misc); lp->stats.tx_errors++; + if (netif_msg_tx_err(lp)) + printk(KERN_ERR "%s: Tx error status=%04x err_status=%08x\n", + dev->name, status, err_status); if (err_status & 0x04000000) lp->stats.tx_aborted_errors++; if (err_status & 0x08000000) lp->stats.tx_carrier_errors++; if (err_status & 0x10000000) lp->stats.tx_window_errors++; @@ -1521,8 +1778,9 @@ lp->stats.tx_fifo_errors++; /* Ackk! On FIFO errors the Tx unit is turned off! */ /* Remove this verbosity later! */ - printk(KERN_ERR "%s: Tx FIFO error! CSR0=%4.4x\n", - dev->name, csr0); + if (netif_msg_tx_err(lp)) + printk(KERN_ERR "%s: Tx FIFO error! CSR0=%4.4x\n", + dev->name, csr0); must_restart = 1; } #else @@ -1531,8 +1789,9 @@ if (! lp->dxsuflo) { /* If controller doesn't recover ... */ /* Ackk! On FIFO errors the Tx unit is turned off! */ /* Remove this verbosity later! */ - printk(KERN_ERR "%s: Tx FIFO error! CSR0=%4.4x\n", - dev->name, csr0); + if (netif_msg_tx_err(lp)) + printk(KERN_ERR "%s: Tx FIFO error! CSR0=%4.4x\n", + dev->name, csr0); must_restart = 1; } } @@ -1545,19 +1804,20 @@ /* We must free the original skb */ if (lp->tx_skbuff[entry]) { - pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], + pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[entry], lp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); dev_kfree_skb_irq(lp->tx_skbuff[entry]); lp->tx_skbuff[entry] = 0; - lp->tx_dma_addr[entry] = 0; + lp->tx_dma_addr[entry] = 0; } dirty_tx++; } delta = (lp->cur_tx - dirty_tx) & (TX_RING_MOD_MASK + TX_RING_SIZE); - if (delta >= TX_RING_SIZE) { - printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, lp->cur_tx, lp->tx_full); + if (delta > TX_RING_SIZE) { + if (netif_msg_drv(lp)) + printk(KERN_ERR "%s: out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dev->name, dirty_tx, lp->cur_tx, lp->tx_full); dirty_tx += TX_RING_SIZE; delta -= TX_RING_SIZE; } @@ -1578,18 +1838,20 @@ /* * this happens when our receive ring is full. This shouldn't * be a problem as we will see normal rx interrupts for the frames - * in the receive ring. But there are some PCI chipsets (I can reproduce - * this on SP3G with Intel saturn chipset) which have sometimes problems - * and will fill up the receive ring with error descriptors. In this - * situation we don't get a rx interrupt, but a missed frame interrupt sooner - * or later. So we try to clean up our receive ring here. + * in the receive ring. But there are some PCI chipsets (I can + * reproduce this on SP3G with Intel saturn chipset) which have + * sometimes problems and will fill up the receive ring with + * error descriptors. In this situation we don't get a rx + * interrupt, but a missed frame interrupt sooner or later. + * So we try to clean up our receive ring here. */ pcnet32_rx(dev); lp->stats.rx_errors++; /* Missed a Rx frame. */ } if (csr0 & 0x0800) { - printk(KERN_ERR "%s: Bus master arbitration failure, status %4.4x.\n", - dev->name, csr0); + if (netif_msg_drv(lp)) + printk(KERN_ERR "%s: Bus master arbitration failure, status %4.4x.\n", + dev->name, csr0); /* unlike for the lance, there is no restart needed */ } @@ -1597,16 +1859,17 @@ /* stop the chip to clear the error condition, then restart */ lp->a.write_csr (ioaddr, 0, 0x0004); pcnet32_restart(dev, 0x0002); + netif_wake_queue(dev); } } /* Clear any other interrupt, and set interrupt enable. */ lp->a.write_csr (ioaddr, 0, 0x7940); lp->a.write_rap (ioaddr,rap); - + if (netif_msg_intr(lp)) printk(KERN_DEBUG "%s: exiting interrupt, csr0=%#4.4x.\n", - dev->name, lp->a.read_csr (ioaddr, 0)); + dev->name, lp->a.read_csr (ioaddr, 0)); spin_unlock(&lp->lock); } @@ -1616,13 +1879,14 @@ { struct pcnet32_private *lp = dev->priv; int entry = lp->cur_rx & RX_RING_MOD_MASK; + int boguscnt = RX_RING_SIZE / 2; /* If we own the next entry, it's a new packet. Send it up. */ while ((short)le16_to_cpu(lp->rx_ring[entry].status) >= 0) { int status = (short)le16_to_cpu(lp->rx_ring[entry].status) >> 8; if (status != 0x03) { /* There was an error. */ - /* + /* * There is a tricky error noted by John Murphy, * to Russ Nelson: Even with full-sized * buffers it's possible for a jabber packet to use two @@ -1639,17 +1903,18 @@ /* Malloc up new buffer, compatible with net-2e. */ short pkt_len = (le32_to_cpu(lp->rx_ring[entry].msg_length) & 0xfff)-4; struct sk_buff *skb; - - if(pkt_len < 60) { - printk(KERN_ERR "%s: Runt packet!\n",dev->name); + + if (pkt_len < 60) { + if (netif_msg_rx_err(lp)) + printk(KERN_ERR "%s: Runt packet!\n", dev->name); lp->stats.rx_errors++; } else { int rx_in_place = 0; if (pkt_len > rx_copybreak) { struct sk_buff *newskb; - - if ((newskb = dev_alloc_skb (PKT_BUF_SZ))) { + + if ((newskb = dev_alloc_skb(PKT_BUF_SZ))) { skb_reserve (newskb, 2); skb = lp->rx_skbuff[entry]; pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], @@ -1657,27 +1922,31 @@ 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, - 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 skb = NULL; } else { skb = dev_alloc_skb(pkt_len+2); - } - + } + if (skb == NULL) { - int i; - printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name); + int i; + if (netif_msg_drv(lp)) + printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", + dev->name); for (i = 0; i < RX_RING_SIZE; i++) - if ((short)le16_to_cpu(lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status) < 0) + if ((short)le16_to_cpu(lp->rx_ring[(entry+i) + & RX_RING_MOD_MASK].status) < 0) break; if (i > RX_RING_SIZE -2) { lp->stats.rx_dropped++; lp->rx_ring[entry].status |= le16_to_cpu(0x8000); + wmb(); /* Make sure adapter sees owner change */ lp->cur_rx++; } break; @@ -1691,8 +1960,8 @@ PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); eth_copy_and_sum(skb, - (unsigned char *)(lp->rx_skbuff[entry]->tail), - pkt_len,0); + (unsigned char *)(lp->rx_skbuff[entry]->tail), + pkt_len,0); } lp->stats.rx_bytes += skb->len; skb->protocol=eth_type_trans(skb,dev); @@ -1709,6 +1978,7 @@ wmb(); /* Make sure owner changes after all others are visible */ lp->rx_ring[entry].status |= le16_to_cpu(0x8000); entry = (++lp->cur_rx) & RX_RING_MOD_MASK; + if (--boguscnt <= 0) break; /* don't stay in loop forever */ } return 0; @@ -1720,9 +1990,14 @@ unsigned long ioaddr = dev->base_addr; struct pcnet32_private *lp = dev->priv; int i; + unsigned long flags; + + del_timer_sync(&lp->watchdog_timer); netif_stop_queue(dev); + spin_lock_irqsave(&lp->lock, flags); + lp->stats.rx_missed_errors = lp->a.read_csr (ioaddr, 112); if (netif_msg_ifdown(lp)) @@ -1733,35 +2008,43 @@ lp->a.write_csr (ioaddr, 0, 0x0004); /* - * Switch back to 16bit mode to avoid problems with dumb + * Switch back to 16bit mode to avoid problems with dumb * DOS packet driver after a warm reboot */ lp->a.write_bcr (ioaddr, 20, 4); + spin_unlock_irqrestore(&lp->lock, flags); + free_irq(dev->irq, dev); - + + spin_lock_irqsave(&lp->lock, flags); + /* free all allocated skbuffs */ for (i = 0; i < RX_RING_SIZE; i++) { - lp->rx_ring[i].status = 0; + lp->rx_ring[i].status = 0; + wmb(); /* Make sure adapter sees owner change */ if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, + 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; - lp->rx_dma_addr[i] = 0; + lp->rx_dma_addr[i] = 0; } - + for (i = 0; i < TX_RING_SIZE; i++) { + lp->tx_ring[i].status = 0; /* CPU owns buffer */ + wmb(); /* Make sure adapter sees owner change */ if (lp->tx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i], lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE); + pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[i], + lp->tx_skbuff[i]->len, PCI_DMA_TODEVICE); dev_kfree_skb(lp->tx_skbuff[i]); - } + } lp->tx_skbuff[i] = NULL; - lp->tx_dma_addr[i] = 0; + lp->tx_dma_addr[i] = 0; } - - MOD_DEC_USE_COUNT; + + spin_unlock_irqrestore(&lp->lock, flags); return 0; } @@ -1793,9 +2076,9 @@ char *addrs; int i; u32 crc; - + /* set all multicast bits */ - if (dev->flags & IFF_ALLMULTI){ + if (dev->flags & IFF_ALLMULTI) { ib->filter[0] = 0xffffffff; ib->filter[1] = 0xffffffff; return; @@ -1805,19 +2088,18 @@ ib->filter[1] = 0; /* Add addresses */ - for (i = 0; i < dev->mc_count; i++){ + for (i = 0; i < dev->mc_count; i++) { addrs = dmi->dmi_addr; dmi = dmi->next; - + /* multicast address? */ if (!(*addrs & 1)) continue; - + crc = ether_crc_le(6, addrs); crc = crc >> 26; mcast_table [crc >> 4] = le16_to_cpu( - le16_to_cpu(mcast_table [crc >> 4]) | (1 << (crc & 0xf)) - ); + le16_to_cpu(mcast_table [crc >> 4]) | (1 << (crc & 0xf))); } return; } @@ -1829,62 +2111,58 @@ static void pcnet32_set_multicast_list(struct net_device *dev) { unsigned long ioaddr = dev->base_addr, flags; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = dev->priv; spin_lock_irqsave(&lp->lock, flags); if (dev->flags&IFF_PROMISC) { /* Log any net taps. */ - printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); + if (netif_msg_hw(lp)) + printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); lp->init_block.mode = le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) << 7); } else { lp->init_block.mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); pcnet32_load_multicast (dev); } - - lp->a.write_csr (ioaddr, 0, 0x0004); /* Temporarily stop the lance. */ + lp->a.write_csr (ioaddr, 0, 0x0004); /* Temporarily stop the lance. */ pcnet32_restart(dev, 0x0042); /* Resume normal operation */ + netif_wake_queue(dev); + spin_unlock_irqrestore(&lp->lock, flags); } +/* This routine assumes that the lp->lock is held */ static int mdio_read(struct net_device *dev, int phy_id, int reg_num) { - struct pcnet32_private *lp = dev->priv; - unsigned long ioaddr = dev->base_addr; - u16 val_out; - int phyaddr; - - if (!lp->mii) - return 0; - - phyaddr = lp->a.read_bcr(ioaddr, 33); - - lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); - val_out = lp->a.read_bcr(ioaddr, 34); - lp->a.write_bcr(ioaddr, 33, phyaddr); - - return val_out; + struct pcnet32_private *lp = dev->priv; + unsigned long ioaddr = dev->base_addr; + u16 val_out; + + if (!lp->mii) + return 0; + + lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); + val_out = lp->a.read_bcr(ioaddr, 34); + + return val_out; } +/* This routine assumes that the lp->lock is held */ static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val) { - struct pcnet32_private *lp = dev->priv; - unsigned long ioaddr = dev->base_addr; - int phyaddr; + struct pcnet32_private *lp = dev->priv; + unsigned long ioaddr = dev->base_addr; + + if (!lp->mii) + return; - if (!lp->mii) - return; - - phyaddr = lp->a.read_bcr(ioaddr, 33); - - lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); - lp->a.write_bcr(ioaddr, 34, val); - lp->a.write_bcr(ioaddr, 33, phyaddr); + lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f)); + lp->a.write_bcr(ioaddr, 34, val); } static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = dev->priv; struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data; int rc; unsigned long flags; @@ -1901,6 +2179,21 @@ return rc; } +static void pcnet32_watchdog(struct net_device *dev) +{ + struct pcnet32_private *lp = dev->priv; + unsigned long flags; + + /* Print the link status if it has changed */ + if (lp->mii) { + spin_lock_irqsave(&lp->lock, flags); + mii_check_media (&lp->mii_if, netif_msg_link(lp), 0); + spin_unlock_irqrestore(&lp->lock, flags); + } + + mod_timer (&(lp->watchdog_timer), PCNET32_WATCHDOG_TIMEOUT); +} + static void __devexit pcnet32_remove_one(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); @@ -1926,15 +2219,15 @@ MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, DRV_NAME " debug level"); MODULE_PARM(max_interrupt_work, "i"); -MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt"); +MODULE_PARM_DESC(max_interrupt_work, DRV_NAME " maximum events handled per interrupt"); MODULE_PARM(rx_copybreak, "i"); -MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames"); +MODULE_PARM_DESC(rx_copybreak, DRV_NAME " copy breakpoint for copy-only-tiny-frames"); MODULE_PARM(tx_start_pt, "i"); -MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)"); +MODULE_PARM_DESC(tx_start_pt, DRV_NAME " transmit start point (0-3)"); MODULE_PARM(pcnet32vlb, "i"); -MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)"); +MODULE_PARM_DESC(pcnet32vlb, DRV_NAME " Vesa local bus (VLB) support (0/1)"); MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i"); -MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)"); +MODULE_PARM_DESC(options, DRV_NAME " initial option setting(s) (0-15)"); MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); MODULE_PARM_DESC(full_duplex, DRV_NAME " full duplex setting(s) (1)"); @@ -1942,6 +2235,8 @@ MODULE_DESCRIPTION("Driver for PCnet32 and PCnetPCI based ethercards"); MODULE_LICENSE("GPL"); +#define PCNET32_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) + /* An additional parameter that may be passed in... */ static int debug = -1; static int tx_start_pt = -1; @@ -1951,8 +2246,7 @@ { printk(KERN_INFO "%s", version); - if (debug >= 0 && debug < (sizeof(int) - 1)) - pcnet32_debug = 1 << debug; + pcnet32_debug = netif_msg_init(debug, PCNET32_MSG_DEFAULT); if ((tx_start_pt >= 0) && (tx_start_pt <= 3)) tx_start = tx_start_pt; @@ -1965,9 +2259,9 @@ if (pcnet32vlb) pcnet32_probe_vlbus(); - if (cards_found) + if (cards_found && (pcnet32_debug & NETIF_MSG_PROBE)) printk(KERN_INFO PFX "%d cards_found.\n", cards_found); - + return (pcnet32_have_pci + cards_found) ? 0 : -ENODEV; } @@ -1975,7 +2269,6 @@ { struct net_device *next_dev; - /* No need to check MOD_IN_USE, as sys_delete_module() checks. */ while (pcnet32_dev) { struct pcnet32_private *lp = pcnet32_dev->priv; next_dev = lp->next; @@ -1985,6 +2278,7 @@ free_netdev(pcnet32_dev); pcnet32_dev = next_dev; } + if (pcnet32_have_pci) pci_unregister_driver(&pcnet32_driver); } @@ -1994,7 +2288,6 @@ /* * Local variables: - * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c pcnet32.c" * c-indent-level: 4 * tab-width: 8 * End: diff -urN linux-2.4.26/drivers/net/r8169.c linux-2.4.27/drivers/net/r8169.c --- linux-2.4.26/drivers/net/r8169.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/r8169.c 2004-08-07 16:26:05.131366377 -0700 @@ -33,6 +33,9 @@ - Copy mc_filter setup code from 8139cp (includes an optimization, and avoids set_bit use) + <2003/11/30> + + - Add new rtl8169_{suspend/resume}() support */ #include @@ -40,11 +43,15 @@ #include #include #include +#include #include #include #include +#define DMA_64BIT_MASK 0xffffffffffffffffULL +#define DMA_32BIT_MASK 0x00000000ffffffffULL + #define RTL8169_VERSION "1.2" #define MODULENAME "r8169" #define RTL8169_DRIVER_NAME MODULENAME " Gigabit Ethernet driver " RTL8169_VERSION @@ -56,9 +63,11 @@ printk( "Assertion failed! %s,%s,%s,line=%d\n", \ #expr,__FILE__,__FUNCTION__,__LINE__); \ } +#define dprintk(fmt, args...) do { printk(PFX fmt, ## args) } while (0) #else #define assert(expr) do {} while (0) -#endif +#define dprintk(fmt, args...) do {} while (0) +#endif /* RTL8169_DEBUG */ /* media options */ #define MAX_UNITS 8 @@ -89,9 +98,12 @@ #define NUM_TX_DESC 64 /* Number of Tx descriptor registers */ #define NUM_RX_DESC 64 /* Number of Rx descriptor registers */ #define RX_BUF_SIZE 1536 /* Rx Buffer size */ +#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) #define RTL_MIN_IO_SIZE 0x80 -#define TX_TIMEOUT (6*HZ) +#define RTL8169_TX_TIMEOUT (6*HZ) +#define RTL8169_PHY_TIMEOUT (HZ) /* write/read MMIO register */ #define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) @@ -101,24 +113,52 @@ #define RTL_R16(reg) readw (ioaddr + (reg)) #define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) -static struct { +enum mac_version { + RTL_GIGA_MAC_VER_B = 0x00, + /* RTL_GIGA_MAC_VER_C = 0x03, */ + RTL_GIGA_MAC_VER_D = 0x01, + RTL_GIGA_MAC_VER_E = 0x02 +}; + +enum phy_version { + RTL_GIGA_PHY_VER_C = 0x03, /* PHY Reg 0x03 bit0-3 == 0x0000 */ + RTL_GIGA_PHY_VER_D = 0x04, /* PHY Reg 0x03 bit0-3 == 0x0000 */ + RTL_GIGA_PHY_VER_E = 0x05, /* PHY Reg 0x03 bit0-3 == 0x0000 */ + RTL_GIGA_PHY_VER_F = 0x06, /* PHY Reg 0x03 bit0-3 == 0x0001 */ + RTL_GIGA_PHY_VER_G = 0x07, /* PHY Reg 0x03 bit0-3 == 0x0002 */ +}; + + +#define _R(NAME,MAC,MASK) \ + { .name = NAME, .mac_version = MAC, .RxConfigMask = MASK } + +const static struct { const char *name; -} board_info[] __devinitdata = { - { -"RealTek RTL8169 Gigabit Ethernet"},}; + u8 mac_version; + u32 RxConfigMask; /* Clears the bits supported by this chip */ +} rtl_chip_info[] = { + _R("RTL8169", RTL_GIGA_MAC_VER_B, 0xff7e1880), + _R("RTL8169s/8110s", RTL_GIGA_MAC_VER_D, 0xff7e1880), + _R("RTL8169s/8110s", RTL_GIGA_MAC_VER_E, 0xff7e1880) +}; +#undef _R -static struct pci_device_id rtl8169_pci_tbl[] __devinitdata = { +static struct pci_device_id rtl8169_pci_tbl[] = { {0x10ec, 0x8169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, {0,}, }; MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); +static int rx_copybreak = 200; + enum RTL8169_registers { MAC0 = 0, /* Ethernet hardware address. */ MAR0 = 8, /* Multicast filter. */ - TxDescStartAddr = 0x20, - TxHDescStartAddr = 0x28, + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, FLASH = 0x30, ERSR = 0x36, ChipCmd = 0x37, @@ -143,7 +183,8 @@ PHYstatus = 0x6C, RxMaxSize = 0xDA, CPlusCmd = 0xE0, - RxDescStartAddr = 0xE4, + RxDescAddrLow = 0xE4, + RxDescAddrHigh = 0xE8, EarlyTxThres = 0xEC, FuncEvent = 0xF0, FuncEventMask = 0xF4, @@ -195,7 +236,13 @@ /*TxConfigBits */ TxInterFrameGapShift = 24, - TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /* CPlusCmd p.31 */ + RxVlan = (1 << 6), + RxChkSum = (1 << 5), + PCIDAC = (1 << 4), + PCIMulRW = (1 << 3), /*rtl8169_PHYstatus */ TBI_Enable = 0x80, @@ -242,14 +289,6 @@ TBILinkOK = 0x02000000, }; -const static struct { - const char *name; - u8 version; /* depend on RTL8169 docs */ - u32 RxConfigMask; /* should clear the bits supported by this chip */ -} rtl_chip_info[] = { - { -"RTL-8169", 0x00, 0xff7e1880,},}; - enum _DescStatusBit { OWNbit = 0x80000000, EORbit = 0x40000000, @@ -257,18 +296,18 @@ LSbit = 0x10000000, }; +#define RsvdMask 0x3fffc000 + struct TxDesc { u32 status; u32 vlan_tag; - u32 buf_addr; - u32 buf_Haddr; + u64 addr; }; struct RxDesc { u32 status; u32 vlan_tag; - u32 buf_addr; - u32 buf_Haddr; + u64 addr; }; struct rtl8169_private { @@ -277,28 +316,34 @@ struct net_device_stats stats; /* statistics of net device */ spinlock_t lock; /* spin lock flag */ int chipset; - unsigned long cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ - unsigned long cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ - unsigned long dirty_tx; - unsigned char *TxDescArrays; /* Index of Tx Descriptor buffer */ - unsigned char *RxDescArrays; /* Index of Rx Descriptor buffer */ + int mac_version; + int phy_version; + u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + u32 dirty_rx; + u32 dirty_tx; struct TxDesc *TxDescArray; /* Index of 256-alignment Tx Descriptor buffer */ struct RxDesc *RxDescArray; /* Index of 256-alignment Rx Descriptor buffer */ - unsigned char *RxBufferRings; /* Index of Rx Buffer */ - unsigned char *RxBufferRing[NUM_RX_DESC]; /* Index of Rx Buffer array */ + dma_addr_t TxPhyAddr; + dma_addr_t RxPhyAddr; + struct sk_buff *Rx_skbuff[NUM_RX_DESC]; /* Rx data buffers */ struct sk_buff *Tx_skbuff[NUM_TX_DESC]; /* Index of Transmit data buffer */ + struct timer_list timer; + unsigned long phy_link_down_cnt; + u16 cp_cmd; }; MODULE_AUTHOR("Realtek"); MODULE_DESCRIPTION("RealTek RTL-8169 Gigabit Ethernet driver"); MODULE_PARM(media, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(rx_copybreak, "i"); MODULE_LICENSE("GPL"); static int rtl8169_open(struct net_device *dev); static int rtl8169_start_xmit(struct sk_buff *skb, struct net_device *dev); -static void rtl8169_interrupt(int irq, void *dev_instance, +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs); -static void rtl8169_init_ring(struct net_device *dev); +static int rtl8169_init_ring(struct net_device *dev); static void rtl8169_hw_start(struct net_device *dev); static int rtl8169_close(struct net_device *dev); static void rtl8169_set_rx_mode(struct net_device *dev); @@ -306,13 +351,16 @@ static struct net_device_stats *rtl8169_get_stats(struct net_device *netdev); static const u16 rtl8169_intr_mask = - SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | - RxErr | RxOK; + RxUnderrun | RxOverflow | RxFIFOOver | TxErr | TxOK | RxErr | RxOK; static const unsigned int rtl8169_rx_config = (RX_FIFO_THRESH << RxCfgFIFOShift) | (RX_DMA_BURST << RxCfgDMAShift); -void -mdio_write(void *ioaddr, int RegAddr, int value) +#define PHY_Cap_10_Half_Or_Less PHY_Cap_10_Half +#define PHY_Cap_10_Full_Or_Less PHY_Cap_10_Full | PHY_Cap_10_Half_Or_Less +#define PHY_Cap_100_Half_Or_Less PHY_Cap_100_Half | PHY_Cap_10_Full_Or_Less +#define PHY_Cap_100_Full_Or_Less PHY_Cap_100_Full | PHY_Cap_100_Half_Or_Less + +static void mdio_write(void *ioaddr, int RegAddr, int value) { int i; @@ -329,8 +377,7 @@ } } -int -mdio_read(void *ioaddr, int RegAddr) +static int mdio_read(void *ioaddr, int RegAddr) { int i, value = -1; @@ -342,13 +389,272 @@ if (RTL_R32(PHYAR) & 0x80000000) { value = (int) (RTL_R32(PHYAR) & 0xFFFF); break; - } else { - udelay(100); } + udelay(100); } return value; } +static void rtl8169_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct rtl8169_private *tp = dev->priv; + + strcpy(info->driver, RTL8169_DRIVER_NAME); + strcpy(info->version, RTL8169_VERSION ); + strcpy(info->bus_info, pci_name(tp->pci_dev)); +} + +static struct ethtool_ops rtl8169_ethtool_ops = { + .get_drvinfo = rtl8169_get_drvinfo, +}; + +static void rtl8169_write_gmii_reg_bit(void *ioaddr, int reg, int bitnum, + int bitval) +{ + int val; + + val = mdio_read(ioaddr, reg); + val = (bitval == 1) ? + val | (bitval << bitnum) : val & ~(0x0001 << bitnum); + mdio_write(ioaddr, reg, val & 0xffff); +} + +static void rtl8169_get_mac_version(struct rtl8169_private *tp, void *ioaddr) +{ + const struct { + u32 mask; + int mac_version; + } mac_info[] = { + { 0x1 << 26, RTL_GIGA_MAC_VER_E }, + { 0x1 << 23, RTL_GIGA_MAC_VER_D }, + { 0x00000000, RTL_GIGA_MAC_VER_B } /* Catch-all */ + }, *p = mac_info; + u32 reg; + + reg = RTL_R32(TxConfig) & 0x7c800000; + while ((reg & p->mask) != p->mask) + p++; + tp->mac_version = p->mac_version; +} + +static void rtl8169_print_mac_version(struct rtl8169_private *tp) +{ + struct { + int version; + char *msg; + } mac_print[] = { + { RTL_GIGA_MAC_VER_E, "RTL_GIGA_MAC_VER_E" }, + { RTL_GIGA_MAC_VER_D, "RTL_GIGA_MAC_VER_D" }, + { RTL_GIGA_MAC_VER_B, "RTL_GIGA_MAC_VER_B" }, + { 0, NULL } + }, *p; + + for (p = mac_print; p->msg; p++) { + if (tp->mac_version == p->version) { + dprintk("mac_version == %s (%04d)\n", p->msg, + p->version); + return; + } + } + dprintk("mac_version == Unknown\n"); +} + +static void rtl8169_get_phy_version(struct rtl8169_private *tp, void *ioaddr) +{ + const struct { + u16 mask; + u16 set; + int phy_version; + } phy_info[] = { + { 0x000f, 0x0002, RTL_GIGA_PHY_VER_G }, + { 0x000f, 0x0001, RTL_GIGA_PHY_VER_F }, + { 0x000f, 0x0000, RTL_GIGA_PHY_VER_E }, + { 0x0000, 0x0000, RTL_GIGA_PHY_VER_D } /* Catch-all */ + }, *p = phy_info; + u16 reg; + + reg = mdio_read(ioaddr, 3) & 0xffff; + while ((reg & p->mask) != p->set) + p++; + tp->phy_version = p->phy_version; +} + +static void rtl8169_print_phy_version(struct rtl8169_private *tp) +{ + struct { + int version; + char *msg; + u32 reg; + } phy_print[] = { + { RTL_GIGA_PHY_VER_G, "RTL_GIGA_PHY_VER_G", 0x0002 }, + { RTL_GIGA_PHY_VER_F, "RTL_GIGA_PHY_VER_F", 0x0001 }, + { RTL_GIGA_PHY_VER_E, "RTL_GIGA_PHY_VER_E", 0x0000 }, + { RTL_GIGA_PHY_VER_D, "RTL_GIGA_PHY_VER_D", 0x0000 }, + { 0, NULL, 0x0000 } + }, *p; + + for (p = phy_print; p->msg; p++) { + if (tp->phy_version == p->version) { + dprintk("phy_version == %s (%04x)\n", p->msg, p->reg); + return; + } + } + dprintk("phy_version == Unknown\n"); +} + +static void rtl8169_hw_phy_config(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + struct { + u16 regs[5]; /* Beware of bit-sign propagation */ + } phy_magic[5] = { { + { 0x0000, //w 4 15 12 0 + 0x00a1, //w 3 15 0 00a1 + 0x0008, //w 2 15 0 0008 + 0x1020, //w 1 15 0 1020 + 0x1000 } },{ //w 0 15 0 1000 + { 0x7000, //w 4 15 12 7 + 0xff41, //w 3 15 0 ff41 + 0xde60, //w 2 15 0 de60 + 0x0140, //w 1 15 0 0140 + 0x0077 } },{ //w 0 15 0 0077 + { 0xa000, //w 4 15 12 a + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xfa00 } },{ //w 0 15 0 fa00 + { 0xb000, //w 4 15 12 b + 0xff41, //w 3 15 0 ff41 + 0xde20, //w 2 15 0 de20 + 0x0140, //w 1 15 0 0140 + 0x00bb } },{ //w 0 15 0 00bb + { 0xf000, //w 4 15 12 f + 0xdf01, //w 3 15 0 df01 + 0xdf20, //w 2 15 0 df20 + 0xff95, //w 1 15 0 ff95 + 0xbf00 } //w 0 15 0 bf00 + } + }, *p = phy_magic; + int i; + + rtl8169_print_mac_version(tp); + rtl8169_print_phy_version(tp); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_B) + return; + if (tp->phy_version >= RTL_GIGA_PHY_VER_F) + return; + + dprintk("MAC version != 0 && PHY version == 0 or 1\n"); + dprintk("Do final_reg2.cfg\n"); + + /* Shazam ! */ + + // phy config for RTL8169s mac_version C chip + mdio_write(ioaddr, 31, 0x0001); //w 31 2 0 1 + mdio_write(ioaddr, 21, 0x1000); //w 21 15 0 1000 + mdio_write(ioaddr, 24, 0x65c7); //w 24 15 0 65c7 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 + + for (i = 0; i < ARRAY_SIZE(phy_magic); i++, p++) { + int val, pos = 4; + + val = (mdio_read(ioaddr, pos) & 0x0fff) | (p->regs[0] & 0xffff); + mdio_write(ioaddr, pos, val); + while (--pos >= 0) + mdio_write(ioaddr, pos, p->regs[4 - pos] & 0xffff); + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 1); //w 4 11 11 1 + rtl8169_write_gmii_reg_bit(ioaddr, 4, 11, 0); //w 4 11 11 0 + } + mdio_write(ioaddr, 31, 0x0000); //w 31 2 0 0 +} + +static void rtl8169_hw_phy_reset(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + int i, val; + + printk(KERN_WARNING PFX "%s: Reset RTL8169s PHY\n", dev->name); + + val = (mdio_read(ioaddr, 0) | 0x8000) & 0xffff; + mdio_write(ioaddr, 0, val); + + for (i = 50; i >= 0; i--) { + if (!(mdio_read(ioaddr, 0) & 0x8000)) + break; + udelay(100); /* Gross */ + } + + if (i < 0) { + printk(KERN_WARNING PFX "%s: no PHY Reset ack. Giving up.\n", + dev->name); + } +} + +static void rtl8169_phy_timer(unsigned long __opaque) +{ + struct net_device *dev = (struct net_device *)__opaque; + struct rtl8169_private *tp = dev->priv; + struct timer_list *timer = &tp->timer; + void *ioaddr = tp->mmio_addr; + + assert(tp->mac_version > RTL_GIGA_MAC_VER_B); + assert(tp->phy_version < RTL_GIGA_PHY_VER_G); + + if (RTL_R8(PHYstatus) & LinkStatus) + tp->phy_link_down_cnt = 0; + else { + tp->phy_link_down_cnt++; + if (tp->phy_link_down_cnt >= 12) { + int reg; + + // If link on 1000, perform phy reset. + reg = mdio_read(ioaddr, PHY_1000_CTRL_REG); + if (reg & PHY_Cap_1000_Full) + rtl8169_hw_phy_reset(dev); + + tp->phy_link_down_cnt = 0; + } + } + + mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT); +} + +static inline void rtl8169_delete_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + struct timer_list *timer = &tp->timer; + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) || + (tp->phy_version >= RTL_GIGA_PHY_VER_G)) + return; + + del_timer_sync(timer); + + tp->phy_link_down_cnt = 0; +} + +static inline void rtl8169_request_timer(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + struct timer_list *timer = &tp->timer; + + if ((tp->mac_version <= RTL_GIGA_MAC_VER_B) || + (tp->phy_version >= RTL_GIGA_PHY_VER_G)) + return; + + tp->phy_link_down_cnt = 0; + + init_timer(timer); + timer->expires = jiffies + RTL8169_PHY_TIMEOUT; + timer->data = (unsigned long)(dev); + timer->function = rtl8169_phy_timer; + add_timer(timer); +} + static int __devinit rtl8169_init_board(struct pci_dev *pdev, struct net_device **dev_out, void **ioaddr_out) @@ -356,9 +662,9 @@ void *ioaddr = NULL; struct net_device *dev; struct rtl8169_private *tp; - int rc, i; unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; - u32 tmp; + int rc, i, acpi_idle_state = 0, pm_cap; + assert(pdev != NULL); assert(ioaddr_out != NULL); @@ -374,12 +680,27 @@ } SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); tp = dev->priv; // enable device (incl. PCI PM wakeup and hotplug setup) rc = pci_enable_device(pdev); - if (rc) + if (rc) { + printk(KERN_ERR PFX "%s: unable to enable device\n", pdev->slot_name); goto err_out; + } + + /* save power state before pci_enable_device overwrites it */ + pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pm_cap) { + u16 pwr_command; + + pci_read_config_word(pdev, pm_cap + PCI_PM_CTRL, &pwr_command); + acpi_idle_state = pwr_command & PCI_PM_CTRL_STATE_MASK; + } else { + printk(KERN_ERR PFX "Cannot find PowerManagement capability, aborting.\n"); + goto err_out_free_res; + } mmio_start = pci_resource_start(pdev, 1); mmio_end = pci_resource_end(pdev, 1); @@ -401,8 +722,24 @@ } rc = pci_request_regions(pdev, dev->name); - if (rc) + if (rc) { + printk(KERN_ERR PFX "%s: Could not request regions.\n", pdev->slot_name); goto err_out_disable; + } + + tp->cp_cmd = PCIMulRW | RxChkSum; + + if ((sizeof(dma_addr_t) > 32) && + !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) + tp->cp_cmd |= PCIDAC; + else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc < 0) { + printk(KERN_ERR PFX "DMA configuration failed.\n"); + goto err_out_free_res; + } + } + // enable PCI bus-mastering pci_set_master(pdev); @@ -419,30 +756,32 @@ RTL_W8(ChipCmd, CmdReset); // Check that the chip has finished the reset. - for (i = 1000; i > 0; i--) + for (i = 1000; i > 0; i--) { if ((RTL_R8(ChipCmd) & CmdReset) == 0) break; - else - udelay(10); + udelay(10); + } - // identify chip attached to board - tmp = RTL_R32(TxConfig); - tmp = ((tmp & 0x7c000000) + ((tmp & 0x00800000) << 2)) >> 24; - - for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) - if (tmp == rtl_chip_info[i].version) { - tp->chipset = i; - goto match; - } - //if unknown chip, assume array element #0, original RTL-8169 in this case - printk(KERN_DEBUG PFX - "PCI device %s: unknown chip version, assuming RTL-8169\n", - pdev->slot_name); - printk(KERN_DEBUG PFX "PCI device %s: TxConfig = 0x%lx\n", - pdev->slot_name, (unsigned long) RTL_R32(TxConfig)); - tp->chipset = 0; + // Identify chip attached to board + rtl8169_get_mac_version(tp, ioaddr); + rtl8169_get_phy_version(tp, ioaddr); + + rtl8169_print_mac_version(tp); + rtl8169_print_phy_version(tp); + + for (i = ARRAY_SIZE(rtl_chip_info) - 1; i >= 0; i--) { + if (tp->mac_version == rtl_chip_info[i].mac_version) + break; + } + if (i < 0) { + /* Unknown chip: assume array element #0, original RTL-8169 */ + printk(KERN_DEBUG PFX + "PCI device %s: unknown chip version, assuming %s\n", + pci_name(pdev), rtl_chip_info[0].name); + i++; + } + tp->chipset = i; -match: *ioaddr_out = ioaddr; *dev_out = dev; return 0; @@ -454,7 +793,7 @@ pci_disable_device(pdev); err_out: - kfree(dev); + free_netdev(dev); return rc; } @@ -495,10 +834,11 @@ dev->open = rtl8169_open; dev->hard_start_xmit = rtl8169_start_xmit; dev->get_stats = rtl8169_get_stats; + dev->ethtool_ops = &rtl8169_ethtool_ops; dev->stop = rtl8169_close; dev->tx_timeout = rtl8169_tx_timeout; dev->set_multicast_list = rtl8169_set_rx_mode; - dev->watchdog_timeo = TX_TIMEOUT; + dev->watchdog_timeo = RTL8169_TX_TIMEOUT; dev->irq = pdev->irq; dev->base_addr = (unsigned long) ioaddr; // dev->do_ioctl = mii_ioctl; @@ -514,7 +854,7 @@ iounmap(ioaddr); pci_release_regions(pdev); pci_disable_device(pdev); - kfree(dev); + free_netdev(dev); return rc; } @@ -527,12 +867,29 @@ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " "IRQ %d\n", dev->name, - board_info[ent->driver_data].name, + rtl_chip_info[ent->driver_data].name, dev->base_addr, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5], dev->irq); + rtl8169_hw_phy_config(dev); + + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + + if (tp->mac_version < RTL_GIGA_MAC_VER_E) { + dprintk("Set PCI Latency=0x40\n"); + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x40); + } + + if (tp->mac_version == RTL_GIGA_MAC_VER_D) { + dprintk("Set MAC Reg C+CR Offset 0x82h = 0x01h\n"); + RTL_W8(0x82, 0x01); + dprintk("Set PHY Reg 0x0bh = 0x00h\n"); + mdio_write(ioaddr, 0x0b, 0x0000); //w 0x0b 15 0 0 + } + // if TBI is not endbled if (!(RTL_R8(PHYstatus) & TBI_Enable)) { int val = mdio_read(ioaddr, PHY_AUTO_NEGO_REG); @@ -545,23 +902,23 @@ Cap10_100 = 0, Cap1000 = 0; switch (option) { case _10_Half: - Cap10_100 = PHY_Cap_10_Half; + Cap10_100 = PHY_Cap_10_Half_Or_Less; Cap1000 = PHY_Cap_Null; break; case _10_Full: - Cap10_100 = PHY_Cap_10_Full; + Cap10_100 = PHY_Cap_10_Full_Or_Less; Cap1000 = PHY_Cap_Null; break; case _100_Half: - Cap10_100 = PHY_Cap_100_Half; + Cap10_100 = PHY_Cap_100_Half_Or_Less; Cap1000 = PHY_Cap_Null; break; case _100_Full: - Cap10_100 = PHY_Cap_100_Full; + Cap10_100 = PHY_Cap_100_Full_Or_Less; Cap1000 = PHY_Cap_Null; break; case _1000_Full: - Cap10_100 = PHY_Cap_Null; + Cap10_100 = PHY_Cap_100_Full_Or_Less; Cap1000 = PHY_Cap_1000_Full; break; default: @@ -575,9 +932,7 @@ // enable 10/100 Full/Half Mode, leave PHY_AUTO_NEGO_REG bit4:0 unchanged mdio_write(ioaddr, PHY_AUTO_NEGO_REG, - PHY_Cap_10_Half | PHY_Cap_10_Full | - PHY_Cap_100_Half | PHY_Cap_100_Full | (val & - 0x1F)); + PHY_Cap_100_Full_Or_Less | (val & 0x1f)); // enable 1000 Full Mode mdio_write(ioaddr, PHY_1000_CTRL_REG, @@ -641,65 +996,101 @@ iounmap(tp->mmio_addr); pci_release_regions(pdev); - // poison memory before freeing - memset(dev, 0xBC, - sizeof (struct net_device) + sizeof (struct rtl8169_private)); - pci_disable_device(pdev); - kfree(dev); + free_netdev(dev); pci_set_drvdata(pdev, NULL); } +#ifdef CONFIG_PM + +static int rtl8169_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + + if (!netif_running(dev)) + return 0; + + netif_device_detach(dev); + netif_stop_queue(dev); + spin_lock_irqsave(&tp->lock, flags); + + /* Disable interrupts, stop Rx and Tx */ + RTL_W16(IntrMask, 0); + RTL_W8(ChipCmd, 0); + + /* Update the error counts. */ + tp->stats.rx_missed_errors += RTL_R32(RxMissed); + RTL_W32(RxMissed, 0); + spin_unlock_irqrestore(&tp->lock, flags); + + return 0; +} + +static int rtl8169_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (!netif_running(dev)) + return 0; + + netif_device_attach(dev); + rtl8169_hw_start(dev); + + return 0; +} + +#endif /* CONFIG_PM */ + static int rtl8169_open(struct net_device *dev) { struct rtl8169_private *tp = dev->priv; + struct pci_dev *pdev = tp->pci_dev; int retval; - u8 diff; - u32 TxPhyAddr, RxPhyAddr; retval = request_irq(dev->irq, rtl8169_interrupt, SA_SHIRQ, dev->name, dev); - if (retval) { - return retval; - } + if (retval < 0) + goto out; - tp->TxDescArrays = - kmalloc(NUM_TX_DESC * sizeof (struct TxDesc) + 256, GFP_KERNEL); - // Tx Desscriptor needs 256 bytes alignment; - TxPhyAddr = virt_to_bus(tp->TxDescArrays); - diff = 256 - (TxPhyAddr - ((TxPhyAddr >> 8) << 8)); - TxPhyAddr += diff; - tp->TxDescArray = (struct TxDesc *) (tp->TxDescArrays + diff); - - tp->RxDescArrays = - kmalloc(NUM_RX_DESC * sizeof (struct RxDesc) + 256, GFP_KERNEL); - // Rx Desscriptor needs 256 bytes alignment; - RxPhyAddr = virt_to_bus(tp->RxDescArrays); - diff = 256 - (RxPhyAddr - ((RxPhyAddr >> 8) << 8)); - RxPhyAddr += diff; - tp->RxDescArray = (struct RxDesc *) (tp->RxDescArrays + diff); + retval = -ENOMEM; - if (tp->TxDescArrays == NULL || tp->RxDescArrays == NULL) { - printk(KERN_INFO - "Allocate RxDescArray or TxDescArray failed\n"); - free_irq(dev->irq, dev); - if (tp->TxDescArrays) - kfree(tp->TxDescArrays); - if (tp->RxDescArrays) - kfree(tp->RxDescArrays); - return -ENOMEM; - } - tp->RxBufferRings = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL); - if (tp->RxBufferRings == NULL) { - printk(KERN_INFO "Allocate RxBufferRing failed\n"); - } + /* + * Rx and Tx desscriptors needs 256 bytes alignment. + * pci_alloc_consistent provides more. + */ + tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES, + &tp->TxPhyAddr); + if (!tp->TxDescArray) + goto err_free_irq; + + tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES, + &tp->RxPhyAddr); + if (!tp->RxDescArray) + goto err_free_tx; + + retval = rtl8169_init_ring(dev); + if (retval < 0) + goto err_free_rx; - rtl8169_init_ring(dev); rtl8169_hw_start(dev); - return 0; - + rtl8169_request_timer(dev); +out: + return retval; + +err_free_rx: + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); +err_free_tx: + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); +err_free_irq: + free_irq(dev->irq, dev); + goto out; } static void @@ -736,11 +1127,21 @@ RTL_W32(TxConfig, (TX_DMA_BURST << TxDMAShift) | (InterFrameGap << TxInterFrameGapShift)); + tp->cp_cmd |= RTL_R16(CPlusCmd); + RTL_W16(CPlusCmd, tp->cp_cmd); + + if (tp->mac_version == RTL_GIGA_MAC_VER_D) { + dprintk(KERN_INFO PFX "Set MAC Reg C+CR Offset 0xE0: bit-3 and bit-14 MUST be 1\n"); + tp->cp_cmd |= (1 << 14) | PCIMulRW; + RTL_W16(CPlusCmd, tp->cp_cmd); + } tp->cur_rx = 0; - RTL_W32(TxDescStartAddr, virt_to_bus(tp->TxDescArray)); - RTL_W32(RxDescStartAddr, virt_to_bus(tp->RxDescArray)); + RTL_W32(TxDescStartAddrLow, ((u64) tp->TxPhyAddr & DMA_32BIT_MASK)); + RTL_W32(TxDescStartAddrHigh, ((u64) tp->TxPhyAddr >> 32)); + RTL_W32(RxDescAddrLow, ((u64) tp->RxPhyAddr & DMA_32BIT_MASK)); + RTL_W32(RxDescAddrHigh, ((u64) tp->RxPhyAddr >> 32)); RTL_W8(Cfg9346, Cfg9346_Lock); udelay(10); @@ -758,31 +1159,131 @@ } -static void -rtl8169_init_ring(struct net_device *dev) +static inline void rtl8169_make_unusable_by_asic(struct RxDesc *desc) +{ + desc->addr = 0x0badbadbadbadbad; + desc->status &= ~cpu_to_le32(OWNbit | RsvdMask); +} + +static void rtl8169_free_rx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, + struct RxDesc *desc) +{ + pci_unmap_single(pdev, le64_to_cpu(desc->addr), RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb(*sk_buff); + *sk_buff = NULL; + rtl8169_make_unusable_by_asic(desc); +} + +static inline void rtl8169_return_to_asic(struct RxDesc *desc) +{ + desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE); +} + +static inline void rtl8169_give_to_asic(struct RxDesc *desc, dma_addr_t mapping) +{ + desc->addr = cpu_to_le64(mapping); + desc->status |= cpu_to_le32(OWNbit + RX_BUF_SIZE); +} + +static int rtl8169_alloc_rx_skb(struct pci_dev *pdev, struct net_device *dev, + struct sk_buff **sk_buff, struct RxDesc *desc) +{ + struct sk_buff *skb; + dma_addr_t mapping; + int ret = 0; + + skb = dev_alloc_skb(RX_BUF_SIZE); + if (!skb) + goto err_out; + + skb->dev = dev; + skb_reserve(skb, 2); + *sk_buff = skb; + + mapping = pci_map_single(pdev, skb->tail, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + + rtl8169_give_to_asic(desc, mapping); + +out: + return ret; + +err_out: + ret = -ENOMEM; + rtl8169_make_unusable_by_asic(desc); + goto out; +} + +static void rtl8169_rx_clear(struct rtl8169_private *tp) { - struct rtl8169_private *tp = dev->priv; int i; - tp->cur_rx = 0; - tp->cur_tx = 0; - tp->dirty_tx = 0; + for (i = 0; i < NUM_RX_DESC; i++) { + if (tp->Rx_skbuff[i]) { + rtl8169_free_rx_skb(tp->pci_dev, tp->Rx_skbuff + i, + tp->RxDescArray + i); + } + } +} + +static u32 rtl8169_rx_fill(struct rtl8169_private *tp, struct net_device *dev, + u32 start, u32 end) +{ + u32 cur; + + for (cur = start; end - cur > 0; cur++) { + int ret, i = cur % NUM_RX_DESC; + + if (tp->Rx_skbuff[i]) + continue; + + ret = rtl8169_alloc_rx_skb(tp->pci_dev, dev, tp->Rx_skbuff + i, + tp->RxDescArray + i); + if (ret < 0) + break; + } + return cur - start; +} + +static inline void rtl8169_mark_as_last_descriptor(struct RxDesc *desc) +{ + desc->status |= cpu_to_le32(EORbit); +} + +static int rtl8169_init_ring(struct net_device *dev) +{ + struct rtl8169_private *tp = dev->priv; + + tp->cur_rx = tp->dirty_rx = 0; + tp->cur_tx = tp->dirty_tx = 0; memset(tp->TxDescArray, 0x0, NUM_TX_DESC * sizeof (struct TxDesc)); memset(tp->RxDescArray, 0x0, NUM_RX_DESC * sizeof (struct RxDesc)); - for (i = 0; i < NUM_TX_DESC; i++) { - tp->Tx_skbuff[i] = NULL; - } - for (i = 0; i < NUM_RX_DESC; i++) { - if (i == (NUM_RX_DESC - 1)) - tp->RxDescArray[i].status = - (OWNbit | EORbit) + RX_BUF_SIZE; - else - tp->RxDescArray[i].status = OWNbit + RX_BUF_SIZE; + memset(tp->Tx_skbuff, 0x0, NUM_TX_DESC * sizeof(struct sk_buff *)); + memset(tp->Rx_skbuff, 0x0, NUM_RX_DESC * sizeof(struct sk_buff *)); - tp->RxBufferRing[i] = &(tp->RxBufferRings[i * RX_BUF_SIZE]); - tp->RxDescArray[i].buf_addr = virt_to_bus(tp->RxBufferRing[i]); - } + if (rtl8169_rx_fill(tp, dev, 0, NUM_RX_DESC) != NUM_RX_DESC) + goto err_out; + + rtl8169_mark_as_last_descriptor(tp->RxDescArray + NUM_RX_DESC - 1); + + return 0; + +err_out: + rtl8169_rx_clear(tp); + return -ENOMEM; +} + +static void rtl8169_unmap_tx_skb(struct pci_dev *pdev, struct sk_buff **sk_buff, + struct TxDesc *desc) +{ + u32 len = sk_buff[0]->len; + + pci_unmap_single(pdev, le64_to_cpu(desc->addr), + len < ETH_ZLEN ? ETH_ZLEN : len, PCI_DMA_TODEVICE); + desc->addr = 0x00; + *sk_buff = NULL; } static void @@ -792,9 +1293,12 @@ tp->cur_tx = 0; for (i = 0; i < NUM_TX_DESC; i++) { - if (tp->Tx_skbuff[i] != NULL) { - dev_kfree_skb(tp->Tx_skbuff[i]); - tp->Tx_skbuff[i] = NULL; + struct sk_buff *skb = tp->Tx_skbuff[i]; + + if (skb) { + rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + i, + tp->TxDescArray + i); + dev_kfree_skb(skb); tp->stats.tx_dropped++; } } @@ -832,41 +1336,51 @@ struct rtl8169_private *tp = dev->priv; void *ioaddr = tp->mmio_addr; int entry = tp->cur_tx % NUM_TX_DESC; + u32 len = skb->len; - if (skb->len < ETH_ZLEN) { + if (unlikely(skb->len < ETH_ZLEN)) { skb = skb_padto(skb, ETH_ZLEN); - if (skb == NULL) - return 0; + if (!skb) + goto err_update_stats; + len = ETH_ZLEN; } spin_lock_irq(&tp->lock); - if ((tp->TxDescArray[entry].status & OWNbit) == 0) { + if (!(le32_to_cpu(tp->TxDescArray[entry].status) & OWNbit)) { + dma_addr_t mapping; + + mapping = pci_map_single(tp->pci_dev, skb->data, len, + PCI_DMA_TODEVICE); + tp->Tx_skbuff[entry] = skb; - tp->TxDescArray[entry].buf_addr = virt_to_bus(skb->data); - if (entry != (NUM_TX_DESC - 1)) - tp->TxDescArray[entry].status = - (OWNbit | FSbit | LSbit) | ((skb->len > ETH_ZLEN) ? - skb->len : ETH_ZLEN); - else - tp->TxDescArray[entry].status = - (OWNbit | EORbit | FSbit | LSbit) | - ((skb->len > ETH_ZLEN) ? skb->len : ETH_ZLEN); + tp->TxDescArray[entry].addr = cpu_to_le64(mapping); + tp->TxDescArray[entry].status = cpu_to_le32(OWNbit | FSbit | + LSbit | len | (EORbit * !((entry + 1) % NUM_TX_DESC))); + RTL_W8(TxPoll, 0x40); //set polling bit dev->trans_start = jiffies; tp->cur_tx++; - } + } else + goto err_drop; - spin_unlock_irq(&tp->lock); if ((tp->cur_tx - NUM_TX_DESC) == tp->dirty_tx) { netif_stop_queue(dev); } +out: + spin_unlock_irq(&tp->lock); return 0; + +err_drop: + dev_kfree_skb(skb); +err_update_stats: + tp->stats.tx_dropped++; + goto out; } static void @@ -884,18 +1398,23 @@ while (tx_left > 0) { int entry = dirty_tx % NUM_TX_DESC; + struct sk_buff *skb = tp->Tx_skbuff[entry]; + u32 status; - if ((tp->TxDescArray[entry].status & OWNbit) == 0) { - struct sk_buff *skb = tp->Tx_skbuff[entry]; + rmb(); + status = le32_to_cpu(tp->TxDescArray[entry].status); + if (status & OWNbit) + break; - tp->stats.tx_bytes += skb->len >= ETH_ZLEN ? + /* FIXME: is it really accurate for TxErr ? */ + tp->stats.tx_bytes += skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN; - tp->stats.tx_packets++; - dev_kfree_skb_irq(skb); - tp->Tx_skbuff[entry] = NULL; - dirty_tx++; - tx_left--; - } + tp->stats.tx_packets++; + rtl8169_unmap_tx_skb(tp->pci_dev, tp->Tx_skbuff + entry, + tp->TxDescArray + entry); + dev_kfree_skb_irq(skb); + dirty_tx++; + tx_left--; } if (tp->dirty_tx != dirty_tx) { @@ -905,74 +1424,108 @@ } } +static inline int rtl8169_try_rx_copy(struct sk_buff **sk_buff, int pkt_size, + struct RxDesc *desc, + struct net_device *dev) +{ + int ret = -1; + + if (pkt_size < rx_copybreak) { + struct sk_buff *skb; + + skb = dev_alloc_skb(pkt_size + 2); + if (skb) { + skb->dev = dev; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, sk_buff[0]->tail, pkt_size, 0); + *sk_buff = skb; + rtl8169_return_to_asic(desc); + ret = 0; + } + } + return ret; +} + static void rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp, void *ioaddr) { - int cur_rx; - struct sk_buff *skb; - int pkt_size = 0; + unsigned long cur_rx, rx_left; + int delta; assert(dev != NULL); assert(tp != NULL); assert(ioaddr != NULL); cur_rx = tp->cur_rx; + rx_left = NUM_RX_DESC + tp->dirty_rx - cur_rx; - while ((tp->RxDescArray[cur_rx].status & OWNbit) == 0) { + while (rx_left > 0) { + int entry = cur_rx % NUM_RX_DESC; + u32 status; - if (tp->RxDescArray[cur_rx].status & RxRES) { + rmb(); + status = le32_to_cpu(tp->RxDescArray[entry].status); + + if (status & OWNbit) + break; + + if (status & RxRES) { printk(KERN_INFO "%s: Rx ERROR!!!\n", dev->name); tp->stats.rx_errors++; - if (tp->RxDescArray[cur_rx].status & (RxRWT | RxRUNT)) + if (status & (RxRWT | RxRUNT)) tp->stats.rx_length_errors++; - if (tp->RxDescArray[cur_rx].status & RxCRC) + if (status & RxCRC) tp->stats.rx_crc_errors++; } else { - pkt_size = - (int) (tp->RxDescArray[cur_rx]. - status & 0x00001FFF) - 4; - skb = dev_alloc_skb(pkt_size + 2); - if (skb != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); // 16 byte align the IP fields. // - eth_copy_and_sum(skb, tp->RxBufferRing[cur_rx], - pkt_size, 0); - skb_put(skb, pkt_size); - skb->protocol = eth_type_trans(skb, dev); - netif_rx(skb); - - if (cur_rx == (NUM_RX_DESC - 1)) - tp->RxDescArray[cur_rx].status = - (OWNbit | EORbit) + RX_BUF_SIZE; - else - tp->RxDescArray[cur_rx].status = - OWNbit + RX_BUF_SIZE; - - tp->RxDescArray[cur_rx].buf_addr = - virt_to_bus(tp->RxBufferRing[cur_rx]); - dev->last_rx = jiffies; - tp->stats.rx_bytes += pkt_size; - tp->stats.rx_packets++; - } else { - printk(KERN_WARNING - "%s: Memory squeeze, deferring packet.\n", - dev->name); - /* We should check that some rx space is free. - If not, free one and mark stats->rx_dropped++. */ - tp->stats.rx_dropped++; + struct RxDesc *desc = tp->RxDescArray + entry; + struct sk_buff *skb = tp->Rx_skbuff[entry]; + int pkt_size = (status & 0x00001FFF) - 4; + + pci_dma_sync_single(tp->pci_dev, le64_to_cpu(desc->addr), + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + + if (rtl8169_try_rx_copy(&skb, pkt_size, desc, dev)) { + pci_unmap_single(tp->pci_dev, le64_to_cpu(desc->addr), + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + tp->Rx_skbuff[entry] = NULL; } - } - cur_rx = (cur_rx + 1) % NUM_RX_DESC; + skb_put(skb, pkt_size); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + + dev->last_rx = jiffies; + tp->stats.rx_bytes += pkt_size; + tp->stats.rx_packets++; + } + + cur_rx++; + rx_left--; } tp->cur_rx = cur_rx; + + delta = rtl8169_rx_fill(tp, dev, tp->dirty_rx, tp->cur_rx); + if (delta > 0) + tp->dirty_rx += delta; + else if (delta < 0) + printk(KERN_INFO "%s: no Rx buffer allocated\n", dev->name); + + /* + * FIXME: until there is periodic timer to try and refill the ring, + * a temporary shortage may definitely kill the Rx process. + * - disable the asic to try and avoid an overflow and kick it again + * after refill ? + * - how do others driver handle this condition (Uh oh...). + */ + if (tp->dirty_rx + NUM_RX_DESC == tp->cur_rx) + printk(KERN_EMERG "%s: Rx buffers exhausted\n", dev->name); } /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ -static void +static irqreturn_t rtl8169_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_instance; @@ -980,14 +1533,16 @@ int boguscnt = max_interrupt_work; void *ioaddr = tp->mmio_addr; int status = 0; + int handled = 0; do { status = RTL_R16(IntrStatus); - /* h/w no longer present (hotplug?) or major error, bail */ - if (status == 0xFFFF) + /* hotplug/major error/no more work/shared irq */ + if ((status == 0xFFFF) || !status) break; + handled = 1; /* if (status & RxUnderrun) link_changed = RTL_R16 (CSCR) & CSCR_LinkChangeBit; @@ -995,9 +1550,7 @@ RTL_W16(IntrStatus, (status & RxFIFOOver) ? (status | RxOverflow) : status); - if ((status & - (SYSErr | PCSTimeout | RxUnderrun | RxOverflow | RxFIFOOver - | TxErr | TxOK | RxErr | RxOK)) == 0) + if (!(status & rtl8169_intr_mask)) break; // Rx interrupt @@ -1020,17 +1573,20 @@ /* Clear all interrupt sources. */ RTL_W16(IntrStatus, 0xffff); } + return IRQ_RETVAL(handled); } static int rtl8169_close(struct net_device *dev) { struct rtl8169_private *tp = dev->priv; + struct pci_dev *pdev = tp->pci_dev; void *ioaddr = tp->mmio_addr; - int i; netif_stop_queue(dev); + rtl8169_delete_timer(dev); + spin_lock_irq(&tp->lock); /* Stop the chip's Tx and Rx DMA processes. */ @@ -1049,16 +1605,15 @@ free_irq(dev->irq, dev); rtl8169_tx_clear(tp); - kfree(tp->TxDescArrays); - kfree(tp->RxDescArrays); - tp->TxDescArrays = NULL; - tp->RxDescArrays = NULL; + + rtl8169_rx_clear(tp); + + pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, + tp->RxPhyAddr); + pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, + tp->TxPhyAddr); tp->TxDescArray = NULL; tp->RxDescArray = NULL; - kfree(tp->RxBufferRings); - for (i = 0; i < NUM_RX_DESC; i++) { - tp->RxBufferRing[i] = NULL; - } return 0; } @@ -1112,11 +1667,25 @@ spin_unlock_irqrestore(&tp->lock, flags); } -struct net_device_stats * -rtl8169_get_stats(struct net_device *dev) +/** + * rtl8169_get_stats - Get rtl8169 read/write statistics + * @dev: The Ethernet Device to get statistics for + * + * Get TX/RX statistics for rtl8169 + */ +static struct net_device_stats *rtl8169_get_stats(struct net_device *dev) { struct rtl8169_private *tp = dev->priv; + void *ioaddr = tp->mmio_addr; + unsigned long flags; + if (netif_running(dev)) { + spin_lock_irqsave(&tp->lock, flags); + tp->stats.rx_missed_errors += RTL_R32(RxMissed); + RTL_W32(RxMissed, 0); + spin_unlock_irqrestore(&tp->lock, flags); + } + return &tp->stats; } @@ -1125,8 +1694,10 @@ .id_table = rtl8169_pci_tbl, .probe = rtl8169_init_one, .remove = __devexit_p(rtl8169_remove_one), - .suspend = NULL, - .resume = NULL, +#ifdef CONFIG_PM + .suspend = rtl8169_suspend, + .resume = rtl8169_resume, +#endif }; static int __init diff -urN linux-2.4.26/drivers/net/sis900.c linux-2.4.27/drivers/net/sis900.c --- linux-2.4.26/drivers/net/sis900.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/sis900.c 2004-08-07 16:26:05.133366460 -0700 @@ -18,10 +18,11 @@ preliminary Rev. 1.0 Jan. 18, 1998 http://www.sis.com.tw/support/databook.htm + Rev 1.08.07 Nov. 2 2003 Daniele Venzano add suspend/resume support Rev 1.08.06 Sep. 24 2002 Mufasa Yang bug fix for Tx timeout & add SiS963 support - Rev 1.08.05 Jun. 6 2002 Mufasa Yang bug fix for read_eeprom & Tx descriptor over-boundary + Rev 1.08.05 Jun. 6 2002 Mufasa Yang bug fix for read_eeprom & Tx descriptor over-boundary Rev 1.08.04 Apr. 25 2002 Mufasa Yang added SiS962 support - Rev 1.08.03 Feb. 1 2002 Matt Domsch update to use library crc32 function + Rev 1.08.03 Feb. 1 2002 Matt Domsch update to use library crc32 function Rev 1.08.02 Nov. 30 2001 Hui-Fen Hsu workaround for EDB & bug fix for dhcp problem Rev 1.08.01 Aug. 25 2001 Hui-Fen Hsu update for 630ET & workaround for ICS1893 PHY Rev 1.08.00 Jun. 11 2001 Hui-Fen Hsu workaround for RTL8201 PHY and some bug fix @@ -47,9 +48,7 @@ */ #include -#include #include -#include #include #include #include @@ -74,7 +73,7 @@ #include "sis900.h" #define SIS900_MODULE_NAME "sis900" -#define SIS900_DRV_VERSION "v1.08.06 9/24/2002" +#define SIS900_DRV_VERSION "v1.08.07 11/02/2003" static char version[] __devinitdata = KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n"; @@ -171,6 +170,7 @@ unsigned int tx_full; /* The Tx queue is full. */ u8 host_bridge_rev; + u32 pci_state[16]; }; MODULE_AUTHOR("Jim Huang , Ollie Lho "); @@ -260,9 +260,13 @@ u8 reg; int i; - if ((isa_bridge = pci_find_device(0x1039, 0x0008, isa_bridge)) == NULL) { - printk("%s: Can not find ISA bridge\n", net_dev->name); - return 0; + isa_bridge = pci_find_device(PCI_VENDOR_ID_SI, 0x0008, isa_bridge); + if (!isa_bridge) { + isa_bridge = pci_find_device(PCI_VENDOR_ID_SI, 0x0018, isa_bridge); + if (!isa_bridge) { + printk("%s: Can not find ISA bridge\n", net_dev->name); + return 0; + } } pci_read_config_byte(isa_bridge, 0x48, ®); pci_write_config_byte(isa_bridge, 0x48, reg | 0x40); @@ -307,7 +311,7 @@ *( ((u16 *)net_dev->dev_addr) + i) = inw(ioaddr + rfdr); } - /* enable packet filitering */ + /* enable packet filtering */ outl(rfcrSave | RFEN, rfcr + ioaddr); return 1; @@ -402,6 +406,7 @@ if (!net_dev) return -ENOMEM; SET_MODULE_OWNER(net_dev); + SET_NETDEV_DEV(net_dev, &pci_dev->dev); /* We do a request_region() to register /proc/ioports info. */ ioaddr = pci_resource_start(pci_dev, 0); @@ -503,7 +508,7 @@ pci_set_drvdata(pci_dev, NULL); pci_release_regions(pci_dev); err_out: - kfree(net_dev); + free_netdev(net_dev); return ret; } @@ -995,7 +1000,7 @@ } } - /* enable packet filitering */ + /* enable packet filtering */ outl(rfcrSave | RFEN, rfcr + ioaddr); } @@ -1467,7 +1472,7 @@ * @net_dev: the net device to transmit with * * Set the transmit buffer descriptor, - * and write TxENA to enable transimt state machine. + * and write TxENA to enable transmit state machine. * tell upper layer if the buffer is full */ @@ -2183,16 +2188,78 @@ pci_free_consistent(pci_dev, TX_TOTAL_SIZE, sis_priv->tx_ring, sis_priv->tx_ring_dma); unregister_netdev(net_dev); - kfree(net_dev); + free_netdev(net_dev); pci_release_regions(pci_dev); pci_set_drvdata(pci_dev, NULL); } +#ifdef CONFIG_PM + +static int sis900_suspend(struct pci_dev *pci_dev, u32 state) +{ + struct net_device *net_dev = pci_get_drvdata(pci_dev); + struct sis900_private *sis_priv = net_dev->priv; + long ioaddr = net_dev->base_addr; + + if(!netif_running(net_dev)) + return 0; + + netif_stop_queue(net_dev); + netif_device_detach(net_dev); + + /* Stop the chip's Tx and Rx Status Machine */ + outl(RxDIS | TxDIS | inl(ioaddr + cr), ioaddr + cr); + + pci_set_power_state(pci_dev, 3); + pci_save_state(pci_dev, sis_priv->pci_state); + + return 0; +} + +static int sis900_resume(struct pci_dev *pci_dev) +{ + struct net_device *net_dev = pci_get_drvdata(pci_dev); + struct sis900_private *sis_priv = net_dev->priv; + long ioaddr = net_dev->base_addr; + + if(!netif_running(net_dev)) + return 0; + pci_restore_state(pci_dev, sis_priv->pci_state); + pci_set_power_state(pci_dev, 0); + + sis900_init_rxfilter(net_dev); + + sis900_init_tx_ring(net_dev); + sis900_init_rx_ring(net_dev); + + set_rx_mode(net_dev); + + netif_device_attach(net_dev); + netif_start_queue(net_dev); + + /* Workaround for EDB */ + sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED); + + /* Enable all known interrupts by setting the interrupt mask. */ + outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr); + outl(RxENA | inl(ioaddr + cr), ioaddr + cr); + outl(IE, ioaddr + ier); + + sis900_check_mode(net_dev, sis_priv->mii); + + return 0; +} +#endif /* CONFIG_PM */ + static struct pci_driver sis900_pci_driver = { .name = SIS900_MODULE_NAME, .id_table = sis900_pci_tbl, .probe = sis900_probe, .remove = __devexit_p(sis900_remove), +#ifdef CONFIG_PM + .suspend = sis900_suspend, + .resume = sis900_resume, +#endif /* CONFIG_PM */ }; static int __init sis900_init_module(void) diff -urN linux-2.4.26/drivers/net/sis900.h linux-2.4.27/drivers/net/sis900.h --- linux-2.4.26/drivers/net/sis900.h 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.27/drivers/net/sis900.h 2004-08-07 16:26:05.133366460 -0700 @@ -77,7 +77,7 @@ IE = 0x00000001 }; -/* maximum dma burst fro transmission and receive*/ +/* maximum dma burst for transmission and receive */ #define MAX_DMA_RANGE 7 /* actually 0 means MAXIMUM !! */ #define TxMXDMA_shift 20 #define RxMXDMA_shift 20 @@ -86,7 +86,7 @@ DMA_BURST_512 = 0, DMA_BURST_64 = 5 }; -/* transmit FIFO threshholds */ +/* transmit FIFO thresholds */ #define TX_FILL_THRESH 16 /* 1/4 FIFO size */ #define TxFILLT_shift 8 #define TxDRNT_shift 0 @@ -140,7 +140,7 @@ EEREQ = 0x00000400, EEDONE = 0x00000200, EEGNT = 0x00000100 }; -/* Manamgement Data I/O (mdio) frame */ +/* Management Data I/O (mdio) frame */ #define MIIread 0x6000 #define MIIwrite 0x5002 #define MIIpmdShift 7 diff -urN linux-2.4.26/drivers/net/tg3.c linux-2.4.27/drivers/net/tg3.c --- linux-2.4.26/drivers/net/tg3.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/tg3.c 2004-08-07 16:26:05.148367076 -0700 @@ -1,8 +1,9 @@ /* * tg3.c: Broadcom Tigon3 ethernet driver. * - * Copyright (C) 2001, 2002, 2003 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com) * Copyright (C) 2001, 2002, 2003 Jeff Garzik (jgarzik@pobox.com) + * Copyright (C) 2004 Sun Microsystems Inc. */ #include @@ -55,8 +56,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "2.9" -#define DRV_MODULE_RELDATE "March 8, 2004" +#define DRV_MODULE_VERSION "3.8" +#define DRV_MODULE_RELDATE "July 14, 2004" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -79,7 +80,8 @@ /* hardware minimum and maximum for a single frame's data payload */ #define TG3_MIN_MTU 60 #define TG3_MAX_MTU(tp) \ - (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 ? 9000 : 1500) + ((GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && \ + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) ? 9000 : 1500) /* These numbers seem to be hard coded in the NIC firmware somehow. * You can't change the ring sizes, but you can change where you place @@ -97,7 +99,8 @@ * replace things like '% foo' with '& (foo - 1)'. */ #define TG3_RX_RCB_RING_SIZE(tp) \ - (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 ? \ + ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || \ + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) ? \ 512 : 1024) #define TG3_TX_RING_SIZE 512 @@ -125,6 +128,9 @@ /* minimum number of free TX descriptors required to wake up TX process */ #define TG3_TX_WAKEUP_THRESH (TG3_TX_RING_SIZE / 4) +/* number of ETHTOOL_GSTATS u64's */ +#define TG3_NUM_STATS (sizeof(struct tg3_ethtool_stats)/sizeof(u64)) + static char version[] __devinitdata = DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; @@ -171,6 +177,8 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5789, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5901_2, @@ -179,6 +187,20 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751M, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, + { PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751F, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX, @@ -198,6 +220,87 @@ MODULE_DEVICE_TABLE(pci, tg3_pci_tbl); +struct { + char string[ETH_GSTRING_LEN]; +} ethtool_stats_keys[TG3_NUM_STATS] = { + { "rx_octets" }, + { "rx_fragments" }, + { "rx_ucast_packets" }, + { "rx_mcast_packets" }, + { "rx_bcast_packets" }, + { "rx_fcs_errors" }, + { "rx_align_errors" }, + { "rx_xon_pause_rcvd" }, + { "rx_xoff_pause_rcvd" }, + { "rx_mac_ctrl_rcvd" }, + { "rx_xoff_entered" }, + { "rx_frame_too_long_errors" }, + { "rx_jabbers" }, + { "rx_undersize_packets" }, + { "rx_in_length_errors" }, + { "rx_out_length_errors" }, + { "rx_64_or_less_octet_packets" }, + { "rx_65_to_127_octet_packets" }, + { "rx_128_to_255_octet_packets" }, + { "rx_256_to_511_octet_packets" }, + { "rx_512_to_1023_octet_packets" }, + { "rx_1024_to_1522_octet_packets" }, + { "rx_1523_to_2047_octet_packets" }, + { "rx_2048_to_4095_octet_packets" }, + { "rx_4096_to_8191_octet_packets" }, + { "rx_8192_to_9022_octet_packets" }, + + { "tx_octets" }, + { "tx_collisions" }, + + { "tx_xon_sent" }, + { "tx_xoff_sent" }, + { "tx_flow_control" }, + { "tx_mac_errors" }, + { "tx_single_collisions" }, + { "tx_mult_collisions" }, + { "tx_deferred" }, + { "tx_excessive_collisions" }, + { "tx_late_collisions" }, + { "tx_collide_2times" }, + { "tx_collide_3times" }, + { "tx_collide_4times" }, + { "tx_collide_5times" }, + { "tx_collide_6times" }, + { "tx_collide_7times" }, + { "tx_collide_8times" }, + { "tx_collide_9times" }, + { "tx_collide_10times" }, + { "tx_collide_11times" }, + { "tx_collide_12times" }, + { "tx_collide_13times" }, + { "tx_collide_14times" }, + { "tx_collide_15times" }, + { "tx_ucast_packets" }, + { "tx_mcast_packets" }, + { "tx_bcast_packets" }, + { "tx_carrier_sense_errors" }, + { "tx_discards" }, + { "tx_errors" }, + + { "dma_writeq_full" }, + { "dma_write_prioq_full" }, + { "rxbds_empty" }, + { "rx_discards" }, + { "rx_errors" }, + { "rx_threshold_hit" }, + + { "dma_readq_full" }, + { "dma_read_prioq_full" }, + { "tx_comp_queue_full" }, + + { "ring_set_send_prod_index" }, + { "ring_status_update" }, + { "nic_irqs" }, + { "nic_avoided_irqs" }, + { "nic_tx_threshold_hit" } +}; + static void tg3_write_indirect_reg32(struct tg3 *tp, u32 off, u32 val) { if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0) { @@ -339,6 +442,7 @@ tp->pci_clock_ctrl = clock_ctrl; if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750 && (orig_clock_ctrl & CLOCK_CTRL_44MHZ_CORE) != 0) { tw32_f(TG3PCI_CLOCK_CTRL, clock_ctrl | @@ -362,7 +466,7 @@ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); - udelay(40); + udelay(80); } *val = 0xffffffff; @@ -395,7 +499,7 @@ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(40); + udelay(80); } return ret; @@ -409,7 +513,7 @@ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, (tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL)); - udelay(40); + udelay(80); } frame_val = ((PHY_ADDR << MI_COM_PHY_ADDR_SHIFT) & @@ -438,7 +542,7 @@ if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) { tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(40); + udelay(80); } return ret; @@ -642,7 +746,14 @@ tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x8200); tg3_writephy(tp, 0x16, 0x0000); - tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { + /* Set Extended packet length bit for jumbo frames */ + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4400); + } + else { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); + } tg3_writephy(tp, MII_TG3_CTRL, phy9_orig); @@ -656,7 +767,7 @@ /* This will reset the tigon3 PHY if there is no valid * link unless the FORCE argument is non-zero. */ -static int tg3_phy_reset(struct tg3 *tp, int force) +static int tg3_phy_reset(struct tg3 *tp) { u32 phy_status; int err; @@ -666,12 +777,6 @@ if (err != 0) return -EBUSY; - /* If we have link, and not forcing a reset, then nothing - * to do. - */ - if ((phy_status & BMSR_LSTATUS) != 0 && (force == 0)) - return 0; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { @@ -698,6 +803,30 @@ tg3_writephy(tp, 0x1c, 0x8d68); tg3_writephy(tp, 0x1c, 0x8d68); } + if (tp->tg3_flags2 & TG3_FLG2_PHY_BER_BUG) { + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x000a); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x310b); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x9506); + tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x401f); + tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x14e2); + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0400); + } + /* Set Extended packet length bit (bit 14) on all chips that */ + /* support jumbo frames */ + if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { + /* Cannot do read-modify-write on 5401 */ + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { + u32 phy_reg; + + /* Set bit 14 with read-modify-write to preserve other bits */ + tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0007); + tg3_readphy(tp, MII_TG3_AUX_CTRL, &phy_reg); + tg3_writephy(tp, MII_TG3_AUX_CTRL, phy_reg | 0x4000); + } tg3_phy_set_wirespeed(tp); return 0; } @@ -783,6 +912,12 @@ static int tg3_setup_phy(struct tg3 *, int); +#define RESET_KIND_SHUTDOWN 0 +#define RESET_KIND_INIT 1 +#define RESET_KIND_SUSPEND 2 + +static void tg3_write_sig_post_reset(struct tg3 *, int); + static int tg3_set_power_state(struct tg3 *tp, int state) { u32 misc_host_ctrl; @@ -869,6 +1004,8 @@ mac_mode = MAC_MODE_PORT_MODE_TBI; } + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) + tw32(MAC_LED_CTRL, tp->led_ctrl); if (((power_caps & PCI_PM_CAP_PME_D3cold) && (tp->tg3_flags & TG3_FLAG_WOL_ENABLE))) @@ -894,7 +1031,8 @@ CLOCK_CTRL_ALTCLK | CLOCK_CTRL_PWRDOWN_PLL133); udelay(40); - } else { + } else if (!((GET_ASIC_REV(tp->pci_chip_rev_id) == 5750) && + (tp->tg3_flags & TG3_FLAG_ENABLE_ASF))) { u32 newbits1, newbits2; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || @@ -903,7 +1041,8 @@ CLOCK_CTRL_TXCLK_DISABLE | CLOCK_CTRL_ALTCLK); newbits2 = newbits1 | CLOCK_CTRL_44MHZ_CORE; - } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { newbits1 = CLOCK_CTRL_625_CORE; newbits2 = newbits1 | CLOCK_CTRL_ALTCLK; } else { @@ -917,7 +1056,8 @@ tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl | newbits2); udelay(40); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { u32 newbits3; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || @@ -940,6 +1080,8 @@ /* Finally, set the new power state. */ pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control); + tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN); + return 0; } @@ -968,6 +1110,8 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 local_adv, u32 remote_adv) { u32 new_tg3_flags = 0; + u32 old_rx_mode = tp->rx_mode; + u32 old_tx_mode = tp->tx_mode; if (local_adv & ADVERTISE_PAUSE_CAP) { if (local_adv & ADVERTISE_PAUSE_ASYM) { @@ -998,10 +1142,18 @@ else tp->rx_mode &= ~RX_MODE_FLOW_CTRL_ENABLE; + if (old_rx_mode != tp->rx_mode) { + tw32_f(MAC_RX_MODE, tp->rx_mode); + } + if (new_tg3_flags & TG3_FLAG_TX_PAUSE) tp->tx_mode |= TX_MODE_FLOW_CTRL_ENABLE; else tp->tx_mode &= ~TX_MODE_FLOW_CTRL_ENABLE; + + if (old_tx_mode != tp->tx_mode) { + tw32_f(MAC_TX_MODE, tp->tx_mode); + } } static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8 *duplex) @@ -1189,7 +1341,8 @@ int err; /* Turn off tap power management. */ - err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c20); + /* Set Extended packet length bit */ + err = tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x4c20); err |= tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x0012); err |= tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x1804); @@ -1211,6 +1364,27 @@ return err; } +static int tg3_copper_is_advertising_all(struct tg3 *tp) +{ + u32 adv_reg, all_mask; + + tg3_readphy(tp, MII_ADVERTISE, &adv_reg); + all_mask = (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + if ((adv_reg & all_mask) != all_mask) + return 0; + if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { + u32 tg3_ctrl; + + tg3_readphy(tp, MII_TG3_CTRL, &tg3_ctrl); + all_mask = (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL); + if ((tg3_ctrl & all_mask) != all_mask) + return 0; + } + return 1; +} + static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset) { int current_link_up; @@ -1230,7 +1404,7 @@ tp->mi_mode = MAC_MI_MODE_BASE; tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(40); + udelay(80); tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x02); @@ -1239,7 +1413,7 @@ */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 || - tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) && + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) && netif_carrier_ok(tp->dev)) { tg3_readphy(tp, MII_BMSR, &bmsr); tg3_readphy(tp, MII_BMSR, &bmsr); @@ -1247,7 +1421,7 @@ force_reset = 1; } if (force_reset) - tg3_phy_reset(tp, 1); + tg3_phy_reset(tp); if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { tg3_readphy(tp, MII_BMSR, &bmsr); @@ -1274,7 +1448,7 @@ if ((tp->phy_id & PHY_ID_REV_MASK) == PHY_REV_BCM5401_B0 && !(bmsr & BMSR_LSTATUS) && tp->link_config.active_speed == SPEED_1000) { - err = tg3_phy_reset(tp, 1); + err = tg3_phy_reset(tp); if (!err) err = tg3_init_5401phy_dsp(tp); if (err) @@ -1299,18 +1473,27 @@ else tg3_writephy(tp, MII_TG3_IMASK, ~0); - if (tp->led_mode == led_mode_three_link) - tg3_writephy(tp, MII_TG3_EXT_CTRL, - MII_TG3_EXT_CTRL_LNK3_LED_MODE); - else - tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { + if (tp->led_ctrl == LED_CTRL_MODE_PHY_1) + tg3_writephy(tp, MII_TG3_EXT_CTRL, + MII_TG3_EXT_CTRL_LNK3_LED_MODE); + else + tg3_writephy(tp, MII_TG3_EXT_CTRL, 0); + } current_link_up = 0; current_speed = SPEED_INVALID; current_duplex = DUPLEX_INVALID; - tg3_readphy(tp, MII_BMSR, &bmsr); - tg3_readphy(tp, MII_BMSR, &bmsr); + bmsr = 0; + for (i = 0; i < 100; i++) { + tg3_readphy(tp, MII_BMSR, &bmsr); + tg3_readphy(tp, MII_BMSR, &bmsr); + if (bmsr & BMSR_LSTATUS) + break; + udelay(40); + } if (bmsr & BMSR_LSTATUS) { u32 aux_stat, bmcr; @@ -1326,22 +1509,25 @@ tg3_aux_stat_to_speed_duplex(tp, aux_stat, ¤t_speed, ¤t_duplex); - tg3_readphy(tp, MII_BMCR, &bmcr); - tg3_readphy(tp, MII_BMCR, &bmcr); + + bmcr = 0; + for (i = 0; i < 200; i++) { + tg3_readphy(tp, MII_BMCR, &bmcr); + tg3_readphy(tp, MII_BMCR, &bmcr); + if (bmcr && bmcr != 0x7fff) + break; + udelay(10); + } + if (tp->link_config.autoneg == AUTONEG_ENABLE) { if (bmcr & BMCR_ANENABLE) { - u32 gig_ctrl; - current_link_up = 1; /* Force autoneg restart if we are exiting * low power mode. */ - tg3_readphy(tp, MII_TG3_CTRL, &gig_ctrl); - if (!(gig_ctrl & (MII_TG3_CTRL_ADV_1000_HALF | - MII_TG3_CTRL_ADV_1000_FULL))) { + if (!tg3_copper_is_advertising_all(tp)) current_link_up = 0; - } } else { current_link_up = 0; } @@ -1407,14 +1593,13 @@ tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) { - if ((tp->led_mode == led_mode_link10) || + if ((tp->led_ctrl == LED_CTRL_MODE_PHY_2) || (current_link_up == 1 && tp->link_config.active_speed == SPEED_10)) tp->mac_mode |= MAC_MODE_LINK_POLARITY; } else { if (current_link_up == 1) tp->mac_mode |= MAC_MODE_LINK_POLARITY; - tw32(MAC_LED_CTRL, LED_CTRL_PHY_MODE_1); } /* ??? Without this setting Netgear GA302T PHY does not @@ -1424,7 +1609,7 @@ tp->pci_chip_rev_id == CHIPREV_ID_5700_ALTIMA) { tp->mi_mode |= MAC_MI_MODE_AUTO_POLL; tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(40); + udelay(80); } tw32_f(MAC_MODE, tp->mac_mode); @@ -1776,6 +1961,67 @@ return ret; } +static int fiber_autoneg(struct tg3 *tp, u32 *flags) +{ + int res = 0; + + if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) { + u32 dig_status; + + dig_status = tr32(SG_DIG_STATUS); + *flags = 0; + if (dig_status & SG_DIG_PARTNER_ASYM_PAUSE) + *flags |= MR_LP_ADV_ASYM_PAUSE; + if (dig_status & SG_DIG_PARTNER_PAUSE_CAPABLE) + *flags |= MR_LP_ADV_SYM_PAUSE; + + if ((dig_status & SG_DIG_AUTONEG_COMPLETE) && + !(dig_status & (SG_DIG_AUTONEG_ERROR | + SG_DIG_PARTNER_FAULT_MASK))) + res = 1; + } else { + struct tg3_fiber_aneginfo aninfo; + int status = ANEG_FAILED; + unsigned int tick; + u32 tmp; + + tw32_f(MAC_TX_AUTO_NEG, 0); + + tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; + tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); + udelay(40); + + tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); + udelay(40); + + memset(&aninfo, 0, sizeof(aninfo)); + aninfo.flags |= MR_AN_ENABLE; + aninfo.state = ANEG_STATE_UNKNOWN; + aninfo.cur_time = 0; + tick = 0; + while (++tick < 195000) { + status = tg3_fiber_aneg_smachine(tp, &aninfo); + if (status == ANEG_DONE || status == ANEG_FAILED) + break; + + udelay(1); + } + + tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; + tw32_f(MAC_MODE, tp->mac_mode); + udelay(40); + + *flags = aninfo.flags; + + if (status == ANEG_DONE && + (aninfo.flags & (MR_AN_COMPLETE | MR_LINK_OK | + MR_LP_ADV_FULL_DUPLEX))) + res = 1; + } + + return res; +} + static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset) { u32 orig_pause_cfg; @@ -1795,6 +2041,20 @@ tw32_f(MAC_MODE, tp->mac_mode); udelay(40); + if (tp->tg3_flags2 & TG3_FLG2_HW_AUTONEG) { + /* Allow time for the hardware to auto-negotiate (195ms) */ + unsigned int tick = 0; + + while (++tick < 195000) { + if (tr32(SG_DIG_STATUS) & SG_DIG_AUTONEG_COMPLETE) + break; + udelay(1); + } + if (tick >= 195000) + printk(KERN_INFO PFX "%s: HW autoneg failed !\n", + tp->dev->name); + } + /* Reset when initting first time or we have a link. */ if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED)) { @@ -1846,53 +2106,18 @@ udelay(40); current_link_up = 0; - if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) { - if (tp->link_config.autoneg == AUTONEG_ENABLE && - !(tp->tg3_flags & TG3_FLAG_GOT_SERDES_FLOWCTL)) { - struct tg3_fiber_aneginfo aninfo; - int status = ANEG_FAILED; - unsigned int tick; - u32 tmp; - - memset(&aninfo, 0, sizeof(aninfo)); - aninfo.flags |= (MR_AN_ENABLE); - - tw32(MAC_TX_AUTO_NEG, 0); - - tmp = tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK; - tw32_f(MAC_MODE, tmp | MAC_MODE_PORT_MODE_GMII); - udelay(40); - - tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_SEND_CONFIGS); - udelay(40); - - aninfo.state = ANEG_STATE_UNKNOWN; - aninfo.cur_time = 0; - tick = 0; - while (++tick < 195000) { - status = tg3_fiber_aneg_smachine(tp, &aninfo); - if (status == ANEG_DONE || - status == ANEG_FAILED) - break; - - udelay(1); - } - - tp->mac_mode &= ~MAC_MODE_SEND_CONFIGS; - tw32_f(MAC_MODE, tp->mac_mode); - udelay(40); - - if (status == ANEG_DONE && - (aninfo.flags & - (MR_AN_COMPLETE | MR_LINK_OK | - MR_LP_ADV_FULL_DUPLEX))) { + if (tr32(MAC_STATUS) & MAC_STATUS_PCS_SYNCED) { + if (tp->link_config.autoneg == AUTONEG_ENABLE) { + u32 flags; + + if (fiber_autoneg(tp, &flags)) { u32 local_adv, remote_adv; local_adv = ADVERTISE_PAUSE_CAP; remote_adv = 0; - if (aninfo.flags & MR_LP_ADV_SYM_PAUSE) - remote_adv |= LPA_PAUSE_CAP; - if (aninfo.flags & MR_LP_ADV_ASYM_PAUSE) + if (flags & MR_LP_ADV_SYM_PAUSE) + remote_adv |= LPA_PAUSE_CAP; + if (flags & MR_LP_ADV_ASYM_PAUSE) remote_adv |= LPA_PAUSE_ASYM; tg3_setup_flow_control(tp, local_adv, remote_adv); @@ -1919,8 +2144,10 @@ } else { /* Forcing 1000FD link up. */ current_link_up = 1; + tp->tg3_flags |= TG3_FLAG_GOT_SERDES_FLOWCTL; } - } + } else + tp->tg3_flags &= ~TG3_FLAG_GOT_SERDES_FLOWCTL; tp->mac_mode &= ~MAC_MODE_LINK_POLARITY; tw32_f(MAC_MODE, tp->mac_mode); @@ -1948,9 +2175,15 @@ if (current_link_up == 1) { tp->link_config.active_speed = SPEED_1000; tp->link_config.active_duplex = DUPLEX_FULL; + tw32(MAC_LED_CTRL, (tp->led_ctrl | + LED_CTRL_LNKLED_OVERRIDE | + LED_CTRL_1000MBPS_ON)); } else { tp->link_config.active_speed = SPEED_INVALID; tp->link_config.active_duplex = DUPLEX_INVALID; + tw32(MAC_LED_CTRL, (tp->led_ctrl | + LED_CTRL_LNKLED_OVERRIDE | + LED_CTRL_TRAFFIC_OVERRIDE)); } if (current_link_up != netif_carrier_ok(tp->dev)) { @@ -2003,6 +2236,16 @@ (6 << TX_LENGTHS_IPG_SHIFT) | (32 << TX_LENGTHS_SLOT_TIME_SHIFT))); + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { + if (netif_carrier_ok(tp->dev)) { + tw32(HOSTCC_STAT_COAL_TICKS, + DEFAULT_STAT_COAL_TICKS); + } else { + tw32(HOSTCC_STAT_COAL_TICKS, 0); + } + } + return err; } @@ -2211,6 +2454,11 @@ int received; hw_idx = tp->hw_status->idx[0].rx_producer; + /* + * We need to order the read of hw_idx and the read of + * the opaque cookie. + */ + rmb(); sw_idx = rx_rcb_ptr % TG3_RX_RCB_RING_SIZE(tp); work_mask = 0; received = 0; @@ -2337,7 +2585,7 @@ static int tg3_poll(struct net_device *netdev, int *budget) { - struct tg3 *tp = netdev->priv; + struct tg3 *tp = netdev_priv(netdev); struct tg3_hw_status *sblk = tp->hw_status; unsigned long flags; int done; @@ -2419,7 +2667,7 @@ static irqreturn_t tg3_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = dev_id; - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); struct tg3_hw_status *sblk = tp->hw_status; unsigned long flags; unsigned int handled = 1; @@ -2465,6 +2713,13 @@ static int tg3_init_hw(struct tg3 *); static int tg3_halt(struct tg3 *); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tg3_poll_controller(struct net_device *dev) +{ + tg3_interrupt(dev->irq, dev, NULL); +} +#endif + static void tg3_reset_task(void *_data) { struct tg3 *tp = _data; @@ -2492,7 +2747,7 @@ static void tg3_tx_timeout(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); printk(KERN_ERR PFX "%s: transmit timed out, resetting\n", dev->name); @@ -2602,13 +2857,12 @@ u32 base = (u32) mapping & 0xffffffff; return ((base > 0xffffdcc0) && - ((u64) mapping >> 32) == 0 && (base + len + 8 < base)); } -static int tg3_start_xmit_4gbug(struct sk_buff *skb, struct net_device *dev) +static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); dma_addr_t mapping; unsigned int i; u32 len, entry, base_flags, mss; @@ -2736,8 +2990,12 @@ would_hit_hwbug = entry + 1; } - tg3_set_txd(tp, entry, mapping, len, - base_flags, (i == last)); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + tg3_set_txd(tp, entry, mapping, len, + base_flags, (i == last)|(mss << 1)); + else + tg3_set_txd(tp, entry, mapping, len, + base_flags, (i == last)); entry = NEXT_TX(entry); } @@ -2806,218 +3064,63 @@ return 0; } -static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) +static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp, + int new_mtu) { - struct tg3 *tp = dev->priv; - dma_addr_t mapping; - u32 len, entry, base_flags, mss; - unsigned long flags; + dev->mtu = new_mtu; - len = skb_headlen(skb); + if (new_mtu > ETH_DATA_LEN) + tp->tg3_flags |= TG3_FLAG_JUMBO_ENABLE; + else + tp->tg3_flags &= ~TG3_FLAG_JUMBO_ENABLE; +} - /* No BH disabling for tx_lock here. We are running in BH disabled - * context and TX reclaim runs via tp->poll inside of a software - * interrupt. Rejoice! - * - * Actually, things are not so simple. If we are to take a hw - * IRQ here, we can deadlock, consider: - * - * CPU1 CPU2 - * tg3_start_xmit - * take tp->tx_lock - * tg3_timer - * take tp->lock - * tg3_interrupt - * spin on tp->lock - * spin on tp->tx_lock - * - * So we really do need to disable interrupts when taking - * tx_lock here. - */ - spin_lock_irqsave(&tp->tx_lock, flags); +static int tg3_change_mtu(struct net_device *dev, int new_mtu) +{ + struct tg3 *tp = netdev_priv(dev); - /* This is a hard error, log it. */ - if (unlikely(TX_BUFFS_AVAIL(tp) <= (skb_shinfo(skb)->nr_frags + 1))) { - netif_stop_queue(dev); - spin_unlock_irqrestore(&tp->tx_lock, flags); - printk(KERN_ERR PFX "%s: BUG! Tx Ring full when queue awake!\n", - dev->name); - return 1; - } + if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU(tp)) + return -EINVAL; - entry = tp->tx_prod; - base_flags = 0; - if (skb->ip_summed == CHECKSUM_HW) - base_flags |= TXD_FLAG_TCPUDP_CSUM; -#if TG3_TSO_SUPPORT != 0 - mss = 0; - if (skb->len > (tp->dev->mtu + ETH_HLEN) && - (mss = skb_shinfo(skb)->tso_size) != 0) { - int tcp_opt_len, ip_tcp_len; + if (!netif_running(dev)) { + /* We'll just catch it later when the + * device is up'd. + */ + tg3_set_mtu(dev, tp, new_mtu); + return 0; + } - tcp_opt_len = ((skb->h.th->doff - 5) * 4); - ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); + tg3_netif_stop(tp); + spin_lock_irq(&tp->lock); + spin_lock(&tp->tx_lock); - base_flags |= (TXD_FLAG_CPU_PRE_DMA | - TXD_FLAG_CPU_POST_DMA); + tg3_halt(tp); - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = ntohs(mss + ip_tcp_len + tcp_opt_len); - skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); + tg3_set_mtu(dev, tp, new_mtu); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { - if (tcp_opt_len || skb->nh.iph->ihl > 5) { - int tsflags; + tg3_init_hw(tp); - tsflags = ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)); - mss |= (tsflags << 11); - } - } else { - if (tcp_opt_len || skb->nh.iph->ihl > 5) { - int tsflags; + spin_unlock(&tp->tx_lock); + spin_unlock_irq(&tp->lock); + tg3_netif_start(tp); - tsflags = ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)); - base_flags |= tsflags << 12; - } - } - } -#else - mss = 0; -#endif -#if TG3_VLAN_TAG_USED - if (tp->vlgrp != NULL && vlan_tx_tag_present(skb)) - base_flags |= (TXD_FLAG_VLAN | - (vlan_tx_tag_get(skb) << 16)); -#endif + return 0; +} - /* Queue skb data, a.k.a. the main skb fragment. */ - mapping = pci_map_single(tp->pdev, skb->data, len, PCI_DMA_TODEVICE); +/* Free up pending packets in all rx/tx rings. + * + * The chip has been shut down and the driver detached from + * the networking, so no interrupts or new tx packets will + * end up in the driver. tp->{tx,}lock is not held and we are not + * in an interrupt context and thus may sleep. + */ +static void tg3_free_rings(struct tg3 *tp) +{ + struct ring_info *rxp; + int i; - tp->tx_buffers[entry].skb = skb; - pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping); - - tg3_set_txd(tp, entry, mapping, len, base_flags, - (skb_shinfo(skb)->nr_frags == 0) | (mss << 1)); - - entry = NEXT_TX(entry); - - /* Now loop through additional data fragments, and queue them. */ - if (skb_shinfo(skb)->nr_frags > 0) { - unsigned int i, last; - - last = skb_shinfo(skb)->nr_frags - 1; - for (i = 0; i <= last; i++) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - - - len = frag->size; - mapping = pci_map_page(tp->pdev, - frag->page, - frag->page_offset, - len, PCI_DMA_TODEVICE); - - tp->tx_buffers[entry].skb = NULL; - pci_unmap_addr_set(&tp->tx_buffers[entry], mapping, mapping); - - tg3_set_txd(tp, entry, mapping, len, - base_flags, (i == last)); - - entry = NEXT_TX(entry); - } - } - - /* Packets are ready, update Tx producer idx local and on card. - * We know this is not a 5700 (by virtue of not being a chip - * requiring the 4GB overflow workaround) so we can safely omit - * the double-write bug tests. - */ - if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) { - tw32_tx_mbox((MAILBOX_SNDHOST_PROD_IDX_0 + - TG3_64BIT_REG_LOW), entry); - } else { - /* First, make sure tg3 sees last descriptor fully - * in SRAM. - */ - if (tp->tg3_flags & TG3_FLAG_MBOX_WRITE_REORDER) - tr32(MAILBOX_SNDNIC_PROD_IDX_0 + - TG3_64BIT_REG_LOW); - - tw32_tx_mbox((MAILBOX_SNDNIC_PROD_IDX_0 + - TG3_64BIT_REG_LOW), entry); - } - - tp->tx_prod = entry; - if (TX_BUFFS_AVAIL(tp) <= (MAX_SKB_FRAGS + 1)) - netif_stop_queue(dev); - - spin_unlock_irqrestore(&tp->tx_lock, flags); - - dev->trans_start = jiffies; - - return 0; -} - -static inline void tg3_set_mtu(struct net_device *dev, struct tg3 *tp, - int new_mtu) -{ - dev->mtu = new_mtu; - - if (new_mtu > ETH_DATA_LEN) - tp->tg3_flags |= TG3_FLAG_JUMBO_ENABLE; - else - tp->tg3_flags &= ~TG3_FLAG_JUMBO_ENABLE; -} - -static int tg3_change_mtu(struct net_device *dev, int new_mtu) -{ - struct tg3 *tp = dev->priv; - - if (new_mtu < TG3_MIN_MTU || new_mtu > TG3_MAX_MTU(tp)) - return -EINVAL; - - if (!netif_running(dev)) { - /* We'll just catch it later when the - * device is up'd. - */ - tg3_set_mtu(dev, tp, new_mtu); - return 0; - } - - tg3_netif_stop(tp); - spin_lock_irq(&tp->lock); - spin_lock(&tp->tx_lock); - - tg3_halt(tp); - - tg3_set_mtu(dev, tp, new_mtu); - - tg3_init_hw(tp); - - spin_unlock(&tp->tx_lock); - spin_unlock_irq(&tp->lock); - tg3_netif_start(tp); - - return 0; -} - -/* Free up pending packets in all rx/tx rings. - * - * The chip has been shut down and the driver detached from - * the networking, so no interrupts or new tx packets will - * end up in the driver. tp->{tx,}lock is not held and we are not - * in an interrupt context and thus may sleep. - */ -static void tg3_free_rings(struct tg3 *tp) -{ - struct ring_info *rxp; - int i; - - for (i = 0; i < TG3_RX_RING_SIZE; i++) { - rxp = &tp->rx_std_buffers[i]; + for (i = 0; i < TG3_RX_RING_SIZE; i++) { + rxp = &tp->rx_std_buffers[i]; if (rxp->skb == NULL) continue; @@ -3282,7 +3385,8 @@ unsigned int i; u32 val; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { switch (ofs) { case RCVLSC_MODE: case DMAC_MODE: @@ -3290,7 +3394,7 @@ case BUFMGR_MODE: case MEMARB_MODE: /* We can't enable/disable these bits of the - * 5705, just say success. + * 5705/5750, just say success. */ return 0; @@ -3389,27 +3493,115 @@ } /* tp->lock is held. */ -static void tg3_chip_reset(struct tg3 *tp) +static int tg3_nvram_lock(struct tg3 *tp) { - u32 val; - u32 flags_save; - - if (!(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) { - /* Force NVRAM to settle. - * This deals with a chip bug which can result in EEPROM - * corruption. - */ - if (tp->tg3_flags & TG3_FLAG_NVRAM) { - int i; + if (tp->tg3_flags & TG3_FLAG_NVRAM) { + int i; - tw32(NVRAM_SWARB, SWARB_REQ_SET1); - for (i = 0; i < 100000; i++) { - if (tr32(NVRAM_SWARB) & SWARB_GNT1) - break; - udelay(10); - } + tw32(NVRAM_SWARB, SWARB_REQ_SET1); + for (i = 0; i < 8000; i++) { + if (tr32(NVRAM_SWARB) & SWARB_GNT1) + break; + udelay(20); } + if (i == 8000) + return -ENODEV; + } + return 0; +} + +/* tp->lock is held. */ +static void tg3_nvram_unlock(struct tg3 *tp) +{ + if (tp->tg3_flags & TG3_FLAG_NVRAM) + tw32_f(NVRAM_SWARB, SWARB_REQ_CLR1); +} + +/* tp->lock is held. */ +static void tg3_write_sig_pre_reset(struct tg3 *tp, int kind) +{ + tg3_write_mem(tp, NIC_SRAM_FIRMWARE_MBOX, + NIC_SRAM_FIRMWARE_MBOX_MAGIC1); + + if (tp->tg3_flags2 & TG3_FLG2_ASF_NEW_HANDSHAKE) { + switch (kind) { + case RESET_KIND_INIT: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_START); + break; + + case RESET_KIND_SHUTDOWN: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_UNLOAD); + break; + + case RESET_KIND_SUSPEND: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_SUSPEND); + break; + + default: + break; + }; } +} + +/* tp->lock is held. */ +static void tg3_write_sig_post_reset(struct tg3 *tp, int kind) +{ + if (tp->tg3_flags2 & TG3_FLG2_ASF_NEW_HANDSHAKE) { + switch (kind) { + case RESET_KIND_INIT: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_START_DONE); + break; + + case RESET_KIND_SHUTDOWN: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_UNLOAD_DONE); + break; + + default: + break; + }; + } +} + +/* tp->lock is held. */ +static void tg3_write_sig_legacy(struct tg3 *tp, int kind) +{ + if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { + switch (kind) { + case RESET_KIND_INIT: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_START); + break; + + case RESET_KIND_SHUTDOWN: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_UNLOAD); + break; + + case RESET_KIND_SUSPEND: + tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, + DRV_STATE_SUSPEND); + break; + + default: + break; + }; + } +} + +/* tp->lock is held. */ +static int tg3_chip_reset(struct tg3 *tp) +{ + u32 val; + u32 flags_save; + int i; + + if (!(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) + tg3_nvram_lock(tp); /* * We must avoid the readl() that normally takes place. @@ -3422,23 +3614,66 @@ /* do the reset */ val = GRC_MISC_CFG_CORECLK_RESET; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + + if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + if (tr32(0x7e2c) == 0x60) { + tw32(0x7e2c, 0x20); + } + if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { + tw32(GRC_MISC_CFG, (1 << 29)); + val |= (1 << 29); + } + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) val |= GRC_MISC_CFG_KEEP_GPHY_POWER; tw32(GRC_MISC_CFG, val); /* restore 5701 hardware bug workaround flag */ tp->tg3_flags = flags_save; + /* Unfortunately, we have to delay before the PCI read back. + * Some 575X chips even will not respond to a PCI cfg access + * when the reset command is given to the chip. + * + * How do these hardware designers expect things to work + * properly if the PCI write is posted for a long period + * of time? It is always necessary to have some method by + * which a register read back can occur to push the write + * out which does the reset. + * + * For most tg3 variants the trick below was working. + * Ho hum... + */ + udelay(120); + /* Flush PCI posted writes. The normal MMIO registers * are inaccessible at this time so this is the only - * way to make this reliably. I tried to use indirect + * way to make this reliably (actually, this is no longer + * the case, see above). I tried to use indirect * register read/write but this upset some 5701 variants. */ pci_read_config_dword(tp->pdev, PCI_COMMAND, &val); - udelay(40); - udelay(40); - udelay(40); + udelay(120); + + if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) { + int i; + u32 cfg_val; + + /* Wait for link training to complete. */ + for (i = 0; i < 5000; i++) + udelay(100); + + pci_read_config_dword(tp->pdev, 0xc4, &cfg_val); + pci_write_config_dword(tp->pdev, 0xc4, + cfg_val | (1 << 15)); + } + /* Set PCIE max payload size and clear error status. */ + pci_write_config_dword(tp->pdev, 0xd8, 0xf5000); + } /* Re-enable indirect register accesses. */ pci_write_config_dword(tp->pdev, TG3PCI_MISC_HOST_CTRL, @@ -3460,14 +3695,67 @@ tw32(MEMARB_MODE, MEMARB_MODE_ENABLE); + tw32(GRC_MODE, tp->grc_mode); + + if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) { + u32 val = tr32(0xc4); + + tw32(0xc4, val | (1 << 15)); + } + if ((tp->nic_sram_data_cfg & NIC_SRAM_DATA_CFG_MINI_PCI) != 0 && GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { - tp->pci_clock_ctrl |= - (CLOCK_CTRL_FORCE_CLKRUN | CLOCK_CTRL_CLKRUN_OENABLE); + tp->pci_clock_ctrl |= CLOCK_CTRL_CLKRUN_OENABLE; + if (tp->pci_chip_rev_id == CHIPREV_ID_5705_A0) + tp->pci_clock_ctrl |= CLOCK_CTRL_FORCE_CLKRUN; tw32(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl); } - tw32(TG3PCI_MISC_HOST_CTRL, tp->misc_host_ctrl); + if (tp->phy_id == PHY_ID_SERDES) { + tp->mac_mode = MAC_MODE_PORT_MODE_TBI; + tw32_f(MAC_MODE, tp->mac_mode); + } else + tw32_f(MAC_MODE, 0); + udelay(40); + + /* Wait for firmware initialization to complete. */ + for (i = 0; i < 100000; i++) { + tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); + if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) + break; + udelay(10); + } + if (i >= 100000 && + !(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) { + printk(KERN_ERR PFX "tg3_reset_hw timed out for %s, " + "firmware will not restart magic=%08x\n", + tp->dev->name, val); + return -ENODEV; + } + + if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && + tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) { + u32 val = tr32(0x7c00); + + tw32(0x7c00, val | (1 << 25)); + } + + /* Reprobe ASF enable state. */ + tp->tg3_flags &= ~TG3_FLAG_ENABLE_ASF; + tp->tg3_flags2 &= ~TG3_FLG2_ASF_NEW_HANDSHAKE; + tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); + if (val == NIC_SRAM_DATA_SIG_MAGIC) { + u32 nic_cfg; + + tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); + if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE) { + tp->tg3_flags |= TG3_FLAG_ENABLE_ASF; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + tp->tg3_flags2 |= TG3_FLG2_ASF_NEW_HANDSHAKE; + } + } + + return 0; } /* tp->lock is held. */ @@ -3494,40 +3782,20 @@ /* tp->lock is held. */ static int tg3_halt(struct tg3 *tp) { - u32 val; - int i; + int err; tg3_stop_fw(tp); + + tg3_write_sig_pre_reset(tp, RESET_KIND_SHUTDOWN); + tg3_abort_hw(tp); - tg3_chip_reset(tp); - tg3_write_mem(tp, - NIC_SRAM_FIRMWARE_MBOX, - NIC_SRAM_FIRMWARE_MBOX_MAGIC1); - for (i = 0; i < 100000; i++) { - tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); - if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) - break; - udelay(10); - } + err = tg3_chip_reset(tp); - if (i >= 100000 && - !(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) { - printk(KERN_ERR PFX "tg3_halt timed out for %s, " - "firmware will not restart magic=%08x\n", - tp->dev->name, val); - return -ENODEV; - } + tg3_write_sig_legacy(tp, RESET_KIND_SHUTDOWN); + tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN); - if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { - if (tp->tg3_flags & TG3_FLAG_WOL_ENABLE) - tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, - DRV_STATE_WOL); - else - tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, - DRV_STATE_UNLOAD); - } else - tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, - DRV_STATE_SUSPEND); + if (err) + return err; return 0; } @@ -3828,305 +4096,305 @@ #if TG3_TSO_SUPPORT != 0 #define TG3_TSO_FW_RELEASE_MAJOR 0x1 -#define TG3_TSO_FW_RELASE_MINOR 0x4 +#define TG3_TSO_FW_RELASE_MINOR 0x6 #define TG3_TSO_FW_RELEASE_FIX 0x0 #define TG3_TSO_FW_START_ADDR 0x08000000 #define TG3_TSO_FW_TEXT_ADDR 0x08000000 -#define TG3_TSO_FW_TEXT_LEN 0x1a90 -#define TG3_TSO_FW_RODATA_ADDR 0x08001a900 +#define TG3_TSO_FW_TEXT_LEN 0x1aa0 +#define TG3_TSO_FW_RODATA_ADDR 0x08001aa0 #define TG3_TSO_FW_RODATA_LEN 0x60 #define TG3_TSO_FW_DATA_ADDR 0x08001b20 -#define TG3_TSO_FW_DATA_LEN 0x20 -#define TG3_TSO_FW_SBSS_ADDR 0x08001b40 +#define TG3_TSO_FW_DATA_LEN 0x30 +#define TG3_TSO_FW_SBSS_ADDR 0x08001b50 #define TG3_TSO_FW_SBSS_LEN 0x2c -#define TG3_TSO_FW_BSS_ADDR 0x08001b70 +#define TG3_TSO_FW_BSS_ADDR 0x08001b80 #define TG3_TSO_FW_BSS_LEN 0x894 -static u32 tg3TsoFwText[] = { - 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c1d0800, - 0x37bd4000, 0x03a0f021, 0x3c100800, 0x26100000, 0x0e000010, 0x00000000, - 0x0000000d, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe0, 0x3c04fefe, - 0xafbf0018, 0x0e0005d4, 0x34840002, 0x0e000664, 0x00000000, 0x3c030800, - 0x90631b58, 0x24020002, 0x3c040800, 0x24841a9c, 0x14620003, 0x24050001, - 0x3c040800, 0x24841a90, 0x24060003, 0x00003821, 0xafa00010, 0x0e000678, +static u32 tg3TsoFwText[(TG3_TSO_FW_TEXT_LEN / 4) + 1] = { + 0x0e000003, 0x00000000, 0x08001b24, 0x00000000, 0x10000003, 0x00000000, + 0x0000000d, 0x0000000d, 0x3c1d0800, 0x37bd4000, 0x03a0f021, 0x3c100800, + 0x26100000, 0x0e000010, 0x00000000, 0x0000000d, 0x27bdffe0, 0x3c04fefe, + 0xafbf0018, 0x0e0005d8, 0x34840002, 0x0e000668, 0x00000000, 0x3c030800, + 0x90631b68, 0x24020002, 0x3c040800, 0x24841aac, 0x14620003, 0x24050001, + 0x3c040800, 0x24841aa0, 0x24060006, 0x00003821, 0xafa00010, 0x0e00067c, 0xafa00014, 0x8f625c50, 0x34420001, 0xaf625c50, 0x8f625c90, 0x34420001, 0xaf625c90, 0x2402ffff, 0x0e000034, 0xaf625404, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x27bdffe0, 0xafbf001c, 0xafb20018, 0xafb10014, 0x0e00005b, 0xafb00010, 0x24120002, 0x24110001, 0x8f706820, 0x32020100, 0x10400003, 0x00000000, 0x0e0000bb, 0x00000000, - 0x8f706820, 0x32022000, 0x10400004, 0x32020001, 0x0e0001ef, 0x24040001, + 0x8f706820, 0x32022000, 0x10400004, 0x32020001, 0x0e0001f0, 0x24040001, 0x32020001, 0x10400003, 0x00000000, 0x0e0000a3, 0x00000000, 0x3c020800, - 0x90421b88, 0x14520003, 0x00000000, 0x0e0004bf, 0x00000000, 0x0a00003c, + 0x90421b98, 0x14520003, 0x00000000, 0x0e0004c0, 0x00000000, 0x0a00003c, 0xaf715028, 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, - 0x27bd0020, 0x27bdffe0, 0x3c040800, 0x24841ab0, 0x00002821, 0x00003021, - 0x00003821, 0xafbf0018, 0xafa00010, 0x0e000678, 0xafa00014, 0x3c040800, - 0x248423c8, 0xa4800000, 0x3c010800, 0xa0201b88, 0x3c010800, 0xac201b8c, - 0x3c010800, 0xac201b90, 0x3c010800, 0xac201b94, 0x3c010800, 0xac201b9c, - 0x3c010800, 0xac201ba8, 0x3c010800, 0xac201bac, 0x8f624434, 0x3c010800, - 0xac221b78, 0x8f624438, 0x3c010800, 0xac221b7c, 0x8f624410, 0xac80f7a8, - 0x3c010800, 0xac201b74, 0x3c010800, 0xac2023d0, 0x3c010800, 0xac2023b8, - 0x3c010800, 0xac2023bc, 0x3c010800, 0xac2023f0, 0x3c010800, 0xac221b80, + 0x27bd0020, 0x27bdffe0, 0x3c040800, 0x24841ac0, 0x00002821, 0x00003021, + 0x00003821, 0xafbf0018, 0xafa00010, 0x0e00067c, 0xafa00014, 0x3c040800, + 0x248423d8, 0xa4800000, 0x3c010800, 0xa0201b98, 0x3c010800, 0xac201b9c, + 0x3c010800, 0xac201ba0, 0x3c010800, 0xac201ba4, 0x3c010800, 0xac201bac, + 0x3c010800, 0xac201bb8, 0x3c010800, 0xac201bbc, 0x8f624434, 0x3c010800, + 0xac221b88, 0x8f624438, 0x3c010800, 0xac221b8c, 0x8f624410, 0xac80f7a8, + 0x3c010800, 0xac201b84, 0x3c010800, 0xac2023e0, 0x3c010800, 0xac2023c8, + 0x3c010800, 0xac2023cc, 0x3c010800, 0xac202400, 0x3c010800, 0xac221b90, 0x8f620068, 0x24030007, 0x00021702, 0x10430005, 0x00000000, 0x8f620068, - 0x00021702, 0x14400004, 0x24020001, 0x3c010800, 0x0a000097, 0xac2023fc, - 0xac820034, 0x3c040800, 0x24841abc, 0x3c050800, 0x8ca523fc, 0x00003021, - 0x00003821, 0xafa00010, 0x0e000678, 0xafa00014, 0x8fbf0018, 0x03e00008, - 0x27bd0020, 0x27bdffe0, 0x3c040800, 0x24841ac8, 0x00002821, 0x00003021, - 0x00003821, 0xafbf0018, 0xafa00010, 0x0e000678, 0xafa00014, 0x0e00005b, + 0x00021702, 0x14400004, 0x24020001, 0x3c010800, 0x0a000097, 0xac20240c, + 0xac820034, 0x3c040800, 0x24841acc, 0x3c050800, 0x8ca5240c, 0x00003021, + 0x00003821, 0xafa00010, 0x0e00067c, 0xafa00014, 0x8fbf0018, 0x03e00008, + 0x27bd0020, 0x27bdffe0, 0x3c040800, 0x24841ad8, 0x00002821, 0x00003021, + 0x00003821, 0xafbf0018, 0xafa00010, 0x0e00067c, 0xafa00014, 0x0e00005b, 0x00000000, 0x0e0000b4, 0x00002021, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x24020001, 0x8f636820, 0x00821004, 0x00021027, 0x00621824, 0x03e00008, 0xaf636820, 0x27bdffd0, 0xafbf002c, 0xafb60028, 0xafb50024, 0xafb40020, 0xafb3001c, 0xafb20018, 0xafb10014, 0xafb00010, 0x8f675c5c, 0x3c030800, - 0x24631bac, 0x8c620000, 0x14470005, 0x3c0200ff, 0x3c020800, 0x90421b88, - 0x14400118, 0x3c0200ff, 0x3442fff8, 0x00e28824, 0xac670000, 0x00111902, + 0x24631bbc, 0x8c620000, 0x14470005, 0x3c0200ff, 0x3c020800, 0x90421b98, + 0x14400119, 0x3c0200ff, 0x3442fff8, 0x00e28824, 0xac670000, 0x00111902, 0x306300ff, 0x30e20003, 0x000211c0, 0x00622825, 0x00a04021, 0x00071602, - 0x3c030800, 0x90631b88, 0x3044000f, 0x14600036, 0x00804821, 0x24020001, - 0x3c010800, 0xa0221b88, 0x00051100, 0x00821025, 0x3c010800, 0xac201b8c, - 0x3c010800, 0xac201b90, 0x3c010800, 0xac201b94, 0x3c010800, 0xac201b9c, - 0x3c010800, 0xac201ba8, 0x3c010800, 0xac201ba0, 0x3c010800, 0xac201ba4, - 0x3c010800, 0xa42223c8, 0x9622000c, 0x30437fff, 0x3c010800, 0xa4222400, - 0x30428000, 0x3c010800, 0xa4231bb6, 0x10400005, 0x24020001, 0x3c010800, - 0xac2223e4, 0x0a000102, 0x2406003e, 0x24060036, 0x3c010800, 0xac2023e4, - 0x9622000a, 0x3c030800, 0x94631bb6, 0x3c010800, 0xac2023e0, 0x3c010800, - 0xac2023e8, 0x00021302, 0x00021080, 0x00c21021, 0x00621821, 0x3c010800, - 0xa42223c0, 0x3c010800, 0x0a000115, 0xa4231b86, 0x9622000c, 0x3c010800, - 0xa42223dc, 0x3c040800, 0x24841b8c, 0x8c820000, 0x00021100, 0x3c010800, - 0x00220821, 0xac311bb8, 0x8c820000, 0x00021100, 0x3c010800, 0x00220821, - 0xac271bbc, 0x8c820000, 0x25030001, 0x306601ff, 0x00021100, 0x3c010800, - 0x00220821, 0xac261bc0, 0x8c820000, 0x00021100, 0x3c010800, 0x00220821, - 0xac291bc4, 0x96230008, 0x3c020800, 0x8c421b9c, 0x00432821, 0x3c010800, - 0xac251b9c, 0x9622000a, 0x30420004, 0x14400018, 0x00061100, 0x8f630c14, + 0x3c030800, 0x90631b98, 0x3044000f, 0x14600036, 0x00804821, 0x24020001, + 0x3c010800, 0xa0221b98, 0x00051100, 0x00821025, 0x3c010800, 0xac201b9c, + 0x3c010800, 0xac201ba0, 0x3c010800, 0xac201ba4, 0x3c010800, 0xac201bac, + 0x3c010800, 0xac201bb8, 0x3c010800, 0xac201bb0, 0x3c010800, 0xac201bb4, + 0x3c010800, 0xa42223d8, 0x9622000c, 0x30437fff, 0x3c010800, 0xa4222410, + 0x30428000, 0x3c010800, 0xa4231bc6, 0x10400005, 0x24020001, 0x3c010800, + 0xac2223f4, 0x0a000102, 0x2406003e, 0x24060036, 0x3c010800, 0xac2023f4, + 0x9622000a, 0x3c030800, 0x94631bc6, 0x3c010800, 0xac2023f0, 0x3c010800, + 0xac2023f8, 0x00021302, 0x00021080, 0x00c21021, 0x00621821, 0x3c010800, + 0xa42223d0, 0x3c010800, 0x0a000115, 0xa4231b96, 0x9622000c, 0x3c010800, + 0xa42223ec, 0x3c040800, 0x24841b9c, 0x8c820000, 0x00021100, 0x3c010800, + 0x00220821, 0xac311bc8, 0x8c820000, 0x00021100, 0x3c010800, 0x00220821, + 0xac271bcc, 0x8c820000, 0x25030001, 0x306601ff, 0x00021100, 0x3c010800, + 0x00220821, 0xac261bd0, 0x8c820000, 0x00021100, 0x3c010800, 0x00220821, + 0xac291bd4, 0x96230008, 0x3c020800, 0x8c421bac, 0x00432821, 0x3c010800, + 0xac251bac, 0x9622000a, 0x30420004, 0x14400018, 0x00061100, 0x8f630c14, 0x3063000f, 0x2c620002, 0x1440000b, 0x3c02c000, 0x8f630c14, 0x3c020800, - 0x8c421b30, 0x3063000f, 0x24420001, 0x3c010800, 0xac221b30, 0x2c620002, + 0x8c421b40, 0x3063000f, 0x24420001, 0x3c010800, 0xac221b40, 0x2c620002, 0x1040fff7, 0x3c02c000, 0x00e21825, 0xaf635c5c, 0x8f625c50, 0x30420002, - 0x10400014, 0x00000000, 0x0a000147, 0x00000000, 0x3c030800, 0x8c631b70, - 0x3c040800, 0x94841b84, 0x01221025, 0x3c010800, 0xa42223ca, 0x24020001, - 0x3c010800, 0xac221ba8, 0x24630001, 0x0085202a, 0x3c010800, 0x10800003, - 0xac231b70, 0x3c010800, 0xa4251b84, 0x3c060800, 0x24c61b8c, 0x8cc20000, - 0x24420001, 0xacc20000, 0x28420080, 0x14400005, 0x00000000, 0x0e000652, - 0x24040002, 0x0a0001e5, 0x00000000, 0x3c020800, 0x8c421ba8, 0x10400077, - 0x24020001, 0x3c050800, 0x90a51b88, 0x14a20071, 0x00000000, 0x3c150800, - 0x96b51b86, 0x3c040800, 0x8c841b9c, 0x32a3ffff, 0x0083102a, 0x1440006b, - 0x00000000, 0x14830003, 0x00000000, 0x3c010800, 0xac2523e0, 0x1060005b, + 0x10400014, 0x00000000, 0x0a000147, 0x00000000, 0x3c030800, 0x8c631b80, + 0x3c040800, 0x94841b94, 0x01221025, 0x3c010800, 0xa42223da, 0x24020001, + 0x3c010800, 0xac221bb8, 0x24630001, 0x0085202a, 0x3c010800, 0x10800003, + 0xac231b80, 0x3c010800, 0xa4251b94, 0x3c060800, 0x24c61b9c, 0x8cc20000, + 0x24420001, 0xacc20000, 0x28420080, 0x14400005, 0x00000000, 0x0e000656, + 0x24040002, 0x0a0001e6, 0x00000000, 0x3c020800, 0x8c421bb8, 0x10400078, + 0x24020001, 0x3c050800, 0x90a51b98, 0x14a20072, 0x00000000, 0x3c150800, + 0x96b51b96, 0x3c040800, 0x8c841bac, 0x32a3ffff, 0x0083102a, 0x1440006c, + 0x00000000, 0x14830003, 0x00000000, 0x3c010800, 0xac2523f0, 0x1060005c, 0x00009021, 0x24d60004, 0x0060a021, 0x24d30014, 0x8ec20000, 0x00028100, - 0x3c110800, 0x02308821, 0x0e000621, 0x8e311bb8, 0x00402821, 0x10a00053, - 0x00000000, 0x9628000a, 0x31020040, 0x10400004, 0x2407180c, 0x8e22000c, - 0x2407188c, 0xaca20018, 0x3c030800, 0x00701821, 0x8c631bc0, 0x3c020800, - 0x00501021, 0x8c421bc4, 0x00031d00, 0x00021400, 0x00621825, 0xaca30014, - 0x8ec30004, 0x96220008, 0x00432023, 0x3242ffff, 0x3083ffff, 0x00431021, - 0x0282102a, 0x14400002, 0x02b23023, 0x00803021, 0x8e620000, 0x30c4ffff, - 0x00441021, 0xae620000, 0x8e220000, 0xaca20000, 0x8e220004, 0x8e63fff4, - 0x00431021, 0xaca20004, 0xa4a6000e, 0x8e62fff4, 0x00441021, 0xae62fff4, - 0x96230008, 0x0043102a, 0x14400005, 0x02469021, 0x8e62fff0, 0xae60fff4, - 0x24420001, 0xae62fff0, 0xaca00008, 0x3242ffff, 0x14540008, 0x24020305, - 0x31020080, 0x54400001, 0x34e70010, 0x24020905, 0xa4a2000c, 0x0a0001ca, - 0x34e70020, 0xa4a2000c, 0x3c020800, 0x8c4223e0, 0x10400003, 0x3c024b65, - 0x0a0001d2, 0x34427654, 0x3c02b49a, 0x344289ab, 0xaca2001c, 0x30e2ffff, - 0xaca20010, 0x0e00059f, 0x00a02021, 0x3242ffff, 0x0054102b, 0x1440ffaa, - 0x00000000, 0x24020002, 0x3c010800, 0x0a0001e5, 0xa0221b88, 0x8ec2083c, - 0x24420001, 0x0a0001e5, 0xaec2083c, 0x0e0004bf, 0x00000000, 0x8fbf002c, - 0x8fb60028, 0x8fb50024, 0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, - 0x8fb00010, 0x03e00008, 0x27bd0030, 0x27bdffd0, 0xafbf0028, 0xafb30024, - 0xafb20020, 0xafb1001c, 0xafb00018, 0x8f725c9c, 0x3c0200ff, 0x3442fff8, - 0x3c060800, 0x24c61ba4, 0x02428824, 0x9623000e, 0x8cc20000, 0x00431021, - 0xacc20000, 0x8e220010, 0x30420020, 0x14400011, 0x00809821, 0x0e000637, - 0x02202021, 0x3c02c000, 0x02421825, 0xaf635c9c, 0x8f625c90, 0x30420002, - 0x10400121, 0x00000000, 0xaf635c9c, 0x8f625c90, 0x30420002, 0x1040011c, - 0x00000000, 0x0a00020c, 0x00000000, 0x8e240008, 0x8e230014, 0x00041402, - 0x000241c0, 0x00031502, 0x304201ff, 0x2442ffff, 0x3042007f, 0x00031942, - 0x30637800, 0x00021100, 0x24424000, 0x00625021, 0x9542000a, 0x3084ffff, - 0x30420008, 0x104000b3, 0x000429c0, 0x3c020800, 0x8c4223f0, 0x1440002d, - 0x25050008, 0x95020014, 0x3c010800, 0xa42223c0, 0x8d070010, 0x00071402, - 0x3c010800, 0xa42223c2, 0x3c010800, 0xa42723c4, 0x9502000e, 0x30e3ffff, - 0x00431023, 0x3c010800, 0xac2223f8, 0x8f626800, 0x3c030010, 0x00431024, - 0x10400005, 0x00000000, 0x9503001a, 0x9502001c, 0x0a000241, 0x00431021, - 0x9502001a, 0x3c010800, 0xac2223ec, 0x3c02c000, 0x02421825, 0x3c010800, - 0xac2823f0, 0x3c010800, 0xac3223f4, 0xaf635c9c, 0x8f625c90, 0x30420002, - 0x104000df, 0x00000000, 0xaf635c9c, 0x8f625c90, 0x30420002, 0x104000da, - 0x00000000, 0x0a00024e, 0x00000000, 0x9502000e, 0x3c030800, 0x946323c4, - 0x00434823, 0x3123ffff, 0x2c620008, 0x1040001c, 0x00000000, 0x95020014, - 0x24420028, 0x00a22821, 0x00031042, 0x1840000b, 0x00002021, 0x24c60848, - 0x00403821, 0x94a30000, 0x8cc20000, 0x24840001, 0x00431021, 0xacc20000, - 0x0087102a, 0x1440fff9, 0x24a50002, 0x31220001, 0x1040001f, 0x3c024000, - 0x3c040800, 0x248423ec, 0xa0a00001, 0x94a30000, 0x8c820000, 0x00431021, - 0x0a00028d, 0xac820000, 0x8f626800, 0x3c030010, 0x00431024, 0x10400009, - 0x00000000, 0x9502001a, 0x3c030800, 0x8c6323ec, 0x00431021, 0x3c010800, - 0xac2223ec, 0x0a00028e, 0x3c024000, 0x9502001a, 0x9504001c, 0x3c030800, - 0x8c6323ec, 0x00441023, 0x00621821, 0x3c010800, 0xac2323ec, 0x3c024000, - 0x02421825, 0xaf635c9c, 0x8f625c90, 0x30420002, 0x1440fffc, 0x00000000, - 0x9542000a, 0x30420010, 0x10400095, 0x00000000, 0x3c060800, 0x24c623f0, - 0x3c020800, 0x944223c4, 0x8cc50000, 0x3c040800, 0x8c8423f8, 0x24420030, - 0x00a22821, 0x94a20004, 0x3c030800, 0x8c6323ec, 0x00441023, 0x00621821, - 0x00603821, 0x00032402, 0x30e2ffff, 0x00823821, 0x00071402, 0x00e23821, - 0x00071027, 0x3c010800, 0xac2323ec, 0xa4a20006, 0x3c030800, 0x8c6323f4, - 0x3c0200ff, 0x3442fff8, 0x00628824, 0x96220008, 0x24040001, 0x24034000, - 0x000241c0, 0x00e01021, 0xa502001a, 0xa500001c, 0xacc00000, 0x3c010800, - 0xac241b50, 0xaf635cb8, 0x8f625cb0, 0x30420002, 0x10400003, 0x00000000, - 0x3c010800, 0xac201b50, 0x8e220008, 0xaf625cb8, 0x8f625cb0, 0x30420002, - 0x10400003, 0x00000000, 0x3c010800, 0xac201b50, 0x3c020800, 0x8c421b50, - 0x1040ffec, 0x00000000, 0x3c040800, 0x0e000637, 0x8c8423f4, 0x0a00032c, - 0x00000000, 0x3c030800, 0x90631b88, 0x24020002, 0x14620003, 0x3c034b65, - 0x0a0002e3, 0x00008021, 0x8e22001c, 0x34637654, 0x10430002, 0x24100002, - 0x24100001, 0x01002021, 0x0e000352, 0x02003021, 0x24020003, 0x3c010800, - 0xa0221b88, 0x24020002, 0x1202000a, 0x24020001, 0x3c030800, 0x8c6323e0, - 0x10620006, 0x00000000, 0x3c020800, 0x944223c8, 0x00021400, 0x0a000321, - 0xae220014, 0x3c040800, 0x248423ca, 0x94820000, 0x00021400, 0xae220014, - 0x3c020800, 0x8c421bac, 0x3c03c000, 0x3c010800, 0xa0201b88, 0x00431025, - 0xaf625c5c, 0x8f625c50, 0x30420002, 0x10400009, 0x00000000, 0x2484f7e2, - 0x8c820000, 0x00431025, 0xaf625c5c, 0x8f625c50, 0x30420002, 0x1440fffa, - 0x00000000, 0x3c020800, 0x24421b74, 0x8c430000, 0x24630001, 0xac430000, - 0x8f630c14, 0x3063000f, 0x2c620002, 0x1440000c, 0x3c024000, 0x8f630c14, - 0x3c020800, 0x8c421b30, 0x3063000f, 0x24420001, 0x3c010800, 0xac221b30, - 0x2c620002, 0x1040fff7, 0x00000000, 0x3c024000, 0x02421825, 0xaf635c9c, - 0x8f625c90, 0x30420002, 0x1440fffc, 0x00000000, 0x12600003, 0x00000000, - 0x0e0004bf, 0x00000000, 0x8fbf0028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, - 0x8fb00018, 0x03e00008, 0x27bd0030, 0x8f634450, 0x3c040800, 0x24841b78, - 0x8c820000, 0x00031c02, 0x0043102b, 0x14400007, 0x3c038000, 0x8c840004, - 0x8f624450, 0x00021c02, 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, - 0x8f624444, 0x00431024, 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, - 0x3042ffff, 0x3c024000, 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, - 0x1440fffc, 0x00000000, 0x03e00008, 0x00000000, 0x27bdffe0, 0x00805821, - 0x14c00011, 0x256e0008, 0x3c020800, 0x8c4223e4, 0x10400007, 0x24020016, - 0x3c010800, 0xa42223c2, 0x2402002a, 0x3c010800, 0x0a000366, 0xa42223c4, - 0x8d670010, 0x00071402, 0x3c010800, 0xa42223c2, 0x3c010800, 0xa42723c4, - 0x3c040800, 0x948423c4, 0x3c030800, 0x946323c2, 0x95cf0006, 0x3c020800, - 0x944223c0, 0x00832023, 0x01e2c023, 0x3065ffff, 0x24a20028, 0x01c24821, - 0x3082ffff, 0x14c0001a, 0x01226021, 0x9582000c, 0x3042003f, 0x3c010800, - 0xa42223c6, 0x95820004, 0x95830006, 0x3c010800, 0xac2023d4, 0x3c010800, - 0xac2023d8, 0x00021400, 0x00431025, 0x3c010800, 0xac221bb0, 0x95220004, - 0x3c010800, 0xa4221bb4, 0x95230002, 0x01e51023, 0x0043102a, 0x10400010, - 0x24020001, 0x3c010800, 0x0a00039a, 0xac2223e8, 0x3c030800, 0x8c6323d8, - 0x3c020800, 0x94421bb4, 0x00431021, 0xa5220004, 0x3c020800, 0x94421bb0, - 0xa5820004, 0x3c020800, 0x8c421bb0, 0xa5820006, 0x3c020800, 0x8c4223e0, - 0x3c0d0800, 0x8dad23d4, 0x3c0a0800, 0x144000e5, 0x8d4a23d8, 0x3c020800, - 0x94421bb4, 0x004a1821, 0x3063ffff, 0x0062182b, 0x24020002, 0x10c2000d, - 0x01435023, 0x3c020800, 0x944223c6, 0x30420009, 0x10400008, 0x00000000, - 0x9582000c, 0x3042fff6, 0xa582000c, 0x3c020800, 0x944223c6, 0x30420009, - 0x01a26823, 0x3c020800, 0x8c4223e8, 0x1040004a, 0x01203821, 0x3c020800, - 0x944223c2, 0x00004021, 0xa520000a, 0x01e21023, 0xa5220002, 0x3082ffff, - 0x00021042, 0x18400008, 0x00003021, 0x00401821, 0x94e20000, 0x25080001, - 0x00c23021, 0x0103102a, 0x1440fffb, 0x24e70002, 0x00061c02, 0x30c2ffff, - 0x00623021, 0x00061402, 0x00c23021, 0x00c02821, 0x00061027, 0xa522000a, - 0x00003021, 0x2527000c, 0x00004021, 0x94e20000, 0x25080001, 0x00c23021, - 0x2d020004, 0x1440fffb, 0x24e70002, 0x95220002, 0x00004021, 0x91230009, - 0x00442023, 0x01803821, 0x3082ffff, 0xa4e00010, 0x00621821, 0x00021042, - 0x18400010, 0x00c33021, 0x00404821, 0x94e20000, 0x24e70002, 0x00c23021, + 0x3c110800, 0x02308821, 0x0e000625, 0x8e311bc8, 0x00402821, 0x10a00054, + 0x00000000, 0x9628000a, 0x31020040, 0x10400005, 0x2407180c, 0x8e22000c, + 0x2407188c, 0x00021400, 0xaca20018, 0x3c030800, 0x00701821, 0x8c631bd0, + 0x3c020800, 0x00501021, 0x8c421bd4, 0x00031d00, 0x00021400, 0x00621825, + 0xaca30014, 0x8ec30004, 0x96220008, 0x00432023, 0x3242ffff, 0x3083ffff, + 0x00431021, 0x0282102a, 0x14400002, 0x02b23023, 0x00803021, 0x8e620000, + 0x30c4ffff, 0x00441021, 0xae620000, 0x8e220000, 0xaca20000, 0x8e220004, + 0x8e63fff4, 0x00431021, 0xaca20004, 0xa4a6000e, 0x8e62fff4, 0x00441021, + 0xae62fff4, 0x96230008, 0x0043102a, 0x14400005, 0x02469021, 0x8e62fff0, + 0xae60fff4, 0x24420001, 0xae62fff0, 0xaca00008, 0x3242ffff, 0x14540008, + 0x24020305, 0x31020080, 0x54400001, 0x34e70010, 0x24020905, 0xa4a2000c, + 0x0a0001cb, 0x34e70020, 0xa4a2000c, 0x3c020800, 0x8c4223f0, 0x10400003, + 0x3c024b65, 0x0a0001d3, 0x34427654, 0x3c02b49a, 0x344289ab, 0xaca2001c, + 0x30e2ffff, 0xaca20010, 0x0e0005a2, 0x00a02021, 0x3242ffff, 0x0054102b, + 0x1440ffa9, 0x00000000, 0x24020002, 0x3c010800, 0x0a0001e6, 0xa0221b98, + 0x8ec2083c, 0x24420001, 0x0a0001e6, 0xaec2083c, 0x0e0004c0, 0x00000000, + 0x8fbf002c, 0x8fb60028, 0x8fb50024, 0x8fb40020, 0x8fb3001c, 0x8fb20018, + 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0030, 0x27bdffd0, 0xafbf0028, + 0xafb30024, 0xafb20020, 0xafb1001c, 0xafb00018, 0x8f725c9c, 0x3c0200ff, + 0x3442fff8, 0x3c070800, 0x24e71bb4, 0x02428824, 0x9623000e, 0x8ce20000, + 0x00431021, 0xace20000, 0x8e220010, 0x30420020, 0x14400011, 0x00809821, + 0x0e00063b, 0x02202021, 0x3c02c000, 0x02421825, 0xaf635c9c, 0x8f625c90, + 0x30420002, 0x1040011e, 0x00000000, 0xaf635c9c, 0x8f625c90, 0x30420002, + 0x10400119, 0x00000000, 0x0a00020d, 0x00000000, 0x8e240008, 0x8e230014, + 0x00041402, 0x000231c0, 0x00031502, 0x304201ff, 0x2442ffff, 0x3042007f, + 0x00031942, 0x30637800, 0x00021100, 0x24424000, 0x00624821, 0x9522000a, + 0x3084ffff, 0x30420008, 0x104000b0, 0x000429c0, 0x3c020800, 0x8c422400, + 0x14400024, 0x24c50008, 0x94c20014, 0x3c010800, 0xa42223d0, 0x8cc40010, + 0x00041402, 0x3c010800, 0xa42223d2, 0x3c010800, 0xa42423d4, 0x94c2000e, + 0x3083ffff, 0x00431023, 0x3c010800, 0xac222408, 0x94c2001a, 0x3c010800, + 0xac262400, 0x3c010800, 0xac322404, 0x3c010800, 0xac2223fc, 0x3c02c000, + 0x02421825, 0xaf635c9c, 0x8f625c90, 0x30420002, 0x104000e5, 0x00000000, + 0xaf635c9c, 0x8f625c90, 0x30420002, 0x104000e0, 0x00000000, 0x0a000246, + 0x00000000, 0x94c2000e, 0x3c030800, 0x946323d4, 0x00434023, 0x3103ffff, + 0x2c620008, 0x1040001c, 0x00000000, 0x94c20014, 0x24420028, 0x00a22821, + 0x00031042, 0x1840000b, 0x00002021, 0x24e60848, 0x00403821, 0x94a30000, + 0x8cc20000, 0x24840001, 0x00431021, 0xacc20000, 0x0087102a, 0x1440fff9, + 0x24a50002, 0x31020001, 0x1040001f, 0x3c024000, 0x3c040800, 0x248423fc, + 0xa0a00001, 0x94a30000, 0x8c820000, 0x00431021, 0x0a000285, 0xac820000, + 0x8f626800, 0x3c030010, 0x00431024, 0x10400009, 0x00000000, 0x94c2001a, + 0x3c030800, 0x8c6323fc, 0x00431021, 0x3c010800, 0xac2223fc, 0x0a000286, + 0x3c024000, 0x94c2001a, 0x94c4001c, 0x3c030800, 0x8c6323fc, 0x00441023, + 0x00621821, 0x3c010800, 0xac2323fc, 0x3c024000, 0x02421825, 0xaf635c9c, + 0x8f625c90, 0x30420002, 0x1440fffc, 0x00000000, 0x9522000a, 0x30420010, + 0x1040009b, 0x00000000, 0x3c030800, 0x946323d4, 0x3c070800, 0x24e72400, + 0x8ce40000, 0x8f626800, 0x24630030, 0x00832821, 0x3c030010, 0x00431024, + 0x1440000a, 0x00000000, 0x94a20004, 0x3c040800, 0x8c842408, 0x3c030800, + 0x8c6323fc, 0x00441023, 0x00621821, 0x3c010800, 0xac2323fc, 0x3c040800, + 0x8c8423fc, 0x00041c02, 0x3082ffff, 0x00622021, 0x00041402, 0x00822021, + 0x00041027, 0xa4a20006, 0x3c030800, 0x8c632404, 0x3c0200ff, 0x3442fff8, + 0x00628824, 0x96220008, 0x24050001, 0x24034000, 0x000231c0, 0x00801021, + 0xa4c2001a, 0xa4c0001c, 0xace00000, 0x3c010800, 0xac251b60, 0xaf635cb8, + 0x8f625cb0, 0x30420002, 0x10400003, 0x00000000, 0x3c010800, 0xac201b60, + 0x8e220008, 0xaf625cb8, 0x8f625cb0, 0x30420002, 0x10400003, 0x00000000, + 0x3c010800, 0xac201b60, 0x3c020800, 0x8c421b60, 0x1040ffec, 0x00000000, + 0x3c040800, 0x0e00063b, 0x8c842404, 0x0a00032a, 0x00000000, 0x3c030800, + 0x90631b98, 0x24020002, 0x14620003, 0x3c034b65, 0x0a0002e1, 0x00008021, + 0x8e22001c, 0x34637654, 0x10430002, 0x24100002, 0x24100001, 0x00c02021, + 0x0e000350, 0x02003021, 0x24020003, 0x3c010800, 0xa0221b98, 0x24020002, + 0x1202000a, 0x24020001, 0x3c030800, 0x8c6323f0, 0x10620006, 0x00000000, + 0x3c020800, 0x944223d8, 0x00021400, 0x0a00031f, 0xae220014, 0x3c040800, + 0x248423da, 0x94820000, 0x00021400, 0xae220014, 0x3c020800, 0x8c421bbc, + 0x3c03c000, 0x3c010800, 0xa0201b98, 0x00431025, 0xaf625c5c, 0x8f625c50, + 0x30420002, 0x10400009, 0x00000000, 0x2484f7e2, 0x8c820000, 0x00431025, + 0xaf625c5c, 0x8f625c50, 0x30420002, 0x1440fffa, 0x00000000, 0x3c020800, + 0x24421b84, 0x8c430000, 0x24630001, 0xac430000, 0x8f630c14, 0x3063000f, + 0x2c620002, 0x1440000c, 0x3c024000, 0x8f630c14, 0x3c020800, 0x8c421b40, + 0x3063000f, 0x24420001, 0x3c010800, 0xac221b40, 0x2c620002, 0x1040fff7, + 0x00000000, 0x3c024000, 0x02421825, 0xaf635c9c, 0x8f625c90, 0x30420002, + 0x1440fffc, 0x00000000, 0x12600003, 0x00000000, 0x0e0004c0, 0x00000000, + 0x8fbf0028, 0x8fb30024, 0x8fb20020, 0x8fb1001c, 0x8fb00018, 0x03e00008, + 0x27bd0030, 0x8f634450, 0x3c040800, 0x24841b88, 0x8c820000, 0x00031c02, + 0x0043102b, 0x14400007, 0x3c038000, 0x8c840004, 0x8f624450, 0x00021c02, + 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, 0x00431024, + 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, 0x3c024000, + 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, 0x00000000, + 0x03e00008, 0x00000000, 0x27bdffe0, 0x00805821, 0x14c00011, 0x256e0008, + 0x3c020800, 0x8c4223f4, 0x10400007, 0x24020016, 0x3c010800, 0xa42223d2, + 0x2402002a, 0x3c010800, 0x0a000364, 0xa42223d4, 0x8d670010, 0x00071402, + 0x3c010800, 0xa42223d2, 0x3c010800, 0xa42723d4, 0x3c040800, 0x948423d4, + 0x3c030800, 0x946323d2, 0x95cf0006, 0x3c020800, 0x944223d0, 0x00832023, + 0x01e2c023, 0x3065ffff, 0x24a20028, 0x01c24821, 0x3082ffff, 0x14c0001a, + 0x01226021, 0x9582000c, 0x3042003f, 0x3c010800, 0xa42223d6, 0x95820004, + 0x95830006, 0x3c010800, 0xac2023e4, 0x3c010800, 0xac2023e8, 0x00021400, + 0x00431025, 0x3c010800, 0xac221bc0, 0x95220004, 0x3c010800, 0xa4221bc4, + 0x95230002, 0x01e51023, 0x0043102a, 0x10400010, 0x24020001, 0x3c010800, + 0x0a000398, 0xac2223f8, 0x3c030800, 0x8c6323e8, 0x3c020800, 0x94421bc4, + 0x00431021, 0xa5220004, 0x3c020800, 0x94421bc0, 0xa5820004, 0x3c020800, + 0x8c421bc0, 0xa5820006, 0x3c020800, 0x8c4223f0, 0x3c0d0800, 0x8dad23e4, + 0x3c0a0800, 0x144000e5, 0x8d4a23e8, 0x3c020800, 0x94421bc4, 0x004a1821, + 0x3063ffff, 0x0062182b, 0x24020002, 0x10c2000d, 0x01435023, 0x3c020800, + 0x944223d6, 0x30420009, 0x10400008, 0x00000000, 0x9582000c, 0x3042fff6, + 0xa582000c, 0x3c020800, 0x944223d6, 0x30420009, 0x01a26823, 0x3c020800, + 0x8c4223f8, 0x1040004a, 0x01203821, 0x3c020800, 0x944223d2, 0x00004021, + 0xa520000a, 0x01e21023, 0xa5220002, 0x3082ffff, 0x00021042, 0x18400008, + 0x00003021, 0x00401821, 0x94e20000, 0x25080001, 0x00c23021, 0x0103102a, + 0x1440fffb, 0x24e70002, 0x00061c02, 0x30c2ffff, 0x00623021, 0x00061402, + 0x00c23021, 0x00c02821, 0x00061027, 0xa522000a, 0x00003021, 0x2527000c, + 0x00004021, 0x94e20000, 0x25080001, 0x00c23021, 0x2d020004, 0x1440fffb, + 0x24e70002, 0x95220002, 0x00004021, 0x91230009, 0x00442023, 0x01803821, + 0x3082ffff, 0xa4e00010, 0x00621821, 0x00021042, 0x18400010, 0x00c33021, + 0x00404821, 0x94e20000, 0x24e70002, 0x00c23021, 0x30e2007f, 0x14400006, + 0x25080001, 0x8d630000, 0x3c02007f, 0x3442ff80, 0x00625824, 0x25670008, + 0x0109102a, 0x1440fff3, 0x00000000, 0x30820001, 0x10400005, 0x00061c02, + 0xa0e00001, 0x94e20000, 0x00c23021, 0x00061c02, 0x30c2ffff, 0x00623021, + 0x00061402, 0x00c23021, 0x0a00047d, 0x30c6ffff, 0x24020002, 0x14c20081, + 0x00000000, 0x3c020800, 0x8c42240c, 0x14400007, 0x00000000, 0x3c020800, + 0x944223d2, 0x95230002, 0x01e21023, 0x10620077, 0x00000000, 0x3c020800, + 0x944223d2, 0x01e21023, 0xa5220002, 0x3c020800, 0x8c42240c, 0x1040001a, + 0x31e3ffff, 0x8dc70010, 0x3c020800, 0x94421b96, 0x00e04021, 0x00072c02, + 0x00aa2021, 0x00431023, 0x00823823, 0x00072402, 0x30e2ffff, 0x00823821, + 0x00071027, 0xa522000a, 0x3102ffff, 0x3c040800, 0x948423d4, 0x00453023, + 0x00e02821, 0x00641823, 0x006d1821, 0x00c33021, 0x00061c02, 0x30c2ffff, + 0x0a00047d, 0x00623021, 0x01203821, 0x00004021, 0x3082ffff, 0x00021042, + 0x18400008, 0x00003021, 0x00401821, 0x94e20000, 0x25080001, 0x00c23021, + 0x0103102a, 0x1440fffb, 0x24e70002, 0x00061c02, 0x30c2ffff, 0x00623021, + 0x00061402, 0x00c23021, 0x00c02821, 0x00061027, 0xa522000a, 0x00003021, + 0x2527000c, 0x00004021, 0x94e20000, 0x25080001, 0x00c23021, 0x2d020004, + 0x1440fffb, 0x24e70002, 0x95220002, 0x00004021, 0x91230009, 0x00442023, + 0x01803821, 0x3082ffff, 0xa4e00010, 0x3c040800, 0x948423d4, 0x00621821, + 0x00c33021, 0x00061c02, 0x30c2ffff, 0x00623021, 0x00061c02, 0x3c020800, + 0x944223d0, 0x00c34821, 0x00441023, 0x00021fc2, 0x00431021, 0x00021043, + 0x18400010, 0x00003021, 0x00402021, 0x94e20000, 0x24e70002, 0x00c23021, 0x30e2007f, 0x14400006, 0x25080001, 0x8d630000, 0x3c02007f, 0x3442ff80, - 0x00625824, 0x25670008, 0x0109102a, 0x1440fff3, 0x00000000, 0x30820001, - 0x10400005, 0x00061c02, 0xa0e00001, 0x94e20000, 0x00c23021, 0x00061c02, - 0x30c2ffff, 0x00623021, 0x00061402, 0x00c23021, 0x0a00047f, 0x30c6ffff, - 0x24020002, 0x14c20081, 0x00000000, 0x3c020800, 0x8c4223fc, 0x14400007, - 0x00000000, 0x3c020800, 0x944223c2, 0x95230002, 0x01e21023, 0x10620077, - 0x00000000, 0x3c020800, 0x944223c2, 0x01e21023, 0xa5220002, 0x3c020800, - 0x8c4223fc, 0x1040001a, 0x31e3ffff, 0x8dc70010, 0x3c020800, 0x94421b86, - 0x00e04021, 0x00072c02, 0x00aa2021, 0x00431023, 0x00823823, 0x00072402, - 0x30e2ffff, 0x00823821, 0x00071027, 0xa522000a, 0x3102ffff, 0x3c040800, - 0x948423c4, 0x00453023, 0x00e02821, 0x00641823, 0x006d1821, 0x00c33021, - 0x00061c02, 0x30c2ffff, 0x0a00047f, 0x00623021, 0x01203821, 0x00004021, - 0x3082ffff, 0x00021042, 0x18400008, 0x00003021, 0x00401821, 0x94e20000, - 0x25080001, 0x00c23021, 0x0103102a, 0x1440fffb, 0x24e70002, 0x00061c02, - 0x30c2ffff, 0x00623021, 0x00061402, 0x00c23021, 0x00c02821, 0x00061027, - 0xa522000a, 0x00003021, 0x2527000c, 0x00004021, 0x94e20000, 0x25080001, - 0x00c23021, 0x2d020004, 0x1440fffb, 0x24e70002, 0x95220002, 0x00004021, - 0x91230009, 0x00442023, 0x01803821, 0x3082ffff, 0xa4e00010, 0x3c040800, - 0x948423c4, 0x00621821, 0x00c33021, 0x00061c02, 0x30c2ffff, 0x00623021, - 0x00061c02, 0x3c020800, 0x944223c0, 0x00c34821, 0x00441023, 0x00021fc2, - 0x00431021, 0x00021043, 0x18400010, 0x00003021, 0x00402021, 0x94e20000, - 0x24e70002, 0x00c23021, 0x30e2007f, 0x14400006, 0x25080001, 0x8d630000, - 0x3c02007f, 0x3442ff80, 0x00625824, 0x25670008, 0x0104102a, 0x1440fff3, - 0x00000000, 0x3c020800, 0x944223dc, 0x00c23021, 0x3122ffff, 0x00c23021, - 0x00061c02, 0x30c2ffff, 0x00623021, 0x00061402, 0x00c23021, 0x00c04021, - 0x00061027, 0xa5820010, 0xadc00014, 0x0a00049f, 0xadc00000, 0x8dc70010, - 0x00e04021, 0x11400007, 0x00072c02, 0x00aa3021, 0x00061402, 0x30c3ffff, - 0x00433021, 0x00061402, 0x00c22821, 0x00051027, 0xa522000a, 0x3c030800, - 0x946323c4, 0x3102ffff, 0x01e21021, 0x00433023, 0x00cd3021, 0x00061c02, - 0x30c2ffff, 0x00623021, 0x00061402, 0x00c23021, 0x00c04021, 0x00061027, - 0xa5820010, 0x3102ffff, 0x00051c00, 0x00431025, 0xadc20010, 0x3c020800, - 0x8c4223e4, 0x10400002, 0x25e2fff2, 0xa5c20034, 0x3c020800, 0x8c4223d8, - 0x3c040800, 0x8c8423d4, 0x24420001, 0x3c010800, 0xac2223d8, 0x3c020800, - 0x8c421bb0, 0x3303ffff, 0x00832021, 0x3c010800, 0xac2423d4, 0x00431821, - 0x0062102b, 0x10400003, 0x2482ffff, 0x3c010800, 0xac2223d4, 0x3c010800, - 0xac231bb0, 0x03e00008, 0x27bd0020, 0x27bdffb8, 0x3c050800, 0x24a51b86, - 0xafbf0044, 0xafbe0040, 0xafb7003c, 0xafb60038, 0xafb50034, 0xafb40030, - 0xafb3002c, 0xafb20028, 0xafb10024, 0xafb00020, 0x94a90000, 0x3c020800, - 0x944223c0, 0x3c030800, 0x8c631ba0, 0x3c040800, 0x8c841b9c, 0x01221023, - 0x0064182a, 0xa7a9001e, 0x106000bc, 0xa7a20016, 0x24be0022, 0x97b6001e, - 0x24b3001a, 0x24b70016, 0x8fc20000, 0x14400008, 0x00000000, 0x8fc2fff8, - 0x97a30016, 0x8fc4fff4, 0x00431021, 0x0082202a, 0x148000ae, 0x00000000, - 0x97d50818, 0x32a2ffff, 0x104000a1, 0x00009021, 0x0040a021, 0x00008821, - 0x0e000621, 0x00000000, 0x00403021, 0x14c00007, 0x00000000, 0x3c020800, - 0x8c4223cc, 0x24420001, 0x3c010800, 0x0a000593, 0xac2223cc, 0x3c100800, - 0x02118021, 0x8e101bb8, 0x9608000a, 0x31020040, 0x10400004, 0x2407180c, - 0x8e02000c, 0x2407188c, 0xacc20018, 0x31020080, 0x54400001, 0x34e70010, - 0x3c020800, 0x00511021, 0x8c421bc0, 0x3c030800, 0x00711821, 0x8c631bc4, - 0x00021500, 0x00031c00, 0x00431025, 0xacc20014, 0x96040008, 0x3242ffff, - 0x00821021, 0x0282102a, 0x14400002, 0x02b22823, 0x00802821, 0x8e020000, - 0x02459021, 0xacc20000, 0x8e020004, 0x00c02021, 0x26310010, 0xac820004, - 0x30e2ffff, 0xac800008, 0xa485000e, 0xac820010, 0x24020305, 0x0e00059f, - 0xa482000c, 0x3242ffff, 0x0054102b, 0x1440ffc6, 0x3242ffff, 0x0a00058b, - 0x00000000, 0x8e620000, 0x8e63fffc, 0x0043102a, 0x10400066, 0x00000000, - 0x8e62fff0, 0x00028900, 0x3c100800, 0x02118021, 0x0e000621, 0x8e101bb8, - 0x00403021, 0x14c00005, 0x00000000, 0x8e62082c, 0x24420001, 0x0a000593, - 0xae62082c, 0x9608000a, 0x31020040, 0x10400004, 0x2407180c, 0x8e02000c, - 0x2407188c, 0xacc20018, 0x3c020800, 0x00511021, 0x8c421bc0, 0x3c030800, - 0x00711821, 0x8c631bc4, 0x00021500, 0x00031c00, 0x00431025, 0xacc20014, - 0x8e63fff4, 0x96020008, 0x00432023, 0x3242ffff, 0x3083ffff, 0x00431021, - 0x02c2102a, 0x10400003, 0x00802821, 0x97a9001e, 0x01322823, 0x8e620000, - 0x30a4ffff, 0x00441021, 0xae620000, 0xa4c5000e, 0x8e020000, 0xacc20000, - 0x8e020004, 0x8e63fff4, 0x00431021, 0xacc20004, 0x8e63fff4, 0x96020008, - 0x00641821, 0x0062102a, 0x14400006, 0x02459021, 0x8e62fff0, 0xae60fff4, - 0x24420001, 0x0a00056e, 0xae62fff0, 0xae63fff4, 0xacc00008, 0x3242ffff, - 0x10560003, 0x31020004, 0x10400006, 0x24020305, 0x31020080, 0x54400001, - 0x34e70010, 0x34e70020, 0x24020905, 0xa4c2000c, 0x8ee30000, 0x8ee20004, - 0x14620007, 0x3c02b49a, 0x8ee20860, 0x54400001, 0x34e70400, 0x3c024b65, - 0x0a000585, 0x34427654, 0x344289ab, 0xacc2001c, 0x30e2ffff, 0xacc20010, - 0x0e00059f, 0x00c02021, 0x3242ffff, 0x0056102b, 0x1440ff9c, 0x00000000, - 0x8e620000, 0x8e63fffc, 0x0043102a, 0x1440ff4a, 0x00000000, 0x8fbf0044, - 0x8fbe0040, 0x8fb7003c, 0x8fb60038, 0x8fb50034, 0x8fb40030, 0x8fb3002c, - 0x8fb20028, 0x8fb10024, 0x8fb00020, 0x03e00008, 0x27bd0048, 0x27bdffe8, - 0xafbf0014, 0xafb00010, 0x8f624450, 0x8f634410, 0x0a0005ae, 0x00808021, - 0x8f626820, 0x30422000, 0x10400003, 0x00000000, 0x0e0001ef, 0x00002021, - 0x8f624450, 0x8f634410, 0x3042ffff, 0x0043102b, 0x1440fff5, 0x00000000, - 0x8f630c14, 0x3063000f, 0x2c620002, 0x1440000b, 0x00000000, 0x8f630c14, - 0x3c020800, 0x8c421b30, 0x3063000f, 0x24420001, 0x3c010800, 0xac221b30, - 0x2c620002, 0x1040fff7, 0x00000000, 0xaf705c18, 0x8f625c10, 0x30420002, - 0x10400009, 0x00000000, 0x8f626820, 0x30422000, 0x1040fff8, 0x00000000, - 0x0e0001ef, 0x00002021, 0x0a0005c1, 0x00000000, 0x8fbf0014, 0x8fb00010, - 0x03e00008, 0x27bd0018, 0x00000000, 0x00000000, 0x27bdffe8, 0x3c1bc000, - 0xafbf0014, 0xafb00010, 0xaf60680c, 0x8f626804, 0x34420082, 0xaf626804, - 0x8f634000, 0x24020b50, 0x3c010800, 0xac221b44, 0x24020b78, 0x3c010800, - 0xac221b54, 0x34630002, 0xaf634000, 0x0e000601, 0x00808021, 0x3c010800, - 0xa0221b58, 0x304200ff, 0x24030002, 0x14430005, 0x00000000, 0x3c020800, - 0x8c421b44, 0x0a0005f4, 0xac5000c0, 0x3c020800, 0x8c421b44, 0xac5000bc, - 0x8f624434, 0x8f634438, 0x8f644410, 0x3c010800, 0xac221b4c, 0x3c010800, - 0xac231b5c, 0x3c010800, 0xac241b48, 0x8fbf0014, 0x8fb00010, 0x03e00008, - 0x27bd0018, 0x3c040800, 0x8c870000, 0x3c03aa55, 0x3463aa55, 0x3c06c003, - 0xac830000, 0x8cc20000, 0x14430007, 0x24050002, 0x3c0355aa, 0x346355aa, - 0xac830000, 0x8cc20000, 0x50430001, 0x24050001, 0x3c020800, 0xac470000, - 0x03e00008, 0x00a01021, 0x27bdfff8, 0x18800009, 0x00002821, 0x8f63680c, - 0x8f62680c, 0x1043fffe, 0x00000000, 0x24a50001, 0x00a4102a, 0x1440fff9, - 0x00000000, 0x03e00008, 0x27bd0008, 0x8f634450, 0x3c020800, 0x8c421b4c, - 0x00031c02, 0x0043102b, 0x14400008, 0x3c038000, 0x3c040800, 0x8c841b5c, - 0x8f624450, 0x00021c02, 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, - 0x8f624444, 0x00431024, 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, - 0x3042ffff, 0x3082ffff, 0x2442e000, 0x2c422001, 0x14400003, 0x3c024000, - 0x0a000644, 0x2402ffff, 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, - 0x1440fffc, 0x00001021, 0x03e00008, 0x00000000, 0x8f624450, 0x3c030800, - 0x8c631b48, 0x0a00064d, 0x3042ffff, 0x8f624450, 0x3042ffff, 0x0043102b, - 0x1440fffc, 0x00000000, 0x03e00008, 0x00000000, 0x27bdffe0, 0x00802821, - 0x3c040800, 0x24841ae0, 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, - 0x0e000678, 0xafa00014, 0x0a00065c, 0x00000000, 0x8fbf0018, 0x03e00008, - 0x27bd0020, 0x00000000, 0x00000000, 0x00000000, 0x3c020800, 0x34423000, - 0x3c030800, 0x34633000, 0x3c040800, 0x348437ff, 0x3c010800, 0xac221b64, - 0x24020040, 0x3c010800, 0xac221b68, 0x3c010800, 0xac201b60, 0xac600000, - 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, - 0x00804821, 0x8faa0010, 0x3c020800, 0x8c421b60, 0x3c040800, 0x8c841b68, - 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010800, 0xac231b60, 0x14400003, - 0x00004021, 0x3c010800, 0xac201b60, 0x3c020800, 0x8c421b60, 0x3c030800, - 0x8c631b64, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001, - 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020800, 0x8c421b60, - 0x3c030800, 0x8c631b64, 0x8f64680c, 0x00021140, 0x00431021, 0xac440008, - 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, - 0x00000000, 0x00000000, + 0x00625824, 0x25670008, 0x0104102a, 0x1440fff3, 0x00000000, 0x3c020800, + 0x944223ec, 0x00c23021, 0x3122ffff, 0x00c23021, 0x00061c02, 0x30c2ffff, + 0x00623021, 0x00061402, 0x00c23021, 0x00c04021, 0x00061027, 0xa5820010, + 0xadc00014, 0x0a00049d, 0xadc00000, 0x8dc70010, 0x00e04021, 0x11400007, + 0x00072c02, 0x00aa3021, 0x00061402, 0x30c3ffff, 0x00433021, 0x00061402, + 0x00c22821, 0x00051027, 0xa522000a, 0x3c030800, 0x946323d4, 0x3102ffff, + 0x01e21021, 0x00433023, 0x00cd3021, 0x00061c02, 0x30c2ffff, 0x00623021, + 0x00061402, 0x00c23021, 0x00c04021, 0x00061027, 0xa5820010, 0x3102ffff, + 0x00051c00, 0x00431025, 0xadc20010, 0x3c020800, 0x8c4223f4, 0x10400005, + 0x2de205eb, 0x14400002, 0x25e2fff2, 0x34028870, 0xa5c20034, 0x3c030800, + 0x246323e8, 0x8c620000, 0x24420001, 0xac620000, 0x3c040800, 0x8c8423e4, + 0x3c020800, 0x8c421bc0, 0x3303ffff, 0x00832021, 0x00431821, 0x0062102b, + 0x3c010800, 0xac2423e4, 0x10400003, 0x2482ffff, 0x3c010800, 0xac2223e4, + 0x3c010800, 0xac231bc0, 0x03e00008, 0x27bd0020, 0x27bdffb8, 0x3c050800, + 0x24a51b96, 0xafbf0044, 0xafbe0040, 0xafb7003c, 0xafb60038, 0xafb50034, + 0xafb40030, 0xafb3002c, 0xafb20028, 0xafb10024, 0xafb00020, 0x94a90000, + 0x3c020800, 0x944223d0, 0x3c030800, 0x8c631bb0, 0x3c040800, 0x8c841bac, + 0x01221023, 0x0064182a, 0xa7a9001e, 0x106000be, 0xa7a20016, 0x24be0022, + 0x97b6001e, 0x24b3001a, 0x24b70016, 0x8fc20000, 0x14400008, 0x00000000, + 0x8fc2fff8, 0x97a30016, 0x8fc4fff4, 0x00431021, 0x0082202a, 0x148000b0, + 0x00000000, 0x97d50818, 0x32a2ffff, 0x104000a3, 0x00009021, 0x0040a021, + 0x00008821, 0x0e000625, 0x00000000, 0x00403021, 0x14c00007, 0x00000000, + 0x3c020800, 0x8c4223dc, 0x24420001, 0x3c010800, 0x0a000596, 0xac2223dc, + 0x3c100800, 0x02118021, 0x8e101bc8, 0x9608000a, 0x31020040, 0x10400005, + 0x2407180c, 0x8e02000c, 0x2407188c, 0x00021400, 0xacc20018, 0x31020080, + 0x54400001, 0x34e70010, 0x3c020800, 0x00511021, 0x8c421bd0, 0x3c030800, + 0x00711821, 0x8c631bd4, 0x00021500, 0x00031c00, 0x00431025, 0xacc20014, + 0x96040008, 0x3242ffff, 0x00821021, 0x0282102a, 0x14400002, 0x02b22823, + 0x00802821, 0x8e020000, 0x02459021, 0xacc20000, 0x8e020004, 0x00c02021, + 0x26310010, 0xac820004, 0x30e2ffff, 0xac800008, 0xa485000e, 0xac820010, + 0x24020305, 0x0e0005a2, 0xa482000c, 0x3242ffff, 0x0054102b, 0x1440ffc5, + 0x3242ffff, 0x0a00058e, 0x00000000, 0x8e620000, 0x8e63fffc, 0x0043102a, + 0x10400067, 0x00000000, 0x8e62fff0, 0x00028900, 0x3c100800, 0x02118021, + 0x0e000625, 0x8e101bc8, 0x00403021, 0x14c00005, 0x00000000, 0x8e62082c, + 0x24420001, 0x0a000596, 0xae62082c, 0x9608000a, 0x31020040, 0x10400005, + 0x2407180c, 0x8e02000c, 0x2407188c, 0x00021400, 0xacc20018, 0x3c020800, + 0x00511021, 0x8c421bd0, 0x3c030800, 0x00711821, 0x8c631bd4, 0x00021500, + 0x00031c00, 0x00431025, 0xacc20014, 0x8e63fff4, 0x96020008, 0x00432023, + 0x3242ffff, 0x3083ffff, 0x00431021, 0x02c2102a, 0x10400003, 0x00802821, + 0x97a9001e, 0x01322823, 0x8e620000, 0x30a4ffff, 0x00441021, 0xae620000, + 0xa4c5000e, 0x8e020000, 0xacc20000, 0x8e020004, 0x8e63fff4, 0x00431021, + 0xacc20004, 0x8e63fff4, 0x96020008, 0x00641821, 0x0062102a, 0x14400006, + 0x02459021, 0x8e62fff0, 0xae60fff4, 0x24420001, 0x0a000571, 0xae62fff0, + 0xae63fff4, 0xacc00008, 0x3242ffff, 0x10560003, 0x31020004, 0x10400006, + 0x24020305, 0x31020080, 0x54400001, 0x34e70010, 0x34e70020, 0x24020905, + 0xa4c2000c, 0x8ee30000, 0x8ee20004, 0x14620007, 0x3c02b49a, 0x8ee20860, + 0x54400001, 0x34e70400, 0x3c024b65, 0x0a000588, 0x34427654, 0x344289ab, + 0xacc2001c, 0x30e2ffff, 0xacc20010, 0x0e0005a2, 0x00c02021, 0x3242ffff, + 0x0056102b, 0x1440ff9b, 0x00000000, 0x8e620000, 0x8e63fffc, 0x0043102a, + 0x1440ff48, 0x00000000, 0x8fbf0044, 0x8fbe0040, 0x8fb7003c, 0x8fb60038, + 0x8fb50034, 0x8fb40030, 0x8fb3002c, 0x8fb20028, 0x8fb10024, 0x8fb00020, + 0x03e00008, 0x27bd0048, 0x27bdffe8, 0xafbf0014, 0xafb00010, 0x8f624450, + 0x8f634410, 0x0a0005b1, 0x00808021, 0x8f626820, 0x30422000, 0x10400003, + 0x00000000, 0x0e0001f0, 0x00002021, 0x8f624450, 0x8f634410, 0x3042ffff, + 0x0043102b, 0x1440fff5, 0x00000000, 0x8f630c14, 0x3063000f, 0x2c620002, + 0x1440000b, 0x00000000, 0x8f630c14, 0x3c020800, 0x8c421b40, 0x3063000f, + 0x24420001, 0x3c010800, 0xac221b40, 0x2c620002, 0x1040fff7, 0x00000000, + 0xaf705c18, 0x8f625c10, 0x30420002, 0x10400009, 0x00000000, 0x8f626820, + 0x30422000, 0x1040fff8, 0x00000000, 0x0e0001f0, 0x00002021, 0x0a0005c4, + 0x00000000, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x00000000, + 0x00000000, 0x00000000, 0x27bdffe8, 0x3c1bc000, 0xafbf0014, 0xafb00010, + 0xaf60680c, 0x8f626804, 0x34420082, 0xaf626804, 0x8f634000, 0x24020b50, + 0x3c010800, 0xac221b54, 0x24020b78, 0x3c010800, 0xac221b64, 0x34630002, + 0xaf634000, 0x0e000605, 0x00808021, 0x3c010800, 0xa0221b68, 0x304200ff, + 0x24030002, 0x14430005, 0x00000000, 0x3c020800, 0x8c421b54, 0x0a0005f8, + 0xac5000c0, 0x3c020800, 0x8c421b54, 0xac5000bc, 0x8f624434, 0x8f634438, + 0x8f644410, 0x3c010800, 0xac221b5c, 0x3c010800, 0xac231b6c, 0x3c010800, + 0xac241b58, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x3c040800, + 0x8c870000, 0x3c03aa55, 0x3463aa55, 0x3c06c003, 0xac830000, 0x8cc20000, + 0x14430007, 0x24050002, 0x3c0355aa, 0x346355aa, 0xac830000, 0x8cc20000, + 0x50430001, 0x24050001, 0x3c020800, 0xac470000, 0x03e00008, 0x00a01021, + 0x27bdfff8, 0x18800009, 0x00002821, 0x8f63680c, 0x8f62680c, 0x1043fffe, + 0x00000000, 0x24a50001, 0x00a4102a, 0x1440fff9, 0x00000000, 0x03e00008, + 0x27bd0008, 0x8f634450, 0x3c020800, 0x8c421b5c, 0x00031c02, 0x0043102b, + 0x14400008, 0x3c038000, 0x3c040800, 0x8c841b6c, 0x8f624450, 0x00021c02, + 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, 0x00431024, + 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, 0x3082ffff, + 0x2442e000, 0x2c422001, 0x14400003, 0x3c024000, 0x0a000648, 0x2402ffff, + 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, 0x00001021, + 0x03e00008, 0x00000000, 0x8f624450, 0x3c030800, 0x8c631b58, 0x0a000651, + 0x3042ffff, 0x8f624450, 0x3042ffff, 0x0043102b, 0x1440fffc, 0x00000000, + 0x03e00008, 0x00000000, 0x27bdffe0, 0x00802821, 0x3c040800, 0x24841af0, + 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, 0x0e00067c, 0xafa00014, + 0x0a000660, 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, + 0x00000000, 0x00000000, 0x3c020800, 0x34423000, 0x3c030800, 0x34633000, + 0x3c040800, 0x348437ff, 0x3c010800, 0xac221b74, 0x24020040, 0x3c010800, + 0xac221b78, 0x3c010800, 0xac201b70, 0xac600000, 0x24630004, 0x0083102b, + 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, 0x00804821, 0x8faa0010, + 0x3c020800, 0x8c421b70, 0x3c040800, 0x8c841b78, 0x8fab0014, 0x24430001, + 0x0044102b, 0x3c010800, 0xac231b70, 0x14400003, 0x00004021, 0x3c010800, + 0xac201b70, 0x3c020800, 0x8c421b70, 0x3c030800, 0x8c631b74, 0x91240000, + 0x00021140, 0x00431021, 0x00481021, 0x25080001, 0xa0440000, 0x29020008, + 0x1440fff4, 0x25290001, 0x3c020800, 0x8c421b70, 0x3c030800, 0x8c631b74, + 0x8f64680c, 0x00021140, 0x00431021, 0xac440008, 0xac45000c, 0xac460010, + 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, 0x00000000, 0x00000000, }; u32 tg3TsoFwRodata[] = { @@ -4134,201 +4402,200 @@ 0x00000000, 0x00000000, 0x73746b6f, 0x66666c64, 0x496e0000, 0x73746b6f, 0x66662a2a, 0x00000000, 0x53774576, 0x656e7430, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x66617461, 0x6c457272, 0x00000000, 0x00000000, + 0x00000000, }; -#if 0 /* All zeros, don't eat up space with it. */ u32 tg3TsoFwData[] = { + 0x00000000, 0x73746b6f, 0x66666c64, 0x5f76312e, 0x362e3000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000 + 0x00000000, }; -#endif /* 5705 needs a special version of the TSO firmware. */ #define TG3_TSO5_FW_RELEASE_MAJOR 0x1 -#define TG3_TSO5_FW_RELASE_MINOR 0x1 +#define TG3_TSO5_FW_RELASE_MINOR 0x2 #define TG3_TSO5_FW_RELEASE_FIX 0x0 #define TG3_TSO5_FW_START_ADDR 0x00010000 #define TG3_TSO5_FW_TEXT_ADDR 0x00010000 -#define TG3_TSO5_FW_TEXT_LEN 0xeb0 -#define TG3_TSO5_FW_RODATA_ADDR 0x00010eb0 +#define TG3_TSO5_FW_TEXT_LEN 0xe90 +#define TG3_TSO5_FW_RODATA_ADDR 0x00010e90 #define TG3_TSO5_FW_RODATA_LEN 0x50 -#define TG3_TSO5_FW_DATA_ADDR 0x00010f20 +#define TG3_TSO5_FW_DATA_ADDR 0x00010f00 #define TG3_TSO5_FW_DATA_LEN 0x20 -#define TG3_TSO5_FW_SBSS_ADDR 0x00010f40 +#define TG3_TSO5_FW_SBSS_ADDR 0x00010f20 #define TG3_TSO5_FW_SBSS_LEN 0x28 -#define TG3_TSO5_FW_BSS_ADDR 0x00010f70 +#define TG3_TSO5_FW_BSS_ADDR 0x00010f50 #define TG3_TSO5_FW_BSS_LEN 0x88 -static u32 tg3Tso5FwText[] = { - 0x0c004003, 0x00000000, 0x00010f30, 0x00000000, 0x10000003, 0x00000000, +static u32 tg3Tso5FwText[(TG3_TSO5_FW_TEXT_LEN / 4) + 1] = { + 0x0c004003, 0x00000000, 0x00010f04, 0x00000000, 0x10000003, 0x00000000, 0x0000000d, 0x0000000d, 0x3c1d0001, 0x37bde000, 0x03a0f021, 0x3c100001, 0x26100000, 0x0c004010, 0x00000000, 0x0000000d, 0x27bdffe0, 0x3c04fefe, - 0xafbf0018, 0x0c0042f0, 0x34840002, 0x0c00436c, 0x00000000, 0x3c030001, - 0x90630f54, 0x24020002, 0x3c040001, 0x24840ebc, 0x14620003, 0x24050001, - 0x3c040001, 0x24840eb0, 0x24060001, 0x00003821, 0xafa00010, 0x0c004380, + 0xafbf0018, 0x0c0042e8, 0x34840002, 0x0c004364, 0x00000000, 0x3c030001, + 0x90630f34, 0x24020002, 0x3c040001, 0x24840e9c, 0x14620003, 0x24050001, + 0x3c040001, 0x24840e90, 0x24060002, 0x00003821, 0xafa00010, 0x0c004378, 0xafa00014, 0x0c00402c, 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x00000000, 0x00000000, 0x27bdffe0, 0xafbf001c, 0xafb20018, 0xafb10014, - 0x0c0042d3, 0xafb00010, 0x3c128000, 0x24110001, 0x8f706810, 0x32020400, + 0x0c0042d4, 0xafb00010, 0x3c128000, 0x24110001, 0x8f706810, 0x32020400, 0x10400007, 0x00000000, 0x8f641008, 0x00921024, 0x14400003, 0x00000000, - 0x0c004064, 0x00000000, 0x3c020001, 0x90420f76, 0x10510003, 0x32020200, + 0x0c004064, 0x00000000, 0x3c020001, 0x90420f56, 0x10510003, 0x32020200, 0x1040fff1, 0x00000000, 0x0c0041b4, 0x00000000, 0x08004034, 0x00000000, 0x8fbf001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x03e00008, 0x27bd0020, - 0x27bdffe0, 0x3c040001, 0x24840ed0, 0x00002821, 0x00003021, 0x00003821, - 0xafbf0018, 0xafa00010, 0x0c004380, 0xafa00014, 0x0000d021, 0x24020130, - 0xaf625000, 0x3c010001, 0xa4200f70, 0x3c010001, 0xa0200f77, 0x8fbf0018, - 0x03e00008, 0x27bd0020, 0x00000000, 0x00000000, 0x3c030001, 0x24630f80, + 0x27bdffe0, 0x3c040001, 0x24840eb0, 0x00002821, 0x00003021, 0x00003821, + 0xafbf0018, 0xafa00010, 0x0c004378, 0xafa00014, 0x0000d021, 0x24020130, + 0xaf625000, 0x3c010001, 0xa4200f50, 0x3c010001, 0xa0200f57, 0x8fbf0018, + 0x03e00008, 0x27bd0020, 0x00000000, 0x00000000, 0x3c030001, 0x24630f60, 0x90620000, 0x27bdfff0, 0x14400003, 0x0080c021, 0x08004073, 0x00004821, 0x3c022000, 0x03021024, 0x10400003, 0x24090002, 0x08004073, 0xa0600000, 0x24090001, 0x00181040, 0x30431f80, 0x346f8008, 0x1520004b, 0x25eb0028, - 0x3c040001, 0x00832021, 0x8c848010, 0x3c050001, 0x24a50f9a, 0x00041402, - 0xa0a20000, 0x3c010001, 0xa0240f9b, 0x3c020001, 0x00431021, 0x94428014, - 0x3c010001, 0xa0220f9c, 0x3c0c0001, 0x01836021, 0x8d8c8018, 0x304200ff, - 0x24420008, 0x000220c3, 0x24020001, 0x3c010001, 0xa0220f80, 0x0124102b, + 0x3c040001, 0x00832021, 0x8c848010, 0x3c050001, 0x24a50f7a, 0x00041402, + 0xa0a20000, 0x3c010001, 0xa0240f7b, 0x3c020001, 0x00431021, 0x94428014, + 0x3c010001, 0xa0220f7c, 0x3c0c0001, 0x01836021, 0x8d8c8018, 0x304200ff, + 0x24420008, 0x000220c3, 0x24020001, 0x3c010001, 0xa0220f60, 0x0124102b, 0x1040000c, 0x00003821, 0x24a6000e, 0x01602821, 0x8ca20000, 0x8ca30004, 0x24a50008, 0x24e70001, 0xacc20000, 0xacc30004, 0x00e4102b, 0x1440fff8, - 0x24c60008, 0x00003821, 0x3c080001, 0x25080f9b, 0x91060000, 0x3c020001, - 0x90420f9c, 0x2503000d, 0x00c32821, 0x00461023, 0x00021fc2, 0x00431021, + 0x24c60008, 0x00003821, 0x3c080001, 0x25080f7b, 0x91060000, 0x3c020001, + 0x90420f7c, 0x2503000d, 0x00c32821, 0x00461023, 0x00021fc2, 0x00431021, 0x00021043, 0x1840000c, 0x00002021, 0x91020001, 0x00461023, 0x00021fc2, 0x00431021, 0x00021843, 0x94a20000, 0x24e70001, 0x00822021, 0x00e3102a, 0x1440fffb, 0x24a50002, 0x00041c02, 0x3082ffff, 0x00622021, 0x00041402, 0x00822021, 0x3c02ffff, 0x01821024, 0x3083ffff, 0x00431025, 0x3c010001, - 0x080040fa, 0xac220fa0, 0x3c050001, 0x24a50f9c, 0x90a20000, 0x3c0c0001, + 0x080040fa, 0xac220f80, 0x3c050001, 0x24a50f7c, 0x90a20000, 0x3c0c0001, 0x01836021, 0x8d8c8018, 0x000220c2, 0x1080000e, 0x00003821, 0x01603021, 0x24a5000c, 0x8ca20000, 0x8ca30004, 0x24a50008, 0x24e70001, 0xacc20000, - 0xacc30004, 0x00e4102b, 0x1440fff8, 0x24c60008, 0x3c050001, 0x24a50f9c, + 0xacc30004, 0x00e4102b, 0x1440fff8, 0x24c60008, 0x3c050001, 0x24a50f7c, 0x90a20000, 0x30430007, 0x24020004, 0x10620011, 0x28620005, 0x10400005, 0x24020002, 0x10620008, 0x000710c0, 0x080040fa, 0x00000000, 0x24020006, 0x1062000e, 0x000710c0, 0x080040fa, 0x00000000, 0x00a21821, 0x9463000c, 0x004b1021, 0x080040fa, 0xa4430000, 0x000710c0, 0x00a21821, 0x8c63000c, 0x004b1021, 0x080040fa, 0xac430000, 0x00a21821, 0x8c63000c, 0x004b2021, 0x00a21021, 0xac830000, 0x94420010, 0xa4820004, 0x95e70006, 0x3c020001, - 0x90420f9c, 0x3c030001, 0x90630f9a, 0x00e2c823, 0x3c020001, 0x90420f9b, + 0x90420f7c, 0x3c030001, 0x90630f7a, 0x00e2c823, 0x3c020001, 0x90420f7b, 0x24630028, 0x01e34021, 0x24420028, 0x15200012, 0x01e23021, 0x94c2000c, - 0x3c010001, 0xa4220f98, 0x94c20004, 0x94c30006, 0x3c010001, 0xa4200f96, - 0x3c010001, 0xa4200f92, 0x00021400, 0x00431025, 0x3c010001, 0xac220f8c, - 0x95020004, 0x3c010001, 0x08004124, 0xa4220f90, 0x3c020001, 0x94420f90, - 0x3c030001, 0x94630f92, 0x00431021, 0xa5020004, 0x3c020001, 0x94420f8c, - 0xa4c20004, 0x3c020001, 0x8c420f8c, 0xa4c20006, 0x3c040001, 0x94840f92, - 0x3c020001, 0x94420f90, 0x3c0a0001, 0x954a0f96, 0x00441821, 0x3063ffff, - 0x0062182a, 0x24020002, 0x1122000b, 0x00832023, 0x3c030001, 0x94630f98, - 0x30620009, 0x10400006, 0x3062fff6, 0xa4c2000c, 0x3c020001, 0x94420f98, + 0x3c010001, 0xa4220f78, 0x94c20004, 0x94c30006, 0x3c010001, 0xa4200f76, + 0x3c010001, 0xa4200f72, 0x00021400, 0x00431025, 0x3c010001, 0xac220f6c, + 0x95020004, 0x3c010001, 0x08004124, 0xa4220f70, 0x3c020001, 0x94420f70, + 0x3c030001, 0x94630f72, 0x00431021, 0xa5020004, 0x3c020001, 0x94420f6c, + 0xa4c20004, 0x3c020001, 0x8c420f6c, 0xa4c20006, 0x3c040001, 0x94840f72, + 0x3c020001, 0x94420f70, 0x3c0a0001, 0x954a0f76, 0x00441821, 0x3063ffff, + 0x0062182a, 0x24020002, 0x1122000b, 0x00832023, 0x3c030001, 0x94630f78, + 0x30620009, 0x10400006, 0x3062fff6, 0xa4c2000c, 0x3c020001, 0x94420f78, 0x30420009, 0x01425023, 0x24020001, 0x1122001b, 0x29220002, 0x50400005, 0x24020002, 0x11200007, 0x31a2ffff, 0x08004197, 0x00000000, 0x1122001d, - 0x24020016, 0x08004197, 0x31a2ffff, 0x3c0e0001, 0x95ce0fa0, 0x10800005, + 0x24020016, 0x08004197, 0x31a2ffff, 0x3c0e0001, 0x95ce0f80, 0x10800005, 0x01806821, 0x01c42021, 0x00041c02, 0x3082ffff, 0x00627021, 0x000e1027, - 0xa502000a, 0x3c030001, 0x90630f9b, 0x31a2ffff, 0x00e21021, 0x0800418d, - 0x00432023, 0x3c020001, 0x94420fa0, 0x00442021, 0x00041c02, 0x3082ffff, + 0xa502000a, 0x3c030001, 0x90630f7b, 0x31a2ffff, 0x00e21021, 0x0800418d, + 0x00432023, 0x3c020001, 0x94420f80, 0x00442021, 0x00041c02, 0x3082ffff, 0x00622021, 0x00807021, 0x00041027, 0x08004185, 0xa502000a, 0x3c050001, - 0x24a50f9a, 0x90a30000, 0x14620002, 0x24e2fff2, 0xa5e20034, 0x90a20000, - 0x00e21023, 0xa5020002, 0x3c030001, 0x94630fa0, 0x3c020001, 0x94420f7a, + 0x24a50f7a, 0x90a30000, 0x14620002, 0x24e2fff2, 0xa5e20034, 0x90a20000, + 0x00e21023, 0xa5020002, 0x3c030001, 0x94630f80, 0x3c020001, 0x94420f5a, 0x30e5ffff, 0x00641821, 0x00451023, 0x00622023, 0x00041c02, 0x3082ffff, - 0x00622021, 0x00041027, 0xa502000a, 0x3c030001, 0x90630f9c, 0x24620001, + 0x00622021, 0x00041027, 0xa502000a, 0x3c030001, 0x90630f7c, 0x24620001, 0x14a20005, 0x00807021, 0x01631021, 0x90420000, 0x08004185, 0x00026200, 0x24620002, 0x14a20003, 0x306200fe, 0x004b1021, 0x944c0000, 0x3c020001, - 0x94420fa2, 0x3183ffff, 0x3c040001, 0x90840f9b, 0x00431021, 0x00e21021, + 0x94420f82, 0x3183ffff, 0x3c040001, 0x90840f7b, 0x00431021, 0x00e21021, 0x00442023, 0x008a2021, 0x00041c02, 0x3082ffff, 0x00622021, 0x00041402, 0x00822021, 0x00806821, 0x00041027, 0xa4c20010, 0x31a2ffff, 0x000e1c00, - 0x00431025, 0x3c040001, 0x24840f92, 0xade20010, 0x94820000, 0x3c050001, - 0x94a50f96, 0x3c030001, 0x8c630f8c, 0x24420001, 0x00b92821, 0xa4820000, - 0x3322ffff, 0x00622021, 0x0083182b, 0x3c010001, 0xa4250f96, 0x10600003, - 0x24a2ffff, 0x3c010001, 0xa4220f96, 0x3c024000, 0x03021025, 0x3c010001, - 0xac240f8c, 0xaf621008, 0x03e00008, 0x27bd0010, 0x3c030001, 0x90630f76, + 0x00431025, 0x3c040001, 0x24840f72, 0xade20010, 0x94820000, 0x3c050001, + 0x94a50f76, 0x3c030001, 0x8c630f6c, 0x24420001, 0x00b92821, 0xa4820000, + 0x3322ffff, 0x00622021, 0x0083182b, 0x3c010001, 0xa4250f76, 0x10600003, + 0x24a2ffff, 0x3c010001, 0xa4220f76, 0x3c024000, 0x03021025, 0x3c010001, + 0xac240f6c, 0xaf621008, 0x03e00008, 0x27bd0010, 0x3c030001, 0x90630f56, 0x27bdffe8, 0x24020001, 0xafbf0014, 0x10620026, 0xafb00010, 0x8f620cf4, - 0x2442ffff, 0x3042007f, 0x00021100, 0x8c434000, 0x3c010001, 0xac230f84, + 0x2442ffff, 0x3042007f, 0x00021100, 0x8c434000, 0x3c010001, 0xac230f64, 0x8c434008, 0x24444000, 0x8c5c4004, 0x30620040, 0x14400002, 0x24020088, - 0x24020008, 0x3c010001, 0xa4220f88, 0x30620004, 0x10400005, 0x24020001, - 0x3c010001, 0xa0220f77, 0x080041d5, 0x00031402, 0x3c010001, 0xa0200f77, - 0x00031402, 0x3c010001, 0xa4220f74, 0x9483000c, 0x24020001, 0x3c010001, - 0xa4200f70, 0x3c010001, 0xa0220f76, 0x3c010001, 0xa4230f82, 0x24020001, + 0x24020008, 0x3c010001, 0xa4220f68, 0x30620004, 0x10400005, 0x24020001, + 0x3c010001, 0xa0220f57, 0x080041d5, 0x00031402, 0x3c010001, 0xa0200f57, + 0x00031402, 0x3c010001, 0xa4220f54, 0x9483000c, 0x24020001, 0x3c010001, + 0xa4200f50, 0x3c010001, 0xa0220f56, 0x3c010001, 0xa4230f62, 0x24020001, 0x1342001e, 0x00000000, 0x13400005, 0x24020003, 0x13420067, 0x00000000, - 0x080042cf, 0x00000000, 0x3c020001, 0x94420f82, 0x241a0001, 0x3c010001, - 0xa4200f7e, 0x3c010001, 0xa4200f72, 0x304407ff, 0x00021bc2, 0x00031823, + 0x080042cf, 0x00000000, 0x3c020001, 0x94420f62, 0x241a0001, 0x3c010001, + 0xa4200f5e, 0x3c010001, 0xa4200f52, 0x304407ff, 0x00021bc2, 0x00031823, 0x3063003e, 0x34630036, 0x00021242, 0x3042003c, 0x00621821, 0x3c010001, - 0xa4240f78, 0x00832021, 0x24630030, 0x3c010001, 0xa4240f7a, 0x3c010001, - 0xa4230f7c, 0x3c060001, 0x24c60f72, 0x94c50000, 0x94c30002, 0x3c040001, - 0x94840f7a, 0x00651021, 0x0044102a, 0x10400013, 0x3c108000, 0x00a31021, - 0xa4c20000, 0x3c02a000, 0xaf620cf4, 0x3c010001, 0xa0200f76, 0x8f641008, + 0xa4240f58, 0x00832021, 0x24630030, 0x3c010001, 0xa4240f5a, 0x3c010001, + 0xa4230f5c, 0x3c060001, 0x24c60f52, 0x94c50000, 0x94c30002, 0x3c040001, + 0x94840f5a, 0x00651021, 0x0044102a, 0x10400013, 0x3c108000, 0x00a31021, + 0xa4c20000, 0x3c02a000, 0xaf620cf4, 0x3c010001, 0xa0200f56, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x104000b7, 0x00000000, 0x0800420f, 0x00000000, 0x3c030001, - 0x94630f70, 0x00851023, 0xa4c40000, 0x00621821, 0x3042ffff, 0x3c010001, - 0xa4230f70, 0xaf620ce8, 0x3c020001, 0x94420f88, 0x34420024, 0xaf620cec, - 0x94c30002, 0x3c020001, 0x94420f70, 0x14620012, 0x3c028000, 0x3c108000, - 0x3c02a000, 0xaf620cf4, 0x3c010001, 0xa0200f76, 0x8f641008, 0x00901024, + 0x94630f50, 0x00851023, 0xa4c40000, 0x00621821, 0x3042ffff, 0x3c010001, + 0xa4230f50, 0xaf620ce8, 0x3c020001, 0x94420f68, 0x34420024, 0xaf620cec, + 0x94c30002, 0x3c020001, 0x94420f50, 0x14620012, 0x3c028000, 0x3c108000, + 0x3c02a000, 0xaf620cf4, 0x3c010001, 0xa0200f56, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x1440fff7, 0x00000000, 0x080042cf, 0x241a0003, 0xaf620cf4, 0x3c108000, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x1440fff7, 0x00000000, 0x080042cf, 0x241a0003, - 0x3c070001, 0x24e70f70, 0x94e20000, 0x03821021, 0xaf620ce0, 0x3c020001, - 0x8c420f84, 0xaf620ce4, 0x3c050001, 0x94a50f74, 0x94e30000, 0x3c040001, - 0x94840f78, 0x3c020001, 0x94420f7e, 0x00a32823, 0x00822023, 0x30a6ffff, - 0x3083ffff, 0x00c3102b, 0x14400043, 0x00000000, 0x3c020001, 0x94420f7c, - 0x00021400, 0x00621025, 0xaf620ce8, 0x94e20000, 0x3c030001, 0x94630f74, + 0x3c070001, 0x24e70f50, 0x94e20000, 0x03821021, 0xaf620ce0, 0x3c020001, + 0x8c420f64, 0xaf620ce4, 0x3c050001, 0x94a50f54, 0x94e30000, 0x3c040001, + 0x94840f58, 0x3c020001, 0x94420f5e, 0x00a32823, 0x00822023, 0x30a6ffff, + 0x3083ffff, 0x00c3102b, 0x14400043, 0x00000000, 0x3c020001, 0x94420f5c, + 0x00021400, 0x00621025, 0xaf620ce8, 0x94e20000, 0x3c030001, 0x94630f54, 0x00441021, 0xa4e20000, 0x3042ffff, 0x14430021, 0x3c020008, 0x3c020001, - 0x90420f77, 0x10400006, 0x3c03000c, 0x3c020001, 0x94420f88, 0x34630624, - 0x0800427c, 0x0000d021, 0x3c020001, 0x94420f88, 0x3c030008, 0x34630624, + 0x90420f57, 0x10400006, 0x3c03000c, 0x3c020001, 0x94420f68, 0x34630624, + 0x0800427c, 0x0000d021, 0x3c020001, 0x94420f68, 0x3c030008, 0x34630624, 0x00431025, 0xaf620cec, 0x3c108000, 0x3c02a000, 0xaf620cf4, 0x3c010001, - 0xa0200f76, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, + 0xa0200f56, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x10400015, 0x00000000, 0x08004283, - 0x00000000, 0x3c030001, 0x94630f88, 0x34420624, 0x3c108000, 0x00621825, + 0x00000000, 0x3c030001, 0x94630f68, 0x34420624, 0x3c108000, 0x00621825, 0x3c028000, 0xaf630cec, 0xaf620cf4, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x1440fff7, - 0x00000000, 0x3c010001, 0x080042cf, 0xa4200f7e, 0x3c020001, 0x94420f7c, - 0x00021400, 0x00c21025, 0xaf620ce8, 0x3c020001, 0x90420f77, 0x10400009, - 0x3c03000c, 0x3c020001, 0x94420f88, 0x34630624, 0x0000d021, 0x00431025, - 0xaf620cec, 0x080042c1, 0x3c108000, 0x3c020001, 0x94420f88, 0x3c030008, - 0x34630604, 0x00431025, 0xaf620cec, 0x3c020001, 0x94420f7e, 0x00451021, - 0x3c010001, 0xa4220f7e, 0x3c108000, 0x3c02a000, 0xaf620cf4, 0x3c010001, - 0xa0200f76, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, + 0x00000000, 0x3c010001, 0x080042cf, 0xa4200f5e, 0x3c020001, 0x94420f5c, + 0x00021400, 0x00c21025, 0xaf620ce8, 0x3c020001, 0x90420f57, 0x10400009, + 0x3c03000c, 0x3c020001, 0x94420f68, 0x34630624, 0x0000d021, 0x00431025, + 0xaf620cec, 0x080042c1, 0x3c108000, 0x3c020001, 0x94420f68, 0x3c030008, + 0x34630604, 0x00431025, 0xaf620cec, 0x3c020001, 0x94420f5e, 0x00451021, + 0x3c010001, 0xa4220f5e, 0x3c108000, 0x3c02a000, 0xaf620cf4, 0x3c010001, + 0xa0200f56, 0x8f641008, 0x00901024, 0x14400003, 0x00000000, 0x0c004064, 0x00000000, 0x8f620cf4, 0x00501024, 0x1440fff7, 0x00000000, 0x8fbf0014, - 0x8fb00010, 0x03e00008, 0x27bd0018, 0x27bdffe0, 0x3c040001, 0x24840ee0, - 0x00002821, 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, 0x0c004380, - 0xafa00014, 0x0000d021, 0x24020130, 0xaf625000, 0x3c010001, 0xa4200f70, - 0x3c010001, 0xa0200f77, 0x8f636804, 0x3c020001, 0x3442e000, 0x00621824, - 0x3c020001, 0x14620003, 0x00000000, 0x080042eb, 0x00000000, 0x8fbf0018, - 0x03e00008, 0x27bd0020, 0x27bdffe8, 0x3c1bc000, 0xafbf0014, 0xafb00010, - 0xaf60680c, 0x8f626804, 0x34420082, 0xaf626804, 0x8f634000, 0x24020b50, - 0x3c010001, 0xac220f40, 0x24020b78, 0x3c010001, 0xac220f50, 0x34630002, - 0xaf634000, 0x0c00431d, 0x00808021, 0x3c010001, 0xa0220f54, 0x304200ff, - 0x24030002, 0x14430005, 0x00000000, 0x3c020001, 0x8c420f40, 0x08004310, - 0xac5000c0, 0x3c020001, 0x8c420f40, 0xac5000bc, 0x8f624434, 0x8f634438, - 0x8f644410, 0x3c010001, 0xac220f48, 0x3c010001, 0xac230f58, 0x3c010001, - 0xac240f44, 0x8fbf0014, 0x8fb00010, 0x03e00008, 0x27bd0018, 0x03e00008, - 0x24020001, 0x27bdfff8, 0x18800009, 0x00002821, 0x8f63680c, 0x8f62680c, - 0x1043fffe, 0x00000000, 0x24a50001, 0x00a4102a, 0x1440fff9, 0x00000000, - 0x03e00008, 0x27bd0008, 0x8f634450, 0x3c020001, 0x8c420f48, 0x00031c02, - 0x0043102b, 0x14400008, 0x3c038000, 0x3c040001, 0x8c840f58, 0x8f624450, - 0x00021c02, 0x0083102b, 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, - 0x00431024, 0x1440fffd, 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, - 0x3082ffff, 0x2442e000, 0x2c422001, 0x14400003, 0x3c024000, 0x0800434f, - 0x2402ffff, 0x00822025, 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, - 0x00001021, 0x03e00008, 0x00000000, 0x8f624450, 0x3c030001, 0x8c630f44, - 0x08004358, 0x3042ffff, 0x8f624450, 0x3042ffff, 0x0043102b, 0x1440fffc, - 0x00000000, 0x03e00008, 0x00000000, 0x27bdffe0, 0x00802821, 0x3c040001, - 0x24840ef0, 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, 0x0c004380, - 0xafa00014, 0x08004367, 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, - 0x3c020001, 0x3442d600, 0x3c030001, 0x3463d600, 0x3c040001, 0x3484ddff, - 0x3c010001, 0xac220f60, 0x24020040, 0x3c010001, 0xac220f64, 0x3c010001, - 0xac200f5c, 0xac600000, 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, - 0x03e00008, 0x00000000, 0x00804821, 0x8faa0010, 0x3c020001, 0x8c420f5c, - 0x3c040001, 0x8c840f64, 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010001, - 0xac230f5c, 0x14400003, 0x00004021, 0x3c010001, 0xac200f5c, 0x3c020001, - 0x8c420f5c, 0x3c030001, 0x8c630f60, 0x91240000, 0x00021140, 0x00431021, - 0x00481021, 0x25080001, 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, - 0x3c020001, 0x8c420f5c, 0x3c030001, 0x8c630f60, 0x8f64680c, 0x00021140, - 0x00431021, 0xac440008, 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, - 0x03e00008, 0xac4b001c, 0x00000000, 0x00000000, + 0x8fb00010, 0x03e00008, 0x27bd0018, 0x00000000, 0x27bdffe0, 0x3c040001, + 0x24840ec0, 0x00002821, 0x00003021, 0x00003821, 0xafbf0018, 0xafa00010, + 0x0c004378, 0xafa00014, 0x0000d021, 0x24020130, 0xaf625000, 0x3c010001, + 0xa4200f50, 0x3c010001, 0xa0200f57, 0x8fbf0018, 0x03e00008, 0x27bd0020, + 0x27bdffe8, 0x3c1bc000, 0xafbf0014, 0xafb00010, 0xaf60680c, 0x8f626804, + 0x34420082, 0xaf626804, 0x8f634000, 0x24020b50, 0x3c010001, 0xac220f20, + 0x24020b78, 0x3c010001, 0xac220f30, 0x34630002, 0xaf634000, 0x0c004315, + 0x00808021, 0x3c010001, 0xa0220f34, 0x304200ff, 0x24030002, 0x14430005, + 0x00000000, 0x3c020001, 0x8c420f20, 0x08004308, 0xac5000c0, 0x3c020001, + 0x8c420f20, 0xac5000bc, 0x8f624434, 0x8f634438, 0x8f644410, 0x3c010001, + 0xac220f28, 0x3c010001, 0xac230f38, 0x3c010001, 0xac240f24, 0x8fbf0014, + 0x8fb00010, 0x03e00008, 0x27bd0018, 0x03e00008, 0x24020001, 0x27bdfff8, + 0x18800009, 0x00002821, 0x8f63680c, 0x8f62680c, 0x1043fffe, 0x00000000, + 0x24a50001, 0x00a4102a, 0x1440fff9, 0x00000000, 0x03e00008, 0x27bd0008, + 0x8f634450, 0x3c020001, 0x8c420f28, 0x00031c02, 0x0043102b, 0x14400008, + 0x3c038000, 0x3c040001, 0x8c840f38, 0x8f624450, 0x00021c02, 0x0083102b, + 0x1040fffc, 0x3c038000, 0xaf634444, 0x8f624444, 0x00431024, 0x1440fffd, + 0x00000000, 0x8f624448, 0x03e00008, 0x3042ffff, 0x3082ffff, 0x2442e000, + 0x2c422001, 0x14400003, 0x3c024000, 0x08004347, 0x2402ffff, 0x00822025, + 0xaf645c38, 0x8f625c30, 0x30420002, 0x1440fffc, 0x00001021, 0x03e00008, + 0x00000000, 0x8f624450, 0x3c030001, 0x8c630f24, 0x08004350, 0x3042ffff, + 0x8f624450, 0x3042ffff, 0x0043102b, 0x1440fffc, 0x00000000, 0x03e00008, + 0x00000000, 0x27bdffe0, 0x00802821, 0x3c040001, 0x24840ed0, 0x00003021, + 0x00003821, 0xafbf0018, 0xafa00010, 0x0c004378, 0xafa00014, 0x0800435f, + 0x00000000, 0x8fbf0018, 0x03e00008, 0x27bd0020, 0x3c020001, 0x3442d600, + 0x3c030001, 0x3463d600, 0x3c040001, 0x3484ddff, 0x3c010001, 0xac220f40, + 0x24020040, 0x3c010001, 0xac220f44, 0x3c010001, 0xac200f3c, 0xac600000, + 0x24630004, 0x0083102b, 0x5040fffd, 0xac600000, 0x03e00008, 0x00000000, + 0x00804821, 0x8faa0010, 0x3c020001, 0x8c420f3c, 0x3c040001, 0x8c840f44, + 0x8fab0014, 0x24430001, 0x0044102b, 0x3c010001, 0xac230f3c, 0x14400003, + 0x00004021, 0x3c010001, 0xac200f3c, 0x3c020001, 0x8c420f3c, 0x3c030001, + 0x8c630f40, 0x91240000, 0x00021140, 0x00431021, 0x00481021, 0x25080001, + 0xa0440000, 0x29020008, 0x1440fff4, 0x25290001, 0x3c020001, 0x8c420f3c, + 0x3c030001, 0x8c630f40, 0x8f64680c, 0x00021140, 0x00431021, 0xac440008, + 0xac45000c, 0xac460010, 0xac470014, 0xac4a0018, 0x03e00008, 0xac4b001c, + 0x00000000, 0x00000000, 0x00000000, }; -u32 tg3Tso5FwRodata[] = { +u32 tg3Tso5FwRodata[(TG3_TSO5_FW_RODATA_LEN / 4) + 1] = { 0x4d61696e, 0x43707542, 0x00000000, 0x4d61696e, 0x43707541, 0x00000000, 0x00000000, 0x00000000, 0x73746b6f, 0x66666c64, 0x00000000, 0x00000000, 0x73746b6f, 0x66666c64, 0x00000000, 0x00000000, 0x66617461, 0x6c457272, - 0x00000000, 0x00000000, 0x00000000 + 0x00000000, 0x00000000, 0x00000000, }; -u32 tg3Tso5FwData[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x73746b6f, - 0x66666c64, 0x5f76312e, 0x312e3000, 0x00000000 +u32 tg3Tso5FwData[(TG3_TSO5_FW_DATA_LEN / 4) + 1] = { + 0x00000000, 0x73746b6f, 0x66666c64, 0x5f76312e, 0x322e3000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, }; /* tp->lock is held. */ @@ -4338,6 +4605,9 @@ unsigned long cpu_base, cpu_scratch_base, cpu_scratch_size; int err, i; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + return 0; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { info.text_base = TG3_TSO5_FW_TEXT_ADDR; info.text_len = TG3_TSO5_FW_TEXT_LEN; @@ -4364,7 +4634,7 @@ info.rodata_data = &tg3TsoFwRodata[0]; info.data_base = TG3_TSO_FW_DATA_ADDR; info.data_len = TG3_TSO_FW_DATA_LEN; - info.data_data = NULL; + info.data_data = &tg3TsoFwData[0]; cpu_base = TX_CPU_BASE; cpu_scratch_base = TX_CPU_SCRATCH_BASE; cpu_scratch_size = TX_CPU_SCRATCH_SIZE; @@ -4440,7 +4710,7 @@ static int tg3_set_mac_addr(struct net_device *dev, void *p) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); struct sockaddr *addr = p; memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); @@ -4485,55 +4755,27 @@ tg3_stop_fw(tp); + tg3_write_sig_pre_reset(tp, RESET_KIND_INIT); + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) { err = tg3_abort_hw(tp); if (err) return err; } - tg3_chip_reset(tp); - - val = tr32(GRC_MODE); - val &= GRC_MODE_HOST_STACKUP; - tw32(GRC_MODE, val | tp->grc_mode); - - tg3_write_mem(tp, - NIC_SRAM_FIRMWARE_MBOX, - NIC_SRAM_FIRMWARE_MBOX_MAGIC1); - if (tp->phy_id == PHY_ID_SERDES) { - tp->mac_mode = MAC_MODE_PORT_MODE_TBI; - tw32_f(MAC_MODE, tp->mac_mode); - } else - tw32_f(MAC_MODE, 0); - udelay(40); - - /* Wait for firmware initialization to complete. */ - for (i = 0; i < 100000; i++) { - tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val); - if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1) - break; - udelay(10); - } - if (i >= 100000 && - !(tp->tg3_flags2 & TG3_FLG2_SUN_5704)) { - printk(KERN_ERR PFX "tg3_reset_hw timed out for %s, " - "firmware will not restart magic=%08x\n", - tp->dev->name, val); - return -ENODEV; - } + err = tg3_chip_reset(tp); + if (err) + return err; - if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) - tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, - DRV_STATE_START); - else - tg3_write_mem(tp, NIC_SRAM_FW_DRV_STATE_MBOX, - DRV_STATE_SUSPEND); + tg3_write_sig_legacy(tp, RESET_KIND_INIT); /* This works around an issue with Athlon chipsets on * B3 tigon3 silicon. This bit has no effect on any - * other revision. + * other revision. But do not set this on PCI Express + * chips. */ - tp->pci_clock_ctrl |= CLOCK_CTRL_DELAY_PCI_GRANT; + if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) + tp->pci_clock_ctrl |= CLOCK_CTRL_DELAY_PCI_GRANT; tw32_f(TG3PCI_CLOCK_CTRL, tp->pci_clock_ctrl); if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0 && @@ -4543,6 +4785,13 @@ tw32(TG3PCI_PCISTATE, val); } + if (GET_CHIP_REV(tp->pci_chip_rev_id) == CHIPREV_5704_BX) { + /* Enable some hw fixes. */ + val = tr32(TG3PCI_MSI_DATA); + val |= (1 << 26) | (1 << 28) | (1 << 29); + tw32(TG3PCI_MSI_DATA, val); + } + /* Descriptor ring init may make accesses to the * NIC SRAM area to setup the TX descriptors, so we * can only do this after the hardware has been @@ -4573,11 +4822,15 @@ (GRC_MODE_IRQ_ON_MAC_ATTN | GRC_MODE_HOST_STACKUP)); /* Setup the timer prescalar register. Clock is always 66Mhz. */ - tw32(GRC_MISC_CFG, - (65 << GRC_MISC_CFG_PRESCALAR_SHIFT)); + val = tr32(GRC_MISC_CFG); + val &= ~0xff; + val |= (65 << GRC_MISC_CFG_PRESCALAR_SHIFT); + tw32(GRC_MISC_CFG, val); /* Initialize MBUF/DESC pool. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + /* Do nothing. */ + } else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { tw32(BUFMGR_MB_POOL_ADDR, NIC_SRAM_MBUF_POOL_BASE); if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) tw32(BUFMGR_MB_POOL_SIZE, NIC_SRAM_MBUF_POOL_SIZE64); @@ -4635,30 +4888,6 @@ return -ENODEV; } - tw32(FTQ_RESET, 0xffffffff); - tw32(FTQ_RESET, 0x00000000); - for (i = 0; i < 2000; i++) { - if (tr32(FTQ_RESET) == 0x00000000) - break; - udelay(10); - } - if (i >= 2000) { - printk(KERN_ERR PFX "tg3_reset_hw cannot reset FTQ for %s.\n", - tp->dev->name); - return -ENODEV; - } - - /* Clear statistics/status block in chip, and status block in ram. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { - for (i = NIC_SRAM_STATS_BLK; - i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE; - i += sizeof(u32)) { - tg3_write_mem(tp, i, 0); - udelay(40); - } - } - memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); - /* Setup replenish threshold. */ tw32(RCVBDI_STD_THRESH, tp->rx_pending / 8); @@ -4689,7 +4918,8 @@ /* Don't even try to program the JUMBO/MINI buffer descriptor * configs on 5705. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { tw32(RCVDBDI_STD_BD + TG3_BDINFO_MAXLEN_FLAGS, RX_STD_MAX_SIZE_5705 << BDINFO_FLAGS_MAXLEN_SHIFT); } else { @@ -4718,10 +4948,11 @@ } - /* There is only one send ring on 5705, no need to explicitly + /* There is only one send ring on 5705/5750, no need to explicitly * disable the others. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { /* Clear out send RCB ring in SRAM. */ for (i = NIC_SRAM_SEND_RCB; i < NIC_SRAM_RCV_RET_RCB; i += TG3_BDINFO_SIZE) tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, @@ -4746,10 +4977,11 @@ NIC_SRAM_TX_BUFFER_DESC); } - /* There is only one receive return ring on 5705, no need to explicitly - * disable the others. + /* There is only one receive return ring on 5705/5750, no need + * to explicitly disable the others. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { for (i = NIC_SRAM_RCV_RET_RCB; i < NIC_SRAM_STATS_BLK; i += TG3_BDINFO_SIZE) { tg3_write_mem(tp, i + TG3_BDINFO_MAXLEN_FLAGS, @@ -4803,17 +5035,24 @@ RDMAC_MODE_LNGREAD_ENAB); if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) rdmac_mode |= RDMAC_MODE_SPLIT_ENABLE; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { - if (tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) { - if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) { - rdmac_mode |= RDMAC_MODE_FIFO_SIZE_128; - } else if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) && - !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) { - rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST; - } + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && + tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) || + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750)) { + if (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE && + (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 || + tp->pci_chip_rev_id == CHIPREV_ID_5705_A2)) { + rdmac_mode |= RDMAC_MODE_FIFO_SIZE_128; + } else if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) && + !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) { + rdmac_mode |= RDMAC_MODE_FIFO_LONG_BURST; } } +#if TG3_TSO_SUPPORT != 0 + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + rdmac_mode |= (1 << 27); +#endif + /* Receive/send statistics. */ if ((rdmac_mode & RDMAC_MODE_FIFO_SIZE_128) && (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE)) { @@ -4841,10 +5080,11 @@ tw32(HOSTCC_TXCOL_TICKS, LOW_TXCOL_TICKS); tw32(HOSTCC_RXMAX_FRAMES, 1); tw32(HOSTCC_TXMAX_FRAMES, LOW_RXMAX_FRAMES); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { tw32(HOSTCC_RXCOAL_TICK_INT, 0); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) tw32(HOSTCC_TXCOAL_TICK_INT, 0); + } tw32(HOSTCC_RXCOAL_MAXF_INT, 1); tw32(HOSTCC_TXCOAL_MAXF_INT, 0); @@ -4854,10 +5094,11 @@ tw32(HOSTCC_STATUS_BLK_HOST_ADDR + TG3_64BIT_REG_LOW, ((u64) tp->status_mapping & 0xffffffff)); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { /* Status/statistics block address. See tg3_timer, * the tg3_periodic_fetch_stats call there, and - * tg3_get_stats to see how this works for 5705 chips. + * tg3_get_stats to see how this works for 5705/5750 chips. */ tw32(HOSTCC_STAT_COAL_TICKS, DEFAULT_STAT_COAL_TICKS); @@ -4873,9 +5114,19 @@ tw32(RCVCC_MODE, RCVCC_MODE_ENABLE | RCVCC_MODE_ATTN_ENABLE); tw32(RCVLPC_MODE, RCVLPC_MODE_ENABLE); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) tw32(RCVLSC_MODE, RCVLSC_MODE_ENABLE | RCVLSC_MODE_ATTN_ENABLE); + /* Clear statistics/status block in chip, and status block in ram. */ + for (i = NIC_SRAM_STATS_BLK; + i < NIC_SRAM_STATUS_BLK + TG3_HW_STATUS_SIZE; + i += sizeof(u32)) { + tg3_write_mem(tp, i, 0); + udelay(40); + } + memset(tp->hw_status, 0, TG3_HW_STATUS_SIZE); + tp->mac_mode = MAC_MODE_TXSTAT_ENABLE | MAC_MODE_RXSTAT_ENABLE | MAC_MODE_TDE_ENABLE | MAC_MODE_RDE_ENABLE | MAC_MODE_FHDE_ENABLE; tw32_f(MAC_MODE, tp->mac_mode | MAC_MODE_RXSTAT_CLEAR | MAC_MODE_TXSTAT_CLEAR); @@ -4891,7 +5142,8 @@ tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0); tr32(MAILBOX_INTERRUPT_0); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) { tw32_f(DMAC_MODE, DMAC_MODE_ENABLE); udelay(40); } @@ -4901,10 +5153,21 @@ WDMAC_MODE_ADDROFLOW_ENAB | WDMAC_MODE_FIFOOFLOW_ENAB | WDMAC_MODE_FIFOURUN_ENAB | WDMAC_MODE_FIFOOREAD_ENAB | WDMAC_MODE_LNGREAD_ENAB); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && - (tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) != 0 && - !(tp->tg3_flags2 & TG3_FLG2_IS_5788)) - val |= WDMAC_MODE_RX_ACCEL; + + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && + tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + if ((tp->tg3_flags & TG3_FLG2_TSO_CAPABLE) && + (tp->pci_chip_rev_id == CHIPREV_ID_5705_A1 || + tp->pci_chip_rev_id == CHIPREV_ID_5705_A2)) { + /* nothing */ + } else if (!(tr32(TG3PCI_PCISTATE) & PCISTATE_BUS_SPEED_HIGH) && + !(tp->tg3_flags2 & TG3_FLG2_IS_5788) && + !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { + val |= WDMAC_MODE_RX_ACCEL; + } + } + tw32_f(WDMAC_MODE, val); udelay(40); @@ -4927,13 +5190,18 @@ udelay(40); tw32(RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE); - if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705) + if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5705 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750) tw32(MBFREE_MODE, MBFREE_MODE_ENABLE); tw32(SNDDATAC_MODE, SNDDATAC_MODE_ENABLE); tw32(SNDBDC_MODE, SNDBDC_MODE_ENABLE | SNDBDC_MODE_ATTN_ENABLE); tw32(RCVBDI_MODE, RCVBDI_MODE_ENABLE | RCVBDI_MODE_RCB_ATTN_ENAB); tw32(RCVDBDI_MODE, RCVDBDI_MODE_ENABLE | RCVDBDI_MODE_INV_RING_SZ); tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE); +#if TG3_TSO_SUPPORT != 0 + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + tw32(SNDDATAI_MODE, SNDDATAI_MODE_ENABLE | 0x8); +#endif tw32(SNDBDI_MODE, SNDBDI_MODE_ENABLE | SNDBDI_MODE_ATTN_ENABLE); tw32(SNDBDS_MODE, SNDBDS_MODE_ENABLE | SNDBDS_MODE_ATTN_ENABLE); @@ -4968,9 +5236,10 @@ tp->mi_mode = MAC_MI_MODE_BASE; tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(40); + udelay(80); + + tw32(MAC_LED_CTRL, tp->led_ctrl); - tw32(MAC_LED_CTRL, 0); tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB); if (tp->phy_id == PHY_ID_SERDES) { tw32_f(MAC_RX_MODE, RX_MODE_RESET); @@ -4979,14 +5248,43 @@ tw32_f(MAC_RX_MODE, tp->rx_mode); udelay(10); - if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) - tw32(MAC_SERDES_CFG, 0x616000); + if (tp->phy_id == PHY_ID_SERDES) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { + /* Set drive transmission level to 1.2V */ + val = tr32(MAC_SERDES_CFG); + val &= 0xfffff000; + val |= 0x880; + tw32(MAC_SERDES_CFG, val); + } + if (tp->pci_chip_rev_id == CHIPREV_ID_5703_A1) + tw32(MAC_SERDES_CFG, 0x616000); + } /* Prevent chip from dropping frames when flow control * is enabled. */ tw32_f(MAC_LOW_WMARK_MAX_RX_FRAME, 2); + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && + tp->phy_id == PHY_ID_SERDES) { + /* Enable hardware link auto-negotiation */ + u32 digctrl, txctrl; + + digctrl = SG_DIG_USING_HW_AUTONEG | SG_DIG_CRC16_CLEAR_N | + SG_DIG_LOCAL_DUPLEX_STATUS | SG_DIG_LOCAL_LINK_STATUS | + (2 << SG_DIG_SPEED_STATUS_SHIFT) | SG_DIG_FIBER_MODE | + SG_DIG_GBIC_ENABLE; + + txctrl = tr32(MAC_SERDES_CFG); + tw32_f(MAC_SERDES_CFG, txctrl | MAC_SERDES_CFG_EDGE_SELECT); + tw32_f(SG_DIG_CTRL, digctrl | SG_DIG_SOFT_RESET); + tr32(SG_DIG_CTRL); + udelay(5); + tw32_f(SG_DIG_CTRL, digctrl); + + tp->tg3_flags2 |= TG3_FLG2_HW_AUTONEG; + } + err = tg3_setup_phy(tp, 1); if (err) return err; @@ -5008,7 +5306,8 @@ tw32(MAC_RCV_RULE_1, 0x86000004 & RCV_RULE_DISABLE_MASK); tw32(MAC_RCV_VALUE_1, 0xffffffff & RCV_RULE_DISABLE_MASK); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) limit = 8; else limit = 16; @@ -5050,6 +5349,8 @@ break; }; + tg3_write_sig_post_reset(tp, RESET_KIND_INIT); + if (tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) tg3_enable_ints(tp); @@ -5150,7 +5451,8 @@ return; } - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) tg3_periodic_fetch_stats(tp); /* This part only runs once per second. */ @@ -5220,7 +5522,7 @@ static int tg3_open(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); int err; spin_lock_irq(&tp->lock); @@ -5519,10 +5821,11 @@ #endif static struct net_device_stats *tg3_get_stats(struct net_device *); +static struct tg3_ethtool_stats *tg3_get_estats(struct tg3 *); static int tg3_close(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); netif_stop_queue(dev); @@ -5550,6 +5853,8 @@ memcpy(&tp->net_stats_prev, tg3_get_stats(tp->dev), sizeof(tp->net_stats_prev)); + memcpy(&tp->estats_prev, tg3_get_estats(tp), + sizeof(tp->estats_prev)); tg3_free_consistent(tp); @@ -5592,9 +5897,101 @@ return get_stat64(&hw_stats->rx_fcs_errors); } +#define ESTAT_ADD(member) \ + estats->member = old_estats->member + \ + get_stat64(&hw_stats->member) + +static struct tg3_ethtool_stats *tg3_get_estats(struct tg3 *tp) +{ + struct tg3_ethtool_stats *estats = &tp->estats; + struct tg3_ethtool_stats *old_estats = &tp->estats_prev; + struct tg3_hw_stats *hw_stats = tp->hw_stats; + + if (!hw_stats) + return old_estats; + + ESTAT_ADD(rx_octets); + ESTAT_ADD(rx_fragments); + ESTAT_ADD(rx_ucast_packets); + ESTAT_ADD(rx_mcast_packets); + ESTAT_ADD(rx_bcast_packets); + ESTAT_ADD(rx_fcs_errors); + ESTAT_ADD(rx_align_errors); + ESTAT_ADD(rx_xon_pause_rcvd); + ESTAT_ADD(rx_xoff_pause_rcvd); + ESTAT_ADD(rx_mac_ctrl_rcvd); + ESTAT_ADD(rx_xoff_entered); + ESTAT_ADD(rx_frame_too_long_errors); + ESTAT_ADD(rx_jabbers); + ESTAT_ADD(rx_undersize_packets); + ESTAT_ADD(rx_in_length_errors); + ESTAT_ADD(rx_out_length_errors); + ESTAT_ADD(rx_64_or_less_octet_packets); + ESTAT_ADD(rx_65_to_127_octet_packets); + ESTAT_ADD(rx_128_to_255_octet_packets); + ESTAT_ADD(rx_256_to_511_octet_packets); + ESTAT_ADD(rx_512_to_1023_octet_packets); + ESTAT_ADD(rx_1024_to_1522_octet_packets); + ESTAT_ADD(rx_1523_to_2047_octet_packets); + ESTAT_ADD(rx_2048_to_4095_octet_packets); + ESTAT_ADD(rx_4096_to_8191_octet_packets); + ESTAT_ADD(rx_8192_to_9022_octet_packets); + + ESTAT_ADD(tx_octets); + ESTAT_ADD(tx_collisions); + ESTAT_ADD(tx_xon_sent); + ESTAT_ADD(tx_xoff_sent); + ESTAT_ADD(tx_flow_control); + ESTAT_ADD(tx_mac_errors); + ESTAT_ADD(tx_single_collisions); + ESTAT_ADD(tx_mult_collisions); + ESTAT_ADD(tx_deferred); + ESTAT_ADD(tx_excessive_collisions); + ESTAT_ADD(tx_late_collisions); + ESTAT_ADD(tx_collide_2times); + ESTAT_ADD(tx_collide_3times); + ESTAT_ADD(tx_collide_4times); + ESTAT_ADD(tx_collide_5times); + ESTAT_ADD(tx_collide_6times); + ESTAT_ADD(tx_collide_7times); + ESTAT_ADD(tx_collide_8times); + ESTAT_ADD(tx_collide_9times); + ESTAT_ADD(tx_collide_10times); + ESTAT_ADD(tx_collide_11times); + ESTAT_ADD(tx_collide_12times); + ESTAT_ADD(tx_collide_13times); + ESTAT_ADD(tx_collide_14times); + ESTAT_ADD(tx_collide_15times); + ESTAT_ADD(tx_ucast_packets); + ESTAT_ADD(tx_mcast_packets); + ESTAT_ADD(tx_bcast_packets); + ESTAT_ADD(tx_carrier_sense_errors); + ESTAT_ADD(tx_discards); + ESTAT_ADD(tx_errors); + + ESTAT_ADD(dma_writeq_full); + ESTAT_ADD(dma_write_prioq_full); + ESTAT_ADD(rxbds_empty); + ESTAT_ADD(rx_discards); + ESTAT_ADD(rx_errors); + ESTAT_ADD(rx_threshold_hit); + + ESTAT_ADD(dma_readq_full); + ESTAT_ADD(dma_read_prioq_full); + ESTAT_ADD(tx_comp_queue_full); + + ESTAT_ADD(ring_set_send_prod_index); + ESTAT_ADD(ring_status_update); + ESTAT_ADD(nic_irqs); + ESTAT_ADD(nic_avoided_irqs); + ESTAT_ADD(nic_tx_threshold_hit); + + return estats; +} + static struct net_device_stats *tg3_get_stats(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); struct net_device_stats *stats = &tp->net_stats; struct net_device_stats *old_stats = &tp->net_stats_prev; struct tg3_hw_stats *hw_stats = tp->hw_stats; @@ -5618,7 +6015,8 @@ get_stat64(&hw_stats->tx_octets); stats->rx_errors = old_stats->rx_errors + - get_stat64(&hw_stats->rx_errors); + get_stat64(&hw_stats->rx_errors) + + get_stat64(&hw_stats->rx_discards); stats->tx_errors = old_stats->tx_errors + get_stat64(&hw_stats->tx_errors) + get_stat64(&hw_stats->tx_mac_errors) + @@ -5685,7 +6083,7 @@ static void __tg3_set_rx_mode(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); u32 rx_mode; rx_mode = tp->rx_mode & ~(RX_MODE_PROMISC | @@ -5749,7 +6147,7 @@ static void tg3_set_rx_mode(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); spin_lock_irq(&tp->lock); __tg3_set_rx_mode(dev); @@ -5763,10 +6161,12 @@ return TG3_REGDUMP_LEN; } -static void tg3_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) +static void tg3_get_regs(struct net_device *dev, + struct ethtool_regs *regs, void *_p) { - struct tg3 *tp = dev->priv; - u8 *orig_p = p; + u32 *p = _p; + struct tg3 *tp = netdev_priv(dev); + u8 *orig_p = _p; int i; regs->version = 0; @@ -5776,15 +6176,15 @@ spin_lock_irq(&tp->lock); spin_lock(&tp->tx_lock); -#define __GET_REG32(reg) (*((u32 *)(p))++ = tr32(reg)) +#define __GET_REG32(reg) (*(p)++ = tr32(reg)) #define GET_REG32_LOOP(base,len) \ -do { p = orig_p + (base); \ +do { p = (u32 *)(orig_p + (base)); \ for (i = 0; i < len; i += 4) \ __GET_REG32((base) + i); \ } while (0) -#define GET_REG32_1(reg) \ -do { p = orig_p + (reg); \ - __GET_REG32((reg)); \ +#define GET_REG32_1(reg) \ +do { p = (u32 *)(orig_p + (reg)); \ + __GET_REG32((reg)); \ } while (0) GET_REG32_LOOP(TG3PCI_VENDOR, 0xb0); @@ -5828,9 +6228,75 @@ spin_unlock_irq(&tp->lock); } +static int tg3_get_eeprom_len(struct net_device *dev) +{ + return EEPROM_CHIP_SIZE; +} + +static int __devinit tg3_nvram_read_using_eeprom(struct tg3 *tp, + u32 offset, u32 *val); +static int tg3_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) +{ + struct tg3 *tp = dev->priv; + int ret; + u8 *pd; + u32 i, offset, len, val, b_offset, b_count; + + offset = eeprom->offset; + len = eeprom->len; + eeprom->len = 0; + + ret = tg3_nvram_read_using_eeprom(tp, 0, &eeprom->magic); + if (ret) + return ret; + eeprom->magic = swab32(eeprom->magic); + + if (offset & 3) { + /* adjustments to start on required 4 byte boundary */ + b_offset = offset & 3; + b_count = 4 - b_offset; + if (b_count > len) { + /* i.e. offset=1 len=2 */ + b_count = len; + } + ret = tg3_nvram_read_using_eeprom(tp, offset-b_offset, &val); + if (ret) + return ret; + memcpy(data, ((char*)&val) + b_offset, b_count); + len -= b_count; + offset += b_count; + eeprom->len += b_count; + } + + /* read bytes upto the last 4 byte boundary */ + pd = &data[eeprom->len]; + for (i = 0; i < (len - (len & 3)); i += 4) { + ret = tg3_nvram_read_using_eeprom(tp, offset + i, + (u32*)(pd + i)); + if (ret) { + eeprom->len += i; + return ret; + } + } + eeprom->len += i; + + if (len & 3) { + /* read last bytes not ending on 4 byte boundary */ + pd = &data[eeprom->len]; + b_count = len & 3; + b_offset = offset + len - b_count; + ret = tg3_nvram_read_using_eeprom(tp, b_offset, &val); + if (ret) + return ret; + memcpy(pd, ((char*)&val), b_count); + eeprom->len += b_count; + } + return 0; +} + static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || tp->link_config.phy_is_low_power) @@ -5865,12 +6331,22 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if (!(tp->tg3_flags & TG3_FLAG_INIT_COMPLETE) || tp->link_config.phy_is_low_power) return -EAGAIN; + if (tp->phy_id == PHY_ID_SERDES) { + /* These are the only valid advertisement bits allowed. */ + if (cmd->autoneg == AUTONEG_ENABLE && + (cmd->advertising & ~(ADVERTISED_1000baseT_Half | + ADVERTISED_1000baseT_Full | + ADVERTISED_Autoneg | + ADVERTISED_FIBRE))) + return -EINVAL; + } + spin_lock_irq(&tp->lock); spin_lock(&tp->tx_lock); @@ -5880,6 +6356,7 @@ tp->link_config.speed = SPEED_INVALID; tp->link_config.duplex = DUPLEX_INVALID; } else { + tp->link_config.advertising = 0; tp->link_config.speed = cmd->speed; tp->link_config.duplex = cmd->duplex; } @@ -5893,7 +6370,7 @@ static void tg3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); strcpy(info->driver, DRV_MODULE_NAME); strcpy(info->version, DRV_MODULE_VERSION); @@ -5902,7 +6379,7 @@ static void tg3_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); wol->supported = WAKE_MAGIC; wol->wolopts = 0; @@ -5913,7 +6390,7 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if (wol->wolopts & ~WAKE_MAGIC) return -EINVAL; @@ -5934,20 +6411,20 @@ static u32 tg3_get_msglevel(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); return tp->msg_enable; } static void tg3_set_msglevel(struct net_device *dev, u32 value) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); tp->msg_enable = value; } #if TG3_TSO_SUPPORT != 0 static int tg3_set_tso(struct net_device *dev, u32 value) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if (!(tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE)) { if (value) @@ -5960,7 +6437,7 @@ static int tg3_nway_reset(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); u32 bmcr; int r; @@ -5979,7 +6456,7 @@ static void tg3_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); ering->rx_max_pending = TG3_RX_RING_SIZE - 1; ering->rx_mini_max_pending = 0; @@ -5993,7 +6470,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if ((ering->rx_pending > TG3_RX_RING_SIZE - 1) || (ering->rx_jumbo_pending > TG3_RX_JUMBO_RING_SIZE - 1) || @@ -6024,7 +6501,7 @@ static void tg3_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); epause->autoneg = (tp->tg3_flags & TG3_FLAG_PAUSE_AUTONEG) != 0; epause->rx_pause = (tp->tg3_flags & TG3_FLAG_PAUSE_RX) != 0; @@ -6033,7 +6510,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); tg3_netif_stop(tp); spin_lock_irq(&tp->lock); @@ -6061,13 +6538,13 @@ static u32 tg3_get_rx_csum(struct net_device *dev) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); return (tp->tg3_flags & TG3_FLAG_RX_CHECKSUMS) != 0; } static int tg3_set_rx_csum(struct net_device *dev, u32 data) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if (tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) { if (data != 0) @@ -6087,7 +6564,7 @@ static int tg3_set_tx_csum(struct net_device *dev, u32 data) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); if (tp->tg3_flags & TG3_FLAG_BROKEN_CHECKSUMS) { if (data != 0) @@ -6102,11 +6579,35 @@ return 0; } - + +static int tg3_get_stats_count (struct net_device *dev) +{ + return TG3_NUM_STATS; +} + +static void tg3_get_strings (struct net_device *dev, u32 stringset, u8 *buf) +{ + switch (stringset) { + case ETH_SS_STATS: + memcpy(buf, ðtool_stats_keys, sizeof(ethtool_stats_keys)); + break; + default: + WARN_ON(1); /* we need a WARN() */ + break; + } +} + +static void tg3_get_ethtool_stats (struct net_device *dev, + struct ethtool_stats *estats, u64 *tmp_stats) +{ + struct tg3 *tp = dev->priv; + memcpy(tmp_stats, tg3_get_estats(tp), sizeof(tp->estats)); +} + static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data; - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); int err; switch(cmd) { @@ -6117,6 +6618,9 @@ case SIOCGMIIREG: { u32 mii_regval; + if (tp->phy_id == PHY_ID_SERDES) + break; /* We have no PHY */ + spin_lock_irq(&tp->lock); err = tg3_readphy(tp, data->reg_num & 0x1f, &mii_regval); spin_unlock_irq(&tp->lock); @@ -6127,6 +6631,9 @@ } case SIOCSMIIREG: + if (tp->phy_id == PHY_ID_SERDES) + break; /* We have no PHY */ + if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -6146,7 +6653,7 @@ #if TG3_VLAN_TAG_USED static void tg3_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); spin_lock_irq(&tp->lock); spin_lock(&tp->tx_lock); @@ -6162,7 +6669,7 @@ static void tg3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); spin_lock_irq(&tp->lock); spin_lock(&tp->tx_lock); @@ -6185,6 +6692,8 @@ .set_msglevel = tg3_set_msglevel, .nway_reset = tg3_nway_reset, .get_link = ethtool_op_get_link, + .get_eeprom_len = tg3_get_eeprom_len, + .get_eeprom = tg3_get_eeprom, .get_ringparam = tg3_get_ringparam, .set_ringparam = tg3_set_ringparam, .get_pauseparam = tg3_get_pauseparam, @@ -6199,6 +6708,9 @@ .get_tso = ethtool_op_get_tso, .set_tso = tg3_set_tso, #endif + .get_strings = tg3_get_strings, + .get_stats_count = tg3_get_stats_count, + .get_ethtool_stats = tg3_get_ethtool_stats, }; /* Chips other than 5700/5701 use the NVRAM for fetching info. */ @@ -6225,7 +6737,15 @@ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 && GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) { - u32 nvcfg1 = tr32(NVRAM_CFG1); + u32 nvcfg1; + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + u32 nvaccess = tr32(NVRAM_ACCESS); + + tw32_f(NVRAM_ACCESS, nvaccess | ACCESS_ENABLE); + } + + nvcfg1 = tr32(NVRAM_CFG1); tp->tg3_flags |= TG3_FLAG_NVRAM; if (nvcfg1 & NVRAM_CFG1_FLASHIF_ENAB) { @@ -6236,6 +6756,11 @@ tw32(NVRAM_CFG1, nvcfg1); } + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + u32 nvaccess = tr32(NVRAM_ACCESS); + + tw32_f(NVRAM_ACCESS, nvaccess & ~ACCESS_ENABLE); + } } else { tp->tg3_flags &= ~(TG3_FLAG_NVRAM | TG3_FLAG_NVRAM_BUFFERED); } @@ -6278,7 +6803,7 @@ static int __devinit tg3_nvram_read(struct tg3 *tp, u32 offset, u32 *val) { - int i, saw_done_clear; + int i; if (tp->tg3_flags2 & TG3_FLG2_SUN_5704) { printk(KERN_ERR PFX "Attempt to do nvram_read on Sun 5704\n"); @@ -6296,11 +6821,12 @@ if (offset > NVRAM_ADDR_MSK) return -EINVAL; - tw32(NVRAM_SWARB, SWARB_REQ_SET1); - for (i = 0; i < 1000; i++) { - if (tr32(NVRAM_SWARB) & SWARB_GNT1) - break; - udelay(20); + tg3_nvram_lock(tp); + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + u32 nvaccess = tr32(NVRAM_ACCESS); + + tw32_f(NVRAM_ACCESS, nvaccess | ACCESS_ENABLE); } tw32(NVRAM_ADDR, offset); @@ -6308,24 +6834,26 @@ NVRAM_CMD_RD | NVRAM_CMD_GO | NVRAM_CMD_FIRST | NVRAM_CMD_LAST | NVRAM_CMD_DONE); - /* Wait for done bit to clear then set again. */ - saw_done_clear = 0; + /* Wait for done bit to clear. */ for (i = 0; i < 1000; i++) { udelay(10); - if (!saw_done_clear && - !(tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) - saw_done_clear = 1; - else if (saw_done_clear && - (tr32(NVRAM_CMD) & NVRAM_CMD_DONE)) + if (tr32(NVRAM_CMD) & NVRAM_CMD_DONE) { + udelay(10); + *val = swab32(tr32(NVRAM_RDDATA)); break; + } } - if (i >= 1000) { - tw32(NVRAM_SWARB, SWARB_REQ_CLR1); - return -EBUSY; + + tg3_nvram_unlock(tp); + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + u32 nvaccess = tr32(NVRAM_ACCESS); + + tw32_f(NVRAM_ACCESS, nvaccess & ~ACCESS_ENABLE); } - *val = swab32(tr32(NVRAM_RDDATA)); - tw32(NVRAM_SWARB, 0x20); + if (i >= 1000) + return -EBUSY; return 0; } @@ -6346,8 +6874,8 @@ { PCI_VENDOR_ID_BROADCOM, 0x0007, PHY_ID_SERDES }, /* BCM95701A7 */ { PCI_VENDOR_ID_BROADCOM, 0x0008, PHY_ID_BCM5701 }, /* BCM95701A10 */ { PCI_VENDOR_ID_BROADCOM, 0x8008, PHY_ID_BCM5701 }, /* BCM95701A12 */ - { PCI_VENDOR_ID_BROADCOM, 0x0009, PHY_ID_BCM5701 }, /* BCM95703Ax1 */ - { PCI_VENDOR_ID_BROADCOM, 0x8009, PHY_ID_BCM5701 }, /* BCM95703Ax2 */ + { PCI_VENDOR_ID_BROADCOM, 0x0009, PHY_ID_BCM5703 }, /* BCM95703Ax1 */ + { PCI_VENDOR_ID_BROADCOM, 0x8009, PHY_ID_BCM5703 }, /* BCM95703Ax2 */ /* 3com boards. */ { PCI_VENDOR_ID_3COM, 0x1000, PHY_ID_BCM5401 }, /* 3C996T */ @@ -6377,7 +6905,6 @@ { u32 eeprom_phy_id, hw_phy_id_1, hw_phy_id_2; u32 hw_phy_id, hw_phy_id_masked; - enum phy_led_mode eeprom_led_mode; u32 val; int i, eeprom_signature_found, err; @@ -6393,11 +6920,10 @@ } eeprom_phy_id = PHY_ID_INVALID; - eeprom_led_mode = led_mode_auto; eeprom_signature_found = 0; tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { - u32 nic_cfg; + u32 nic_cfg, led_cfg; tg3_read_mem(tp, NIC_SRAM_DATA_CFG, &nic_cfg); tp->nic_sram_data_cfg = nic_cfg; @@ -6421,45 +6947,89 @@ } } - switch (nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK) { - case NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD: - eeprom_led_mode = led_mode_three_link; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { + tg3_read_mem(tp, NIC_SRAM_DATA_CFG_2, &led_cfg); + led_cfg &= (NIC_SRAM_DATA_CFG_LED_MODE_MASK | + SHASTA_EXT_LED_MODE_MASK); + } else + led_cfg = nic_cfg & NIC_SRAM_DATA_CFG_LED_MODE_MASK; + + switch (led_cfg) { + default: + case NIC_SRAM_DATA_CFG_LED_MODE_PHY_1: + tp->led_ctrl = LED_CTRL_MODE_PHY_1; break; - case NIC_SRAM_DATA_CFG_LED_LINK_SPD: - eeprom_led_mode = led_mode_link10; + case NIC_SRAM_DATA_CFG_LED_MODE_PHY_2: + tp->led_ctrl = LED_CTRL_MODE_PHY_2; break; - default: - eeprom_led_mode = led_mode_auto; + case NIC_SRAM_DATA_CFG_LED_MODE_MAC: + tp->led_ctrl = LED_CTRL_MODE_MAC; + break; + + case SHASTA_EXT_LED_SHARED: + tp->led_ctrl = LED_CTRL_MODE_SHARED; + if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0 && + tp->pci_chip_rev_id != CHIPREV_ID_5750_A1) + tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 | + LED_CTRL_MODE_PHY_2); + break; + + case SHASTA_EXT_LED_MAC: + tp->led_ctrl = LED_CTRL_MODE_SHASTA_MAC; break; + + case SHASTA_EXT_LED_COMBO: + tp->led_ctrl = LED_CTRL_MODE_COMBO; + if (tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) + tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 | + LED_CTRL_MODE_PHY_2); + break; + }; + if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) && + tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL) + tp->led_ctrl = LED_CTRL_MODE_PHY_2; + if (((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) && (nic_cfg & NIC_SRAM_DATA_CFG_EEPROM_WP)) tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; - if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE) + if (nic_cfg & NIC_SRAM_DATA_CFG_ASF_ENABLE) { tp->tg3_flags |= TG3_FLAG_ENABLE_ASF; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + tp->tg3_flags2 |= TG3_FLG2_ASF_NEW_HANDSHAKE; + } if (nic_cfg & NIC_SRAM_DATA_CFG_FIBER_WOL) tp->tg3_flags |= TG3_FLAG_SERDES_WOL_CAP; } - /* Now read the physical PHY_ID from the chip and verify - * that it is sane. If it doesn't look good, we fall back - * to either the hard-coded table based PHY_ID and failing - * that the value found in the eeprom area. - */ - err = tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); - err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); - - hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; - hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; - hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; + /* Reading the PHY ID register can conflict with ASF + * firwmare access to the PHY hardware. + */ + err = 0; + if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) { + hw_phy_id = hw_phy_id_masked = PHY_ID_INVALID; + } else { + /* Now read the physical PHY_ID from the chip and verify + * that it is sane. If it doesn't look good, we fall back + * to either the hard-coded table based PHY_ID and failing + * that the value found in the eeprom area. + */ + err |= tg3_readphy(tp, MII_PHYSID1, &hw_phy_id_1); + err |= tg3_readphy(tp, MII_PHYSID2, &hw_phy_id_2); + + hw_phy_id = (hw_phy_id_1 & 0xffff) << 10; + hw_phy_id |= (hw_phy_id_2 & 0xfc00) << 16; + hw_phy_id |= (hw_phy_id_2 & 0x03ff) << 0; - hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; + hw_phy_id_masked = hw_phy_id & PHY_ID_MASK; + } if (!err && KNOWN_PHY_ID(hw_phy_id_masked)) { tp->phy_id = hw_phy_id; @@ -6476,64 +7046,62 @@ } } - err = tg3_phy_reset(tp, 1); - if (err) - return err; + if (tp->phy_id != PHY_ID_SERDES && + !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) { + u32 bmsr, adv_reg, tg3_ctrl; - if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || - tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) { - u32 mii_tg3_ctrl; - - /* These chips, when reset, only advertise 10Mb - * capabilities. Fix that. - */ - err = tg3_writephy(tp, MII_ADVERTISE, - (ADVERTISE_CSMA | - ADVERTISE_PAUSE_CAP | - ADVERTISE_10HALF | - ADVERTISE_10FULL | - ADVERTISE_100HALF | - ADVERTISE_100FULL)); - mii_tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | - MII_TG3_CTRL_ADV_1000_FULL | - MII_TG3_CTRL_AS_MASTER | - MII_TG3_CTRL_ENABLE_AS_MASTER); - if (tp->tg3_flags & TG3_FLAG_10_100_ONLY) - mii_tg3_ctrl = 0; + tg3_readphy(tp, MII_BMSR, &bmsr); + tg3_readphy(tp, MII_BMSR, &bmsr); - err |= tg3_writephy(tp, MII_TG3_CTRL, mii_tg3_ctrl); - err |= tg3_writephy(tp, MII_BMCR, - (BMCR_ANRESTART | BMCR_ANENABLE)); - } + if (bmsr & BMSR_LSTATUS) + goto skip_phy_reset; + + err = tg3_phy_reset(tp); + if (err) + return err; - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703) { - tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x0c00); - tg3_writephy(tp, MII_TG3_DSP_ADDRESS, 0x201f); - tg3_writephy(tp, MII_TG3_DSP_RW_PORT, 0x2aaa); - } + adv_reg = (ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL | + ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + tg3_ctrl = 0; + if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) { + tg3_ctrl = (MII_TG3_CTRL_ADV_1000_HALF | + MII_TG3_CTRL_ADV_1000_FULL); + if (tp->pci_chip_rev_id == CHIPREV_ID_5701_A0 || + tp->pci_chip_rev_id == CHIPREV_ID_5701_B0) + tg3_ctrl |= (MII_TG3_CTRL_AS_MASTER | + MII_TG3_CTRL_ENABLE_AS_MASTER); + } - if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) && - (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0)) { - tg3_writephy(tp, 0x1c, 0x8d68); - tg3_writephy(tp, 0x1c, 0x8d68); + if (!tg3_copper_is_advertising_all(tp)) { + tg3_writephy(tp, MII_ADVERTISE, adv_reg); + + if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) + tg3_writephy(tp, MII_TG3_CTRL, tg3_ctrl); + + tg3_writephy(tp, MII_BMCR, + BMCR_ANENABLE | BMCR_ANRESTART); + } + tg3_phy_set_wirespeed(tp); + + tg3_writephy(tp, MII_ADVERTISE, adv_reg); + if (!(tp->tg3_flags & TG3_FLAG_10_100_ONLY)) + tg3_writephy(tp, MII_TG3_CTRL, tg3_ctrl); } - /* Enable Ethernet@WireSpeed */ - tg3_phy_set_wirespeed(tp); +skip_phy_reset: + if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) { + err = tg3_init_5401phy_dsp(tp); + if (err) + return err; + } if (!err && ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)) { err = tg3_init_5401phy_dsp(tp); } - /* Determine the PHY led mode. */ - if (tp->pdev->subsystem_vendor == PCI_VENDOR_ID_DELL) { - tp->led_mode = led_mode_link10; - } else { - tp->led_mode = led_mode_three_link; - if (eeprom_signature_found && - eeprom_led_mode != led_mode_auto) - tp->led_mode = eeprom_led_mode; - } + if (!eeprom_signature_found) + tp->led_ctrl = LED_CTRL_MODE_PHY_1; if (tp->phy_id == PHY_ID_SERDES) tp->link_config.advertising = @@ -6715,6 +7283,9 @@ tp->pci_hdr_type = (cacheline_sz_reg >> 16) & 0xff; tp->pci_bist = (cacheline_sz_reg >> 24) & 0xff; + if (pci_find_capability(tp->pdev, PCI_CAP_ID_EXP) != 0) + tp->tg3_flags2 |= TG3_FLG2_PCI_EXPRESS; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 && tp->pci_lat_timer < 64) { tp->pci_lat_timer = 64; @@ -6767,8 +7338,12 @@ /* Back to back register writes can cause problems on this chip, * the workaround is to read back all reg writes except those to * mailbox regs. See tg3_write_indirect_reg32(). + * + * PCI Express 5750_A0 rev chips need this workaround too. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 || + ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) && + tp->pci_chip_rev_id == CHIPREV_ID_5750_A0)) tp->tg3_flags |= TG3_FLAG_5701_REG_WRITE_BUG; if ((pci_state_reg & PCISTATE_BUS_SPEED_HIGH) != 0) @@ -6787,7 +7362,7 @@ err = tg3_set_power_state(tp, 0); if (err) { printk(KERN_ERR PFX "(%s) transition to D0 failed\n", - tp->pdev->slot_name); + pci_name(tp->pdev)); return err; } @@ -6835,6 +7410,10 @@ if (tp->pci_chip_rev_id == CHIPREV_ID_5704_A0) tp->tg3_flags2 |= TG3_FLG2_PHY_5704_A0_BUG; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG; + /* Only 5701 and later support tagged irq status mode. * Also, 5788 chips cannot use tagged irq status. * @@ -6850,7 +7429,7 @@ /* Initialize MAC MI mode, polling disabled. */ tw32_f(MAC_MI_MODE, tp->mi_mode); - udelay(40); + udelay(80); /* Initialize data/descriptor byte/word swapping. */ val = tr32(GRC_MODE); @@ -6891,12 +7470,22 @@ udelay(50); tg3_nvram_init(tp); + /* Always use host TXDs, it performs better in particular + * with multi-frag packets. The tests below are kept here + * as documentation should we change this decision again + * in the future. + */ + tp->tg3_flags |= TG3_FLAG_HOST_TXDS; + +#if 0 /* Determine if TX descriptors will reside in * main memory or in the chip SRAM. */ if ((tp->tg3_flags & TG3_FLAG_PCIX_TARGET_HWBUG) != 0 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) tp->tg3_flags |= TG3_FLAG_HOST_TXDS; +#endif grc_misc_cfg = tr32(GRC_MISC_CFG); grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; @@ -6919,13 +7508,15 @@ tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM && (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5901 || tp->pdev->device == PCI_DEVICE_ID_TIGON3_5901_2 || - tp->pdev->device == PCI_DEVICE_ID_TIGON3_5705F))) + tp->pdev->device == PCI_DEVICE_ID_TIGON3_5705F)) || + (tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM && + tp->pdev->device == PCI_DEVICE_ID_TIGON3_5751F)) tp->tg3_flags |= TG3_FLAG_10_100_ONLY; err = tg3_phy_probe(tp); if (err) { printk(KERN_ERR PFX "(%s) phy probe failed, err %d\n", - tp->pdev->slot_name, err); + pci_name(tp->pdev), err); /* ... but do not return immediately ... */ } @@ -6933,11 +7524,6 @@ if (tp->phy_id == PHY_ID_SERDES) { tp->tg3_flags &= ~TG3_FLAG_USE_MI_INTERRUPT; - - /* And override led_mode in case Dell ever makes - * a fibre board. - */ - tp->led_mode = led_mode_three_link; } else { if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) tp->tg3_flags |= TG3_FLAG_USE_MI_INTERRUPT; @@ -6954,7 +7540,7 @@ else tp->tg3_flags &= ~TG3_FLAG_USE_LINKCHG_REG; - /* The led_mode is set during tg3_phy_probe, here we might + /* The led_ctrl is set during tg3_phy_probe, here we might * have to force the link status polling mechanism based * upon subsystem IDs. */ @@ -6978,13 +7564,10 @@ else tp->tg3_flags &= ~TG3_FLAG_TXD_MBOX_HWBUG; - /* 5700 chips can get confused if TX buffers straddle the - * 4GB address boundary in some cases. + /* It seems all chips can get confused if TX buffers + * straddle the 4GB address boundary in some cases. */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700) - tp->dev->hard_start_xmit = tg3_start_xmit_4gbug; - else - tp->dev->hard_start_xmit = tg3_start_xmit; + tp->dev->hard_start_xmit = tg3_start_xmit; tp->rx_offset = 2; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 && @@ -7037,10 +7620,16 @@ return 0; #endif - if (PCI_FUNC(tp->pdev->devfn) == 0) - mac_offset = 0x7c; - else - mac_offset = 0xcc; + mac_offset = 0x7c; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && + !(tp->tg3_flags & TG3_FLG2_SUN_5704)) { + if (tr32(TG3PCI_DUAL_MAC_CTRL) & DUAL_MAC_CTRL_ID) + mac_offset = 0xcc; + if (tg3_nvram_lock(tp)) + tw32_f(NVRAM_CMD, NVRAM_CMD_RESET); + else + tg3_nvram_unlock(tp); + } /* First try to get it from MAC address mailbox. */ tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi); @@ -7184,50 +7773,9 @@ goto out_nofree; } - if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) == 0) { - tp->dma_rwctrl = - (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | - (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | - (0x7 << DMA_RWCTRL_WRITE_WATER_SHIFT) | - (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) | - (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) - tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA - << DMA_RWCTRL_MIN_DMA_SHIFT); - } else { - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) - tp->dma_rwctrl = - (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | - (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | - (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) | - (0x7 << DMA_RWCTRL_READ_WATER_SHIFT) | - (0x00 << DMA_RWCTRL_MIN_DMA_SHIFT); - else - tp->dma_rwctrl = - (0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | - (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT) | - (0x3 << DMA_RWCTRL_WRITE_WATER_SHIFT) | - (0x3 << DMA_RWCTRL_READ_WATER_SHIFT) | - (0x0f << DMA_RWCTRL_MIN_DMA_SHIFT); - - /* Wheee, some more chip bugs... */ - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { - u32 ccval = (tr32(TG3PCI_CLOCK_CTRL) & 0x1f); - - if (ccval == 0x6 || ccval == 0x7) - tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; - } - } - - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || - GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) - tp->dma_rwctrl &= ~(DMA_RWCTRL_MIN_DMA - << DMA_RWCTRL_MIN_DMA_SHIFT); + tp->dma_rwctrl = ((0x7 << DMA_RWCTRL_PCI_WRITE_CMD_SHIFT) | + (0x6 << DMA_RWCTRL_PCI_READ_CMD_SHIFT)); - /* We don't do this on x86 because it seems to hurt performace. - * It does help things on other platforms though. - */ #ifndef CONFIG_X86 { u8 byte; @@ -7239,55 +7787,63 @@ else cacheline_size = (int) byte * 4; - tp->dma_rwctrl &= ~(DMA_RWCTRL_READ_BNDRY_MASK | - DMA_RWCTRL_WRITE_BNDRY_MASK); - switch (cacheline_size) { case 16: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_16 | - DMA_RWCTRL_WRITE_BNDRY_16); - break; - case 32: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_32 | - DMA_RWCTRL_WRITE_BNDRY_32); - break; - case 64: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_64 | - DMA_RWCTRL_WRITE_BNDRY_64); - break; - case 128: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_128 | - DMA_RWCTRL_WRITE_BNDRY_128); - break; - + if ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) && + !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) { + tp->dma_rwctrl |= + DMA_RWCTRL_WRITE_BNDRY_384_PCIX; + break; + } else if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + tp->dma_rwctrl &= + ~(DMA_RWCTRL_PCI_WRITE_CMD); + tp->dma_rwctrl |= + DMA_RWCTRL_WRITE_BNDRY_128_PCIE; + break; + } + /* fallthrough */ case 256: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_256 | - DMA_RWCTRL_WRITE_BNDRY_256); - break; - - case 512: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_512 | - DMA_RWCTRL_WRITE_BNDRY_512); - break; - - case 1024: - tp->dma_rwctrl |= - (DMA_RWCTRL_READ_BNDRY_1024 | - DMA_RWCTRL_WRITE_BNDRY_1024); - break; + if (!(tp->tg3_flags & TG3_FLAG_PCIX_MODE) && + !(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) + tp->dma_rwctrl |= + DMA_RWCTRL_WRITE_BNDRY_256; + else if (!(tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS)) + tp->dma_rwctrl |= + DMA_RWCTRL_WRITE_BNDRY_256_PCIX; }; } #endif + if (tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) { + tp->dma_rwctrl |= 0x001f0000; + } else if (!(tp->tg3_flags & TG3_FLAG_PCIX_MODE)) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) + tp->dma_rwctrl |= 0x003f0000; + else + tp->dma_rwctrl |= 0x003f000f; + } else { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { + u32 ccval = (tr32(TG3PCI_CLOCK_CTRL) & 0x1f); + + if (ccval == 0x6 || ccval == 0x7) + tp->dma_rwctrl |= DMA_RWCTRL_ONE_DMA; + + /* Set bit 23 to renable PCIX hw bug fix */ + tp->dma_rwctrl |= 0x009f0000; + } else { + tp->dma_rwctrl |= 0x001b000f; + } + } + + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5703 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) + tp->dma_rwctrl &= 0xfffffff0; + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701) { /* Remove this if it causes problems for some boards. */ @@ -7430,6 +7986,7 @@ case PHY_ID_BCM5703: return "5703"; case PHY_ID_BCM5704: return "5704"; case PHY_ID_BCM5705: return "5705"; + case PHY_ID_BCM5750: return "5750"; case PHY_ID_BCM8002: return "8002"; case PHY_ID_SERDES: return "serdes"; default: return "unknown"; @@ -7527,6 +8084,7 @@ } SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; @@ -7536,7 +8094,7 @@ dev->vlan_rx_kill_vid = tg3_vlan_rx_kill_vid; #endif - tp = dev->priv; + tp = netdev_priv(dev); tp->pdev = pdev; tp->dev = dev; tp->pm_cap = pm_cap; @@ -7604,6 +8162,9 @@ dev->watchdog_timeo = TG3_TX_TIMEOUT; dev->change_mtu = tg3_change_mtu; dev->irq = pdev->irq; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = tg3_poll_controller; +#endif err = tg3_get_invariants(tp); if (err) { @@ -7612,7 +8173,8 @@ goto err_out_iounmap; } - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) { + if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750) { tp->bufmgr_config.mbuf_read_dma_low_water = DEFAULT_MB_RDMA_LOW_WATER_5705; tp->bufmgr_config.mbuf_mac_rx_low_water = @@ -7625,8 +8187,8 @@ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701 || tp->pci_chip_rev_id == CHIPREV_ID_5705_A0 || - (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0 || - (tp->tg3_flags2 & TG3_FLG2_IS_5788)) { + ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0 && + GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750)) { tp->tg3_flags2 &= ~TG3_FLG2_TSO_CAPABLE; } else { tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; @@ -7718,13 +8280,26 @@ printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + printk(KERN_INFO "%s: HostTXDS[%d] RXcsums[%d] LinkChgREG[%d] " + "MIirq[%d] ASF[%d] Split[%d] WireSpeed[%d] " + "TSOcap[%d] \n", + dev->name, + (tp->tg3_flags & TG3_FLAG_HOST_TXDS) != 0, + (tp->tg3_flags & TG3_FLAG_RX_CHECKSUMS) != 0, + (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) != 0, + (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) != 0, + (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0, + (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) != 0, + (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) == 0, + (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) != 0); + return 0; err_out_iounmap: iounmap((void *) tp->regs); err_out_free_dev: - kfree(dev); + free_netdev(dev); err_out_free_res: pci_release_regions(pdev); @@ -7740,9 +8315,11 @@ struct net_device *dev = pci_get_drvdata(pdev); if (dev) { + struct tg3 *tp = netdev_priv(dev); + unregister_netdev(dev); - iounmap((void *) ((struct tg3 *)(dev->priv))->regs); - kfree(dev); + iounmap((void *)tp->regs); + free_netdev(dev); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -7752,7 +8329,7 @@ static int tg3_suspend(struct pci_dev *pdev, u32 state) { struct net_device *dev = pci_get_drvdata(pdev); - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); int err; if (!netif_running(dev)) @@ -7799,12 +8376,14 @@ static int tg3_resume(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - struct tg3 *tp = dev->priv; + struct tg3 *tp = netdev_priv(dev); int err; if (!netif_running(dev)) return 0; + pci_restore_state(tp->pdev, tp->pci_cfg_state); + err = tg3_set_power_state(tp, 0); if (err) return err; diff -urN linux-2.4.26/drivers/net/tg3.h linux-2.4.27/drivers/net/tg3.h --- linux-2.4.26/drivers/net/tg3.h 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/tg3.h 2004-08-07 16:26:05.150367158 -0700 @@ -1,8 +1,9 @@ /* $Id: tg3.h,v 1.37.2.32 2002/03/11 12:18:18 davem Exp $ * tg3.h: Definitions for Broadcom Tigon3 ethernet driver. * - * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com) + * Copyright (C) 2001, 2002, 2003, 2004 David S. Miller (davem@redhat.com) * Copyright (C) 2001 Jeff Garzik (jgarzik@pobox.com) + * Copyright (C) 2004 Sun Microsystems Inc. */ #ifndef _T3_H @@ -116,14 +117,20 @@ #define CHIPREV_ID_5704_A0 0x2000 #define CHIPREV_ID_5704_A1 0x2001 #define CHIPREV_ID_5704_A2 0x2002 +#define CHIPREV_ID_5704_A3 0x2003 #define CHIPREV_ID_5705_A0 0x3000 #define CHIPREV_ID_5705_A1 0x3001 +#define CHIPREV_ID_5705_A2 0x3002 +#define CHIPREV_ID_5705_A3 0x3003 +#define CHIPREV_ID_5750_A0 0x4000 +#define CHIPREV_ID_5750_A1 0x4001 #define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12) #define ASIC_REV_5700 0x07 #define ASIC_REV_5701 0x00 #define ASIC_REV_5703 0x01 #define ASIC_REV_5704 0x02 #define ASIC_REV_5705 0x03 +#define ASIC_REV_5750 0x04 #define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8) #define CHIPREV_5700_AX 0x70 #define CHIPREV_5700_BX 0x71 @@ -144,8 +151,11 @@ #define DMA_RWCTRL_READ_BNDRY_MASK 0x00000700 #define DMA_RWCTRL_READ_BNDRY_DISAB 0x00000000 #define DMA_RWCTRL_READ_BNDRY_16 0x00000100 +#define DMA_RWCTRL_READ_BNDRY_128_PCIX 0x00000100 #define DMA_RWCTRL_READ_BNDRY_32 0x00000200 +#define DMA_RWCTRL_READ_BNDRY_256_PCIX 0x00000200 #define DMA_RWCTRL_READ_BNDRY_64 0x00000300 +#define DMA_RWCTRL_READ_BNDRY_384_PCIX 0x00000300 #define DMA_RWCTRL_READ_BNDRY_128 0x00000400 #define DMA_RWCTRL_READ_BNDRY_256 0x00000500 #define DMA_RWCTRL_READ_BNDRY_512 0x00000600 @@ -153,8 +163,11 @@ #define DMA_RWCTRL_WRITE_BNDRY_MASK 0x00003800 #define DMA_RWCTRL_WRITE_BNDRY_DISAB 0x00000000 #define DMA_RWCTRL_WRITE_BNDRY_16 0x00000800 +#define DMA_RWCTRL_WRITE_BNDRY_128_PCIX 0x00000800 #define DMA_RWCTRL_WRITE_BNDRY_32 0x00001000 +#define DMA_RWCTRL_WRITE_BNDRY_256_PCIX 0x00001000 #define DMA_RWCTRL_WRITE_BNDRY_64 0x00001800 +#define DMA_RWCTRL_WRITE_BNDRY_384_PCIX 0x00001800 #define DMA_RWCTRL_WRITE_BNDRY_128 0x00002000 #define DMA_RWCTRL_WRITE_BNDRY_256 0x00002800 #define DMA_RWCTRL_WRITE_BNDRY_512 0x00003000 @@ -170,6 +183,9 @@ #define DMA_RWCTRL_PCI_READ_CMD_SHIFT 24 #define DMA_RWCTRL_PCI_WRITE_CMD 0xf0000000 #define DMA_RWCTRL_PCI_WRITE_CMD_SHIFT 28 +#define DMA_RWCTRL_WRITE_BNDRY_64_PCIE 0x10000000 +#define DMA_RWCTRL_WRITE_BNDRY_128_PCIE 0x30000000 +#define DMA_RWCTRL_WRITE_BNDRY_DISAB_PCIE 0x70000000 #define TG3PCI_PCISTATE 0x00000070 #define PCISTATE_FORCE_RESET 0x00000001 #define PCISTATE_INT_NOT_ACTIVE 0x00000002 @@ -202,7 +218,11 @@ #define TG3PCI_STD_RING_PROD_IDX 0x00000098 /* 64-bit */ #define TG3PCI_RCV_RET_RING_CON_IDX 0x000000a0 /* 64-bit */ #define TG3PCI_SND_PROD_IDX 0x000000a8 /* 64-bit */ -/* 0xb0 --> 0x100 unused */ +/* 0xb0 --> 0xb8 unused */ +#define TG3PCI_DUAL_MAC_CTRL 0x000000b8 +#define DUAL_MAC_CTRL_CH_MASK 0x00000003 +#define DUAL_MAC_CTRL_ID 0x00000004 +/* 0xbc --> 0x100 unused */ /* 0x100 --> 0x200 unused */ @@ -334,9 +354,12 @@ #define LED_CTRL_100MBPS_STATUS 0x00000100 #define LED_CTRL_10MBPS_STATUS 0x00000200 #define LED_CTRL_TRAFFIC_STATUS 0x00000400 -#define LED_CTRL_MAC_MODE 0x00000000 -#define LED_CTRL_PHY_MODE_1 0x00000800 -#define LED_CTRL_PHY_MODE_2 0x00001000 +#define LED_CTRL_MODE_MAC 0x00000000 +#define LED_CTRL_MODE_PHY_1 0x00000800 +#define LED_CTRL_MODE_PHY_2 0x00001000 +#define LED_CTRL_MODE_SHASTA_MAC 0x00002000 +#define LED_CTRL_MODE_SHARED 0x00004000 +#define LED_CTRL_MODE_COMBO 0x00008000 #define LED_CTRL_BLINK_RATE_MASK 0x7ff80000 #define LED_CTRL_BLINK_RATE_SHIFT 19 #define LED_CTRL_BLINK_PER_OVERRIDE 0x00080000 @@ -497,8 +520,50 @@ #define MAC_EXTADDR_11_HIGH 0x00000588 #define MAC_EXTADDR_11_LOW 0x0000058c #define MAC_SERDES_CFG 0x00000590 +#define MAC_SERDES_CFG_EDGE_SELECT 0x00001000 #define MAC_SERDES_STAT 0x00000594 -/* 0x598 --> 0x600 unused */ +/* 0x598 --> 0x5b0 unused */ +#define SG_DIG_CTRL 0x000005b0 +#define SG_DIG_USING_HW_AUTONEG 0x80000000 +#define SG_DIG_SOFT_RESET 0x40000000 +#define SG_DIG_DISABLE_LINKRDY 0x20000000 +#define SG_DIG_CRC16_CLEAR_N 0x01000000 +#define SG_DIG_EN10B 0x00800000 +#define SG_DIG_CLEAR_STATUS 0x00400000 +#define SG_DIG_LOCAL_DUPLEX_STATUS 0x00200000 +#define SG_DIG_LOCAL_LINK_STATUS 0x00100000 +#define SG_DIG_SPEED_STATUS_MASK 0x000c0000 +#define SG_DIG_SPEED_STATUS_SHIFT 18 +#define SG_DIG_JUMBO_PACKET_DISABLE 0x00020000 +#define SG_DIG_RESTART_AUTONEG 0x00010000 +#define SG_DIG_FIBER_MODE 0x00008000 +#define SG_DIG_REMOTE_FAULT_MASK 0x00006000 +#define SG_DIG_PAUSE_MASK 0x00001800 +#define SG_DIG_GBIC_ENABLE 0x00000400 +#define SG_DIG_CHECK_END_ENABLE 0x00000200 +#define SG_DIG_SGMII_AUTONEG_TIMER 0x00000100 +#define SG_DIG_CLOCK_PHASE_SELECT 0x00000080 +#define SG_DIG_GMII_INPUT_SELECT 0x00000040 +#define SG_DIG_MRADV_CRC16_SELECT 0x00000020 +#define SG_DIG_COMMA_DETECT_ENABLE 0x00000010 +#define SG_DIG_AUTONEG_TIMER_REDUCE 0x00000008 +#define SG_DIG_AUTONEG_LOW_ENABLE 0x00000004 +#define SG_DIG_REMOTE_LOOPBACK 0x00000002 +#define SG_DIG_LOOPBACK 0x00000001 +#define SG_DIG_STATUS 0x000005b4 +#define SG_DIG_CRC16_BUS_MASK 0xffff0000 +#define SG_DIG_PARTNER_FAULT_MASK 0x00600000 /* If !MRADV_CRC16_SELECT */ +#define SG_DIG_PARTNER_ASYM_PAUSE 0x00100000 /* If !MRADV_CRC16_SELECT */ +#define SG_DIG_PARTNER_PAUSE_CAPABLE 0x00080000 /* If !MRADV_CRC16_SELECT */ +#define SG_DIG_PARTNER_HALF_DUPLEX 0x00040000 /* If !MRADV_CRC16_SELECT */ +#define SG_DIG_PARTNER_FULL_DUPLEX 0x00020000 /* If !MRADV_CRC16_SELECT */ +#define SG_DIG_PARTNER_NEXT_PAGE 0x00010000 /* If !MRADV_CRC16_SELECT */ +#define SG_DIG_AUTONEG_STATE_MASK 0x00000ff0 +#define SG_DIG_COMMA_DETECTOR 0x00000008 +#define SG_DIG_MAC_ACK_STATUS 0x00000004 +#define SG_DIG_AUTONEG_COMPLETE 0x00000002 +#define SG_DIG_AUTONEG_ERROR 0x00000001 +/* 0x5b8 --> 0x600 unused */ #define MAC_TX_MAC_STATE_BASE 0x00000600 /* 16 bytes */ #define MAC_RX_MAC_STATE_BASE 0x00000610 /* 20 bytes */ /* 0x624 --> 0x800 unused */ @@ -1332,6 +1397,9 @@ #define SWARB_REQ3 0x00008000 #define NVRAM_BUFFERED_PAGE_SIZE 264 #define NVRAM_BUFFERED_PAGE_POS 9 +#define NVRAM_ACCESS 0x00007024 +#define ACCESS_ENABLE 0x00000001 +#define ACCESS_WR_ENABLE 0x00000002 /* 0x7024 --> 0x7400 unused */ /* 0x7400 --> 0x8000 unused */ @@ -1355,11 +1423,9 @@ #define NIC_SRAM_DATA_CFG 0x00000b58 #define NIC_SRAM_DATA_CFG_LED_MODE_MASK 0x0000000c -#define NIC_SRAM_DATA_CFG_LED_MODE_UNKNOWN 0x00000000 -#define NIC_SRAM_DATA_CFG_LED_TRIPLE_SPD 0x00000004 -#define NIC_SRAM_DATA_CFG_LED_OPEN_DRAIN 0x00000004 -#define NIC_SRAM_DATA_CFG_LED_LINK_SPD 0x00000008 -#define NIC_SRAM_DATA_CFG_LED_OUTPUT 0x00000008 +#define NIC_SRAM_DATA_CFG_LED_MODE_MAC 0x00000000 +#define NIC_SRAM_DATA_CFG_LED_MODE_PHY_1 0x00000004 +#define NIC_SRAM_DATA_CFG_LED_MODE_PHY_2 0x00000008 #define NIC_SRAM_DATA_CFG_PHY_TYPE_MASK 0x00000030 #define NIC_SRAM_DATA_CFG_PHY_TYPE_UNKNOWN 0x00000000 #define NIC_SRAM_DATA_CFG_PHY_TYPE_COPPER 0x00000010 @@ -1386,7 +1452,9 @@ #define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00 #define NIC_SRAM_FW_DRV_STATE_MBOX 0x00000c04 #define DRV_STATE_START 0x00000001 +#define DRV_STATE_START_DONE 0x80000001 #define DRV_STATE_UNLOAD 0x00000002 +#define DRV_STATE_UNLOAD_DONE 0x80000002 #define DRV_STATE_WOL 0x00000003 #define DRV_STATE_SUSPEND 0x00000004 @@ -1395,6 +1463,14 @@ #define NIC_SRAM_MAC_ADDR_HIGH_MBOX 0x00000c14 #define NIC_SRAM_MAC_ADDR_LOW_MBOX 0x00000c18 +#define NIC_SRAM_DATA_CFG_2 0x00000d38 + +#define SHASTA_EXT_LED_MODE_MASK 0x00018000 +#define SHASTA_EXT_LED_LEGACY 0x00000000 +#define SHASTA_EXT_LED_SHARED 0x00008000 +#define SHASTA_EXT_LED_MAC 0x00010000 +#define SHASTA_EXT_LED_COMBO 0x00018000 + #define NIC_SRAM_RX_MINI_BUFFER_DESC 0x00001000 #define NIC_SRAM_DMA_DESC_POOL_BASE 0x00002000 @@ -1756,12 +1832,6 @@ u8 __reserved4[0xb00-0x9c0]; }; -enum phy_led_mode { - led_mode_auto, - led_mode_three_link, - led_mode_link10 -}; - /* 'mapping' is superfluous as the chip does not write into * the tx/rx post rings so we could just fetch it from there. * But the cache behavior is better how we are doing it now. @@ -1817,6 +1887,89 @@ u32 dma_high_water; }; +struct tg3_ethtool_stats { + /* Statistics maintained by Receive MAC. */ + u64 rx_octets; + u64 rx_fragments; + u64 rx_ucast_packets; + u64 rx_mcast_packets; + u64 rx_bcast_packets; + u64 rx_fcs_errors; + u64 rx_align_errors; + u64 rx_xon_pause_rcvd; + u64 rx_xoff_pause_rcvd; + u64 rx_mac_ctrl_rcvd; + u64 rx_xoff_entered; + u64 rx_frame_too_long_errors; + u64 rx_jabbers; + u64 rx_undersize_packets; + u64 rx_in_length_errors; + u64 rx_out_length_errors; + u64 rx_64_or_less_octet_packets; + u64 rx_65_to_127_octet_packets; + u64 rx_128_to_255_octet_packets; + u64 rx_256_to_511_octet_packets; + u64 rx_512_to_1023_octet_packets; + u64 rx_1024_to_1522_octet_packets; + u64 rx_1523_to_2047_octet_packets; + u64 rx_2048_to_4095_octet_packets; + u64 rx_4096_to_8191_octet_packets; + u64 rx_8192_to_9022_octet_packets; + + /* Statistics maintained by Transmit MAC. */ + u64 tx_octets; + u64 tx_collisions; + u64 tx_xon_sent; + u64 tx_xoff_sent; + u64 tx_flow_control; + u64 tx_mac_errors; + u64 tx_single_collisions; + u64 tx_mult_collisions; + u64 tx_deferred; + u64 tx_excessive_collisions; + u64 tx_late_collisions; + u64 tx_collide_2times; + u64 tx_collide_3times; + u64 tx_collide_4times; + u64 tx_collide_5times; + u64 tx_collide_6times; + u64 tx_collide_7times; + u64 tx_collide_8times; + u64 tx_collide_9times; + u64 tx_collide_10times; + u64 tx_collide_11times; + u64 tx_collide_12times; + u64 tx_collide_13times; + u64 tx_collide_14times; + u64 tx_collide_15times; + u64 tx_ucast_packets; + u64 tx_mcast_packets; + u64 tx_bcast_packets; + u64 tx_carrier_sense_errors; + u64 tx_discards; + u64 tx_errors; + + /* Statistics maintained by Receive List Placement. */ + u64 dma_writeq_full; + u64 dma_write_prioq_full; + u64 rxbds_empty; + u64 rx_discards; + u64 rx_errors; + u64 rx_threshold_hit; + + /* Statistics maintained by Send Data Initiator. */ + u64 dma_readq_full; + u64 dma_read_prioq_full; + u64 tx_comp_queue_full; + + /* Statistics maintained by Host Coalescing. */ + u64 ring_set_send_prod_index; + u64 ring_status_update; + u64 nic_irqs; + u64 nic_avoided_irqs; + u64 nic_tx_threshold_hit; +}; + struct tg3 { /* begin "general, frequently-used members" cacheline section */ @@ -1880,6 +2033,9 @@ /* begin "everything else" cacheline(s) section */ struct net_device_stats net_stats; struct net_device_stats net_stats_prev; + struct tg3_ethtool_stats estats; + struct tg3_ethtool_stats estats_prev; + unsigned long phy_crc_errors; u32 rx_offset; @@ -1929,6 +2085,10 @@ #define TG3_FLG2_TSO_CAPABLE 0x00000020 #define TG3_FLG2_PHY_ADC_BUG 0x00000040 #define TG3_FLG2_PHY_5704_A0_BUG 0x00000080 +#define TG3_FLG2_PHY_BER_BUG 0x00000100 +#define TG3_FLG2_PCI_EXPRESS 0x00000200 +#define TG3_FLG2_ASF_NEW_HANDSHAKE 0x00000400 +#define TG3_FLG2_HW_AUTONEG 0x00000800 u32 split_mode_max_reqs; #define SPLIT_MODE_5704_MAX_REQ 3 @@ -1974,6 +2134,7 @@ #define PHY_ID_BCM5703 0x60008160 #define PHY_ID_BCM5704 0x60008190 #define PHY_ID_BCM5705 0x600081a0 +#define PHY_ID_BCM5750 0x60008180 #define PHY_ID_BCM8002 0x60010140 #define PHY_ID_SERDES 0xfeedbee0 #define PHY_ID_INVALID 0xffffffff @@ -1983,7 +2144,7 @@ #define PHY_REV_BCM5401_C0 0x6 #define PHY_REV_BCM5411_X0 0x1 /* Found on Netgear GA302T */ - enum phy_led_mode led_mode; + u32 led_ctrl; char board_part_number[24]; u32 nic_sram_data_cfg; @@ -1997,7 +2158,7 @@ ((X) == PHY_ID_BCM5400 || (X) == PHY_ID_BCM5401 || \ (X) == PHY_ID_BCM5411 || (X) == PHY_ID_BCM5701 || \ (X) == PHY_ID_BCM5703 || (X) == PHY_ID_BCM5704 || \ - (X) == PHY_ID_BCM5705 || \ + (X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \ (X) == PHY_ID_BCM8002 || (X) == PHY_ID_SERDES) struct tg3_hw_stats *hw_stats; diff -urN linux-2.4.26/drivers/net/tulip/ChangeLog linux-2.4.27/drivers/net/tulip/ChangeLog --- linux-2.4.26/drivers/net/tulip/ChangeLog 2003-06-13 07:51:35.000000000 -0700 +++ linux-2.4.27/drivers/net/tulip/ChangeLog 1969-12-31 16:00:00.000000000 -0800 @@ -1,563 +0,0 @@ -2002-09-18 Ryan Bradetich - - tulip hppa support: - * eeprom.c (tulip_build_fake_mediatable): new function - (tulip_parse_eeprom): call it, when no media table - * interrupt.c (phy_interrupt): new function - (tulip_interrupt): call it, before checking for no-irq-work - * tulip.c: add HAS_PHY_IRQ chip feature flag. - add csr12_shadow to tulip_private struct, only for hppa currently. - * tulip_core (tulip_init_one): support hppa wonky eeproms - -2002-05-11 Juan Quintela - - * 21142.c (t21142_lnk_change): Revert earlier patch - to always reset phy; only conditionally do so now. - -2002-05-03 Jeff Garzik - - * tulip_core (tulip_pci_tbl): Add new "comet" - pci id. Contributed by Ohta Kyuma. - -2002-03-07 Jeff Garzik - - * tulip_core (tulip_mwi_config): Use new PCI API functions - for enabling and disabled Memory-Write-Invalidate - PCI transaction. - Fix bugs in tulip MWI config also. - -2002-02-07 Uwe Bonnes - - * tulip_core (tulip_pci_tbl[]): - Add PCI id for comet tulip clone. - -2001-12-19 John Zielinski - - * tulip_core.c (tulip_up, tulip_init_one): - More places to revert PHY autoconfiguration bit removal. - -2001-12-16 Andrew Lambeth - - * tulip_core.c (tulip_start_xmit): Use the more-portable - spin_lock_irqsave. - -2001-11-13 David S. Miller - - * tulip_core.c (tulip_mwi_config): Kill unused label early_out. - -2001-11-06 Richard Mortimer - - * tulip_core.c: Correct set of values to mask out of csr0, - for DM9102A chips. Limit burst/alignment of DM9102A chips - on Sparcs. - -2001-11-06 Jun Sun - - * tulip_core.c: Support finding MAC address on - two MIPS boards, DDB5476 and DDB5477. - -2001-11-06 Kevin B. Hendricks - - * Makefile, tulip.h, tulip_core.c, pnic2.c, 21142.c: - Fixes for PNIC II support. - -2001-11-06 David S. Miller - - * tulip_core.c: Support reading MAC address from - Sparc OBP property local-mac-address. - -2001-07-17 Erik A. Hendriks - - * 21142.c: Merge fix from tulip.c 0.92w which prevents the - overwriting of csr6 bits we want to preserve. - -2001-07-10 Jeff Golds - - * tulip_core.c: Fix two comments - -2001-07-06 Stephen Degler - - * media.c: - The media selection process at the end of NWAY is busted - because for the case of MII/SYM it needs to be: - - csr13 <- 0 - csr14 <- 0 - csr6 <- the value calculated is okay. - - In the other media cases csr14 is computed by - t21142_csr14val[dev->if_port], which seems ok. The value of - zero as opposed to 3FFFFF comes straight from appendix D of the - 21143 data book, and it makes logical sense because you're - bypassing all the SIA interface when you usa MII or SYM (see - figure 1-1 in the data book if your're visually oriented) - -2001-07-03 Jeff Golds - - * tulip_core.c (tulip_clean_tx_ring): - Clear status for in-progress Tx's, and count - Tx errors for all packets being released. - -2001-06-16 Jeff Garzik - - * tulip.h, tulip_core.c: - Integrate MMIO support from devel branch, but default - it to off for stable kernel and driver series. - -2001-06-16 Jeff Garzik - - * tulip_core.c (tulip_init_one): - Free descriptor rings on error. - -2001-06-16 Jeff Garzik - - * tulip_core.c (tulip_mwi_config, tulip_init_one): - Large update to csr0 bus configuration code. This is not stable - yet, so it is only conditionally enabled, via CONFIG_TULIP_MWI. - -2001-06-16 Jeff Garzik - - * tulip_core.c: - Initialize timer in tulip_init_one and tulip_down, - not in tulip_up. - -2001-06-14 Jeff Garzik - - * tulip_core.c: - - Update tulip_suspend, tulip_resume for new PCI PM API. - - Surround suspend/resume code with CONFIG_PM. - -2001-06-12 Jeff Golds - - * tulip_core.c: - - Reset sw ring ptrs in tulip_up. Fixes PM resume case. - - Clean rx and tx rings on device down. - -2001-06-05 David Miller - - * tulip_core (set_rx_mode): Do not use set_bit - on an integer variable. Also fix endianness issue. - -2001-06-04 Jeff Garzik - - * interrupt.c: - Simplify rx processing when CONFIG_NET_HW_FLOWCONTROL is - active, and in the process fix a bug where flow control - and low load caused rx not to be acknowledged properly. - -2001-06-01 Jeff Garzik - - * tulip.h: - - Remove tulip_outl_csr helper, redundant. - - Add tulip_start_rxtx inline helper. - - tulip_stop_rxtx helper: Add synchronization. Always use current - csr6 value, instead of tp->csr6 value or value passed as arg. - - tulip_restart_rxtx helper: Add synchronization. Always - use tp->csr6 for desired mode, not value passed as arg. - - New RxOn, TxOn, RxTx constants for csr6 modes. - - Remove now-redundant constants csr6_st, csr6_sr. - - * 21142.c, interrupt.c, media.c, pnic.c, tulip_core.c: - Update for above rxtx helper changes. - - * interrupt.c: - - whitespace cleanup around #ifdef CONFIG_NET_HW_FLOWCONTROL, - convert tabs to spaces. - - Move tp->stats.rx_missed_errors update outside the ifdef. - -2001-05-18 Jeff Garzik - - * tulip_core.c: Added ethtool support. - ETHTOOL_GDRVINFO ioctl only, for now. - -2001-05-14 Robert Olsson - - * Restored HW_FLOWCONTROL from Linux 2.1 series tulip (ANK) - plus Jamal's NETIF_RX_* feedback control. - -2001-05-14 Robert Olsson - - * Added support for 21143's Interrupt Mitigation. - Jamal original instigator. - -2001-05-14 Robert Olsson - - * tulip_refill_rx prototype added to tulip.h - -2001-05-13 Jeff Garzik - - * tulip_core.c: Remove HAS_PCI_MWI flag from Comet, untested. - -2001-05-12 Jeff Garzik - - * tulip_core.c, tulip.h: Remove Conexant PCI id, no chip - docs are available to fix problems with support. - -2001-05-12 Jeff Garzik - - * tulip_core.c (tulip_init_one): Do not call - unregister_netdev in error cleanup. Remnant of old - usage of init_etherdev. - -2001-05-12 Jeff Garzik - - * media.c (tulip_find_mii): Simple write the updated BMCR - twice, as it seems the best thing to do for both broken and - sane chips. - If the mii_advert value, as read from MII_ADVERTISE, is zero, - then generate a value we should advertise from the capability - bits in BMSR. - Fill in tp->advertising for all cases. - Just to be safe, clear all unwanted bits. - -2001-05-12 Jeff Garzik - - * tulip_core.c (private_ioctl): Fill in tp->advertising - when advertising value is changed by the user. - -2001-05-12 Jeff Garzik - - * tulip_core.c: Mark Comet chips as needed the updated MWI - csr0 configuration. - -2001-05-12 Jeff Garzik - - * media.c, tulip_core.c: Move MII scan into - from inlined inside tulip_init_one to new function - tulip_find_mii in media.c. - -2001-05-12 Jeff Garzik - - * media.c (tulip_check_duplex): - Only restart Rx/Tx engines if they are active - (and csr6 changes) - -2001-05-12 Jeff Garzik - - * tulip_core.c (tulip_mwi_config): - Clamp values read from PCI cache line size register to - values acceptable to tulip chip. Done for safety and - -almost- certainly unneeded. - -2001-05-11 Jeff Garzik - - * tulip_core.c (tulip_init_one): - Instead of unconditionally enabling autonegotiation, disable - autonegotiation if not using the default port. Further, - flip the nway bit immediately, and then update the - speed/duplex in a separate MII transaction. We do this - because some boards require that nway be disabled separately, - before media selection is forced. - - TODO: Investigate if we can simply write the same value - to BMCR twice, to avoid setting unnecessarily changing - phy settings. - -2001-05-11 Jeff Garzik - - * tulip.h, tulip_core.c: If HAS_PCI_MWI is set for a - given chip, adjust the csr0 values not according to - provided values but according to system cache line size. - Currently cache alignment is matched as closely to cache - line size as possible. Currently programmable burst limit - is set (ie. never unlimited), and always equal to cache - alignment and system cache size. Currently MWI bit is set - only if the MWI bit is present in the PCI command register. - -2001-05-11 Jeff Garzik - - * media.c (tulip_select_media): - For media types 1 and 3, only use the provided eeprom - advertising value if it is non-zero. - (tulip_check_duplex): - Do not exit ASAP if full_duplex_lock is set. This - ensures that the csr6 value is written if an update - is needed. - -2001-05-10 Jeff Garzik - - Merge PNIC-II-specific stuff from Becker's tulip.c: - - * tulip.h, 21142.c (pnic2_lnk_change): new function - * tulip_core.c (tulip_init_one): use it - - * tulip_core.c (tulip_tx_timeout): Add specific - debugging for PNIC2. - -2001-05-10 Jeff Garzik - - * tulip_core.c (tulip_init_one): Print out - tulip%d instead of PCI device number, for - consistency. - -2001-05-10 Jeff Garzik - - * Merge changes from Becker's tulip.c: - Fix bugs in ioctl. - Fix several bugs by distinguishing between MII - and SYM advertising values. - Set CSR14 autonegotiation bit for media types 2 and 4, - where the SIA CSR setup values are not provided. - -2001-05-10 Jeff Garzik - - * media.c (tulip_select_media): Only update MII - advertising value if startup arg < 2. - - * tulip.h: Do not enable CSR13/14/15 autoconfiguration - for 21041. - - * tulip_core.c: - 21041: add specific code for reset, and do not set CAC bit - When resetting media, for media table type 11 media, pass - value 2 as 'startup' arg to select_media, to avoid updating - MII advertising value. - -2001-05-10 Jeff Garzik - - * pnic.c (pnic_check_duplex): remove - pnic.c (pnic_lnk_change, pnic_timer): use - tulip_check_duplex not pnic_check_duplex. - - * media.c (tulip_check_duplex): - Clean up to use symbolic names instead of numeric constants. - Set TxThreshold mode as necessary as well as clearing it. - Update csr6 if csr6 changes, not simply if duplex changes. - - (found by Manfred Spraul) - -2001-05-10 Jeff Garzik - - * 21142.c, eeprom.c, tulip.h, tulip_core.c: - Remove DPRINTK as another, better method of - debug message printing is available. - -2001-05-09 Jeff Garzik - - * 21142.c (t21142_lnk_change): Pass arg startup==1 - to tulip_select_media, in order to force csr13 to be - zeroed out prior to going to full duplex mode. Fixes - autonegotiation on a quad-port Znyx card. - (from Stephen Dengler) - -2001-05-09 Russell King - - * interrupt.c: Better PCI bus error reporting. - -2001-04-03 Jeff Garzik - - * tulip_core.c: Now that dev->name is only available late - in the probe, insert a hack to replace a not-evaluated - "eth%d" string with an evaluated "tulip%d" string. - Also, remove obvious comment and an indentation cleanup. - -2001-04-03 Jeff Garzik - - * tulip_core.c: If we are a module, always print out the - version string. If we are built into the kernel, only print - the version string if at least one tulip is detected. - -2001-04-03 Jeff Garzik - - Merged from Becker's tulip.c 0.92t: - - * tulip_core.c: Add support for Conexant LANfinity. - -2001-04-03 Jeff Garzik - - * tulip_core.c: Only suspend/resume if the interface - is up and running. Use alloc_etherdev and pci_request_regions. - Spelling fix. - -2001-04-03 Jeff Garzik - - * tulip_core.c: Remove code that existed when one or more of - the following defines existed. These defines were never used - by normal users in practice: TULIP_FULL_DUPLEX, - TULIP_DEFAULT_MEDIA, and TULIP_NO_MEDIA_SWITCH. - - * tulip.h, eeprom.c: Move EE_* constants from tulip.h to eeprom.c. - * tulip.h, media.c: Move MDIO_* constants from tulip.h to media.c. - - * media.c: Add barrier() to mdio_read/write's PNIC status check - loops. - -2001-04-03 Jeff Garzik - - Merged from Becker's tulip.c 0.92t: - - * tulip.h: Add MEDIA_MASK constant for bounding medianame[] - array lookups. - * eeprom.c, media.c, timer.c, tulip_core.c: Use it. - - * media.c, tulip_core.c: mdio_{read,write} cleanup. Since this - is called [pretty much] directly from ioctl, we mask - read/write arguments to limit the values passed. - Added mii_lock. Added comet_miireg2offset and better - Comet-specific mdio_read/write code. Pay closer attention - to the bits we set in ioctl. Remove spinlocks from ioctl, - they are in mdio_read/write now. Use mask to limit - phy number in tulip_init_one's MII scan. - -2001-04-03 Jeff Garzik - - Merged from Becker's tulip.c 0.92t: - - * 21142.c, tulip_core.c: PNIC2 MAC address and NWay fixes. - * tulip.h: Add FullDuplex constant, used in above change. - -2001-04-03 Jeff Garzik - - * timer.c: Do not call netif_carrier_{on,off}, it is not used in - the main tree. Leave code in, disabled, as markers for future - carrier notification. - -2001-04-03 Jeff Garzik - - Merged from Becker's tulip.c 0.92t, except for the tulip.h - whitespace cleanup: - - * interrupt.c: If Rx stops, make sure to update the - multicast filter before restarting. - * tulip.h: Add COMET_MAC_ADDR feature flag, clean up flags. - Add Accept* Rx mode bit constants. - Add mc_filter[] to driver private struct. - * tulip_core.c: Add new Comet PCI id 0x1113:0x9511. - Add COMET_MAC_ADDR feature flag to comet entry in board info array. - Prefer to test COMET_MAC_ADDR flag to testing chip_id for COMET, - when dealing with the Comet's MAC address. - Enable Tx underrun recovery for Comet chips. - Use new Accept* constants in set_rx_mode. - Prefer COMET_MAC_ADDR flag test to chip_id test in set_rx_mode. - Store built mc_filter for later use in intr handler by Comets. - -2001-04-03 Jeff Garzik - - * tulip_core.c: Use tp->cur_tx when building the - setup frame, instead of assuming that the setup - frame is always built in slot zero. This case is - hit during PM resume. - -2001-04-03 Jeff Garzik - - * *.c: Update file headers (copyright, urls, etc.) - * Makefile: re-order to that chip-specific modules on own line - * eeprom.c: BSS/zero-init cleanup (Andrey Panin) - * tulip_core.c: merge medianame[] update from tulip.c. - Additional arch-specific rx_copybreak, csr0 values. (various) - -2001-02-20 Jeff Garzik - - * media.c (tulip_select_media): No need to initialize - new_csr6, all cases initialize it properly. - -2001-02-18 Manfred Spraul - - * interrupt.c (tulip_refill_rx): Make public. - If PNIC chip stops due to lack of Rx buffers, restart it. - (tulip_interrupt): PNIC doesn't have a h/w timer, emulate - with software timers. - * pnic.c (pnic_check_duplex): New function, PNIC-specific - version of tulip_check_duplex. - (pnic_lnk_change): Call pnic_check_duplex. If we use an - external MII, then we mustn't use the internal negotiation. - (pnic_timer): Support Rx refilling on work overflow in - interrupt handler, as PNIC doesn't support a h/w timer. - * tulip_core.c (tulip_tbl[]): Modify default csr6 - -2001-02-11 Jeff Garzik - - * tulip_core.c (tulip_init_one): Call pci_enable_device - to ensure wakeup/resource assignment before checking those - values. - (tulip_init_one): Replace PCI ids with constants from pci_id.h. - (tulip_suspend, tulip_resume, tulip_remove_one): Call - pci_power_on/off (commented out for now). - -2001-02-10 Jeff Garzik - - * tulip.h: Add CFDD_xxx bits for Tulip power management - * tulip_core.c (tulip_set_power_state): New function, - manipulating Tulip chip power state where supported. - (tulip_up, tulip_down, tulip_init_one): Use it. - -2001-02-10 Jeff Garzik - - * tulip_core.c (tulip_tx_timeout): Call netif_wake_queue - to ensure the next Tx is always sent to us. - -2001-01-27 Jeff Garzik - - * tulip_core.c (tulip_remove_one): Fix mem leak by freeing - tp->media_tbl. Add check for !dev, reformat code appropriately. - -2001-01-27 Jeff Garzik - - * tulip_tbl[]: Comment all entries to make order and chip_id - relationship more clear. - * tulip_pci_tbl[]: Add new Accton PCI id (COMET chipset). - -2001-01-16 Jeff Garzik - - * tulip_core.c: static vars no longer explicitly - initialized to zero. - * eeprom.c (tulip_read_eeprom): Make sure to delay between - EE_ENB and EE_ENB|EE_SHIFT_CLK. Merged from becker tulip.c. - -2001-01-05 Peter De Schrijver - - * eeprom.c (tulip_parse_eeprom): Interpret a bit more of 21142 - extended format type 3 info blocks in a tulip SROM. - -2001-01-03 Matti Aarnio - - * media.c (tulip_select_media): Support media types 5 and 6 - -2001-??-?? ?? - - * tulip_core.c: Add comment about LanMedia needing - a different driver. - Enable workarounds for early PCI chipsets. - Add IA64 csr0 support, update HPPA csr0 support. - -2000-12-17 Alan Cox - - * eeprom.c, timer.c, tulip.h, tulip_core.c: Merge support - for the Davicom's quirks into the main tulip. - Patch by Tobias Ringstrom - -2000-11-08 Jim Studt - - * eeprom.c (tulip_parse_eeprom): Check array bounds for - medianame[] and block_name[] arrays to avoid oops due - to bad values returned from hardware. - -2000-11-02 Jeff Garzik - - * tulip_core.c (set_rx_mode): This is synchronized via - dev->xmit_lock, so only the queueing of the setup frame needs to - be locked, against tulip_interrupt. - -2000-11-02 Alexey Kuznetov - - * timer.c (tulip_timer): Call netif_carrier_{on,off} to report - link state to the rest of the kernel, and userspace. - * interrupt.c (tulip_interrupt): Remove tx_full. - * tulip.h: Likewise. - * tulip_core.c (tulip_init_ring, tulip_start_xmit, set_rx_mode): - Likewise. - -2000-10-18 Jeff Garzik - - * tulip_core.c: (tulip_init_one) Print out ethernet interface - on error. Print out a message when pci_enable_device fails. - Handle DMA alloc failure. - -2000-10-18 Jeff Garzik - - * Makefile: New file. - * tulip_core.c (tulip_init_one): Correct error messages - on PIO/MMIO region reserve failure. - (tulip_init_one) Add new check to ensure that PIO region is - sufficient for our needs. - diff -urN linux-2.4.26/drivers/net/tulip/timer.c linux-2.4.27/drivers/net/tulip/timer.c --- linux-2.4.26/drivers/net/tulip/timer.c 2003-06-13 07:51:35.000000000 -0700 +++ linux-2.4.27/drivers/net/tulip/timer.c 2004-08-07 16:26:05.155367364 -0700 @@ -211,10 +211,16 @@ if (tulip_debug > 1) printk(KERN_DEBUG "%s: Comet link status %4.4x partner capability " "%4.4x.\n", - dev->name, inl(ioaddr + 0xB8), inl(ioaddr + 0xC8)); + dev->name, + tulip_mdio_read(dev, tp->phys[0], 1), + tulip_mdio_read(dev, tp->phys[0], 5)); /* mod_timer synchronizes us with potential add_timer calls * from interrupts. */ + if (tulip_check_duplex(dev) < 0) + { netif_carrier_off(dev); } + else + { netif_carrier_on(dev); } mod_timer(&tp->timer, RUN_AT(next_tick)); } diff -urN linux-2.4.26/drivers/net/tulip/tulip_core.c linux-2.4.27/drivers/net/tulip/tulip_core.c --- linux-2.4.26/drivers/net/tulip/tulip_core.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/tulip/tulip_core.c 2004-08-07 16:26:05.157367446 -0700 @@ -176,7 +176,7 @@ /* COMET */ { "ADMtek Comet", 256, 0x0001abef, - MC_HASH_ONLY | COMET_MAC_ADDR, comet_timer }, + HAS_MII | MC_HASH_ONLY | COMET_MAC_ADDR, comet_timer }, /* COMPEX9881 */ { "Compex 9881 PMAC", 128, 0x0001ebef, @@ -226,6 +226,7 @@ { 0x1113, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x1113, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, MX98715 }, { 0x1113, 0x9511, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, + { 0x1186, 0x1541, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x1186, 0x1561, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x1626, 0x8410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, { 0x1737, 0xAB09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET }, @@ -320,8 +321,8 @@ tp->dirty_rx = tp->dirty_tx = 0; if (tp->flags & MC_HASH_ONLY) { - u32 addr_low = cpu_to_le32(get_unaligned((u32 *)dev->dev_addr)); - u32 addr_high = cpu_to_le32(get_unaligned((u16 *)(dev->dev_addr+4))); + u32 addr_low = le32_to_cpu(get_unaligned((u32 *)dev->dev_addr)); + u32 addr_high = le16_to_cpu(get_unaligned((u16 *)(dev->dev_addr+4))); if (tp->chip_id == AX88140) { outl(0, ioaddr + CSR13); outl(addr_low, ioaddr + CSR14); @@ -1544,8 +1545,8 @@ } } else if (chip_idx == COMET) { /* No need to read the EEPROM. */ - put_unaligned(inl(ioaddr + 0xA4), (u32 *)dev->dev_addr); - put_unaligned(inl(ioaddr + 0xA8), (u16 *)(dev->dev_addr + 4)); + put_unaligned(cpu_to_le32(inl(ioaddr + 0xA4)), (u32 *)dev->dev_addr); + put_unaligned(cpu_to_le16(inl(ioaddr + 0xA8)), (u16 *)(dev->dev_addr + 4)); for (i = 0; i < 6; i ++) sum += dev->dev_addr[i]; } else { @@ -1884,11 +1885,11 @@ return; tp = dev->priv; + unregister_netdev (dev); pci_free_consistent (pdev, sizeof (struct tulip_rx_desc) * RX_RING_SIZE + sizeof (struct tulip_tx_desc) * TX_RING_SIZE, tp->rx_ring, tp->rx_ring_dma); - unregister_netdev (dev); if (tp->mtable) kfree (tp->mtable); #ifndef USE_IO_OPS diff -urN linux-2.4.26/drivers/net/tun.c linux-2.4.27/drivers/net/tun.c --- linux-2.4.26/drivers/net/tun.c 2002-08-02 17:39:44.000000000 -0700 +++ linux-2.4.27/drivers/net/tun.c 2004-08-07 16:26:05.158367487 -0700 @@ -138,8 +138,8 @@ dev->addr_len = 0; dev->mtu = 1500; - /* Type PPP seems most suitable */ - dev->type = ARPHRD_PPP; + /* Zero header length */ + dev->type = ARPHRD_NONE; dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; dev->tx_queue_len = 10; break; diff -urN linux-2.4.26/drivers/net/via-rhine.c linux-2.4.27/drivers/net/via-rhine.c --- linux-2.4.26/drivers/net/via-rhine.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/net/via-rhine.c 2004-08-07 16:26:05.160367569 -0700 @@ -28,10 +28,10 @@ Linux kernel version history: - + LK1.1.0: - Jeff Garzik: softnet 'n stuff - + LK1.1.1: - Justin Guyett: softnet and locking fixes - Jeff Garzik: use PCI interface @@ -58,7 +58,7 @@ LK1.1.6: - Urban Widmark: merges from Beckers 1.08b version (VT6102 + mdio) set netif_running_on/off on startup, del_timer_sync - + LK1.1.7: - Manfred Spraul: added reset into tx_timeout @@ -83,7 +83,7 @@ LK1.1.13 (jgarzik): - Add ethtool support - Replace some MII-related magic numbers with constants - + LK1.1.14 (Ivan G.): - fixes comments for Rhine-III - removes W_MAX_TIMEOUT (unused) @@ -92,7 +92,7 @@ - sends chip_id as a parameter to wait_for_reset since np is not initialized on first call - changes mmio "else if (chip_id==VT6102)" to "else" so it will work - for Rhine-III's (documentation says same bit is correct) + for Rhine-III's (documentation says same bit is correct) - transmit frame queue message is off by one - fixed - adds IntrNormalSummary to "Something Wicked" exclusion list so normal interrupts will not trigger the message (src: Donald Becker) @@ -316,10 +316,10 @@ The driver runs as two independent, single-threaded flows of control. One is the send-packet routine, which enforces single-threaded use by the -dev->priv->lock spinlock. The other thread is the interrupt handler, which +dev->priv->lock spinlock. The other thread is the interrupt handler, which is single threaded by the hardware and interrupt handling software. -The send packet thread has partial control over the Tx ring. It locks the +The send packet thread has partial control over the Tx ring. It locks the dev->priv->lock whenever it's queuing a Tx packet. If the next slot in the ring is not available it stops the transmit queue by calling netif_stop_queue. @@ -615,6 +615,15 @@ break; } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void via_rhine_poll(struct net_device *dev) +{ + disable_irq(dev->irq); + via_rhine_interrupt(dev->irq, (void *)dev, NULL); + enable_irq(dev->irq); +} +#endif + static int __devinit via_rhine_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -630,7 +639,7 @@ #ifdef USE_MEM long ioaddr0; #endif - + /* when built into the kernel, we only print version if device is found */ #ifndef MODULE static int printed_version; @@ -651,7 +660,7 @@ printk(KERN_ERR "32-bit PCI DMA addresses not supported by the card!?\n"); goto err_out; } - + /* sanity check */ if ((pci_resource_len (pdev, 0) < io_size) || (pci_resource_len (pdev, 1) < io_size)) { @@ -671,7 +680,8 @@ goto err_out; } SET_MODULE_OWNER(dev); - + SET_NETDEV_DEV(dev, &pdev->dev); + if (pci_request_regions(pdev, shortname)) goto err_out_free_netdev; @@ -783,6 +793,9 @@ dev->ethtool_ops = &netdev_ethtool_ops; dev->tx_timeout = via_rhine_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = via_rhine_poll; +#endif if (np->drv_flags & ReqTxAlign) dev->features |= NETIF_F_SG|NETIF_F_HW_CSUM; @@ -834,6 +847,8 @@ netif_carrier_on(dev); else netif_carrier_off(dev); + + break; } } np->mii_cnt = phy_idx; @@ -867,7 +882,7 @@ #endif pci_release_regions(pdev); err_out_free_netdev: - kfree (dev); + free_netdev (dev); err_out: return -ENODEV; } @@ -878,7 +893,7 @@ void *ring; dma_addr_t ring_dma; - ring = pci_alloc_consistent(np->pdev, + ring = pci_alloc_consistent(np->pdev, RX_RING_SIZE * sizeof(struct rx_desc) + TX_RING_SIZE * sizeof(struct tx_desc), &ring_dma); @@ -890,7 +905,7 @@ np->tx_bufs = pci_alloc_consistent(np->pdev, PKT_BUF_SZ * TX_RING_SIZE, &np->tx_bufs_dma); if (np->tx_bufs == NULL) { - pci_free_consistent(np->pdev, + pci_free_consistent(np->pdev, RX_RING_SIZE * sizeof(struct rx_desc) + TX_RING_SIZE * sizeof(struct tx_desc), ring, ring_dma); @@ -910,7 +925,7 @@ { struct netdev_private *np = dev->priv; - pci_free_consistent(np->pdev, + pci_free_consistent(np->pdev, RX_RING_SIZE * sizeof(struct rx_desc) + TX_RING_SIZE * sizeof(struct tx_desc), np->rx_ring, np->rx_ring_dma); @@ -935,7 +950,7 @@ np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32); np->rx_head_desc = &np->rx_ring[0]; next = np->rx_ring_dma; - + /* Init the ring entries */ for (i = 0; i < RX_RING_SIZE; i++) { np->rx_ring[i].rx_status = 0; @@ -1138,7 +1153,7 @@ if (debug > 1) printk(KERN_DEBUG "%s: via_rhine_open() irq %d.\n", dev->name, np->pdev->irq); - + i = alloc_ring(dev); if (i) return i; @@ -1253,7 +1268,7 @@ /* Reinitialize the hardware. */ wait_for_reset(dev, np->chip_id, dev->name); init_registers(dev); - + spin_unlock(&np->lock); enable_irq(np->pdev->irq); @@ -1303,7 +1318,7 @@ np->tx_ring[entry].addr = cpu_to_le32(np->tx_skbuff_dma[entry]); } - np->tx_ring[entry].desc_length = + np->tx_ring[entry].desc_length = cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); /* lock eth irq */ @@ -1351,7 +1366,7 @@ int handled = 0; ioaddr = dev->base_addr; - + while ((intr_status = get_intr_status(dev))) { handled = 1; @@ -1569,7 +1584,7 @@ break; /* Better luck next round. */ skb->dev = dev; /* Mark as being used by this device. */ np->rx_skbuff_dma[entry] = - pci_map_single(np->pdev, skb->tail, np->rx_buf_sz, + pci_map_single(np->pdev, skb->tail, np->rx_buf_sz, PCI_DMA_FROMDEVICE); np->rx_ring[entry].addr = cpu_to_le32(np->rx_skbuff_dma[entry]); } @@ -1877,7 +1892,7 @@ static void __devexit via_rhine_remove_one (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - + unregister_netdev(dev); pci_release_regions(pdev); @@ -1886,7 +1901,7 @@ iounmap((char *)(dev->base_addr)); #endif - kfree(dev); + free_netdev(dev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); } diff -urN linux-2.4.26/drivers/net/wan/farsync.c linux-2.4.27/drivers/net/wan/farsync.c --- linux-2.4.26/drivers/net/wan/farsync.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/net/wan/farsync.c 2004-08-07 16:26:05.168367898 -0700 @@ -1,9 +1,9 @@ /* - * FarSync X21 driver for Linux (generic HDLC version) + * FarSync WAN driver for Linux (2.4.x kernel version) * * Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards * - * Copyright (C) 2001 FarSite Communications Ltd. + * Copyright (C) 2001-2004 FarSite Communications Ltd. * www.farsite.co.uk * * This program is free software; you can redistribute it and/or @@ -11,66 +11,81 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: R.J.Dunlop + * Author: R.J.Dunlop + * Maintainer: Kevin Curtis */ #include #include +#include #include #include #include #include #include #include +#include #include "farsync.h" - /* * Module info */ MODULE_AUTHOR("R.J.Dunlop "); -MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd."); +MODULE_DESCRIPTION("FarSync T-Series WAN driver. FarSite Communications Ltd."); +MODULE_PARM(fst_txq_low, "i"); +MODULE_PARM(fst_txq_high, "i"); +MODULE_PARM(fst_max_reads, "i"); +MODULE_PARM(fst_excluded_cards, "i"); +MODULE_PARM(fst_excluded_list, "0-32i"); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; - /* Driver configuration and global parameters * ========================================== */ -/* Number of ports (per card) supported +/* Number of ports (per card) and cards supported */ #define FST_MAX_PORTS 4 - - -/* PCI vendor and device IDs - */ -#define FSC_PCI_VENDOR_ID 0x1619 /* FarSite Communications Ltd */ -#define T2P_PCI_DEVICE_ID 0x0400 /* T2P X21 2 port card */ -#define T4P_PCI_DEVICE_ID 0x0440 /* T4P X21 4 port card */ - +#define FST_MAX_CARDS 32 /* Default parameters for the link */ -#define FST_TX_QUEUE_LEN 100 /* At 8Mbps a longer queue length is - * useful, the syncppp module forces - * this down assuming a slower line I - * guess. - */ -#define FST_MAX_MTU 8000 /* Huge but possible */ -#define FST_DEF_MTU 1500 /* Common sane value */ +#define FST_TX_QUEUE_LEN 100 /* At 8Mbps a longer queue length is + * useful, the syncppp module forces + * this down assuming a slower line I + * guess. + */ +#define FST_TXQ_DEPTH 16 /* This one is for the buffering + * of frames on the way down to the card + * so that we can keep the card busy + * and maximise throughput + */ +#define FST_HIGH_WATER_MARK 12 /* Point at which we flow control + * network layer */ +#define FST_LOW_WATER_MARK 8 /* Point at which we remove flow + * control from network layer */ +#define FST_MAX_MTU 8000 /* Huge but possible */ +#define FST_DEF_MTU 1500 /* Common sane value */ #define FST_TX_TIMEOUT (2*HZ) - #ifdef ARPHRD_RAWHDLC -#define ARPHRD_MYTYPE ARPHRD_RAWHDLC /* Raw frames */ +#define ARPHRD_MYTYPE ARPHRD_RAWHDLC /* Raw frames */ #else -#define ARPHRD_MYTYPE ARPHRD_HDLC /* Cisco-HDLC (keepalives etc) */ +#define ARPHRD_MYTYPE ARPHRD_HDLC /* Cisco-HDLC (keepalives etc) */ #endif +/* + * Modules parameters and associated varaibles + */ +int fst_txq_low = FST_LOW_WATER_MARK; +int fst_txq_high = FST_HIGH_WATER_MARK; +int fst_max_reads = 7; +int fst_excluded_cards = 0; +int fst_excluded_list[FST_MAX_CARDS]; /* Card shared memory layout * ========================= @@ -87,58 +102,57 @@ * be used to check that we have not got out of step with the firmware * contained in the .CDE files. */ -#define SMC_VERSION 11 +#define SMC_VERSION 24 -#define FST_MEMSIZE 0x100000 /* Size of card memory (1Mb) */ +#define FST_MEMSIZE 0x100000 /* Size of card memory (1Mb) */ -#define SMC_BASE 0x00002000L /* Base offset of the shared memory window main - * configuration structure */ -#define BFM_BASE 0x00010000L /* Base offset of the shared memory window DMA - * buffers */ +#define SMC_BASE 0x00002000L /* Base offset of the shared memory window main + * configuration structure */ +#define BFM_BASE 0x00010000L /* Base offset of the shared memory window DMA + * buffers */ -#define LEN_TX_BUFFER 8192 /* Size of packet buffers */ +#define LEN_TX_BUFFER 8192 /* Size of packet buffers */ #define LEN_RX_BUFFER 8192 -#define LEN_SMALL_TX_BUFFER 256 /* Size of obsolete buffs used for DOS diags */ +#define LEN_SMALL_TX_BUFFER 256 /* Size of obsolete buffs used for DOS diags */ #define LEN_SMALL_RX_BUFFER 256 -#define NUM_TX_BUFFER 2 /* Must be power of 2. Fixed by firmware */ +#define NUM_TX_BUFFER 2 /* Must be power of 2. Fixed by firmware */ #define NUM_RX_BUFFER 8 /* Interrupt retry time in milliseconds */ #define INT_RETRY_TIME 2 - /* The Am186CH/CC processors support a SmartDMA mode using circular pools * of buffer descriptors. The structure is almost identical to that used * in the LANCE Ethernet controllers. Details available as PDF from the * AMD web site: http://www.amd.com/products/epd/processors/\ * 2.16bitcont/3.am186cxfa/a21914/21914.pdf */ -struct txdesc { /* Transmit descriptor */ - volatile u16 ladr; /* Low order address of packet. This is a - * linear address in the Am186 memory space - */ - volatile u8 hadr; /* High order address. Low 4 bits only, high 4 - * bits must be zero - */ - volatile u8 bits; /* Status and config */ - volatile u16 bcnt; /* 2s complement of packet size in low 15 bits. - * Transmit terminal count interrupt enable in - * top bit. - */ - u16 unused; /* Not used in Tx */ +struct txdesc { /* Transmit descriptor */ + volatile u16 ladr; /* Low order address of packet. This is a + * linear address in the Am186 memory space + */ + volatile u8 hadr; /* High order address. Low 4 bits only, high 4 + * bits must be zero + */ + volatile u8 bits; /* Status and config */ + volatile u16 bcnt; /* 2s complement of packet size in low 15 bits. + * Transmit terminal count interrupt enable in + * top bit. + */ + u16 unused; /* Not used in Tx */ }; -struct rxdesc { /* Receive descriptor */ - volatile u16 ladr; /* Low order address of packet */ - volatile u8 hadr; /* High order address */ - volatile u8 bits; /* Status and config */ - volatile u16 bcnt; /* 2s complement of buffer size in low 15 bits. - * Receive terminal count interrupt enable in - * top bit. - */ - volatile u16 mcnt; /* Message byte count (15 bits) */ +struct rxdesc { /* Receive descriptor */ + volatile u16 ladr; /* Low order address of packet */ + volatile u8 hadr; /* High order address */ + volatile u8 bits; /* Status and config */ + volatile u16 bcnt; /* 2s complement of buffer size in low 15 bits. + * Receive terminal count interrupt enable in + * top bit. + */ + volatile u16 mcnt; /* Message byte count (15 bits) */ }; /* Convert a length into the 15 bit 2's complement */ @@ -149,57 +163,99 @@ #define cnv_bcnt(len) (-(len)) /* Status and config bits for the above */ -#define DMA_OWN 0x80 /* SmartDMA owns the descriptor */ -#define TX_STP 0x02 /* Tx: start of packet */ -#define TX_ENP 0x01 /* Tx: end of packet */ -#define RX_ERR 0x40 /* Rx: error (OR of next 4 bits) */ -#define RX_FRAM 0x20 /* Rx: framing error */ -#define RX_OFLO 0x10 /* Rx: overflow error */ -#define RX_CRC 0x08 /* Rx: CRC error */ -#define RX_HBUF 0x04 /* Rx: buffer error */ -#define RX_STP 0x02 /* Rx: start of packet */ -#define RX_ENP 0x01 /* Rx: end of packet */ - +#define DMA_OWN 0x80 /* SmartDMA owns the descriptor */ +#define TX_STP 0x02 /* Tx: start of packet */ +#define TX_ENP 0x01 /* Tx: end of packet */ +#define RX_ERR 0x40 /* Rx: error (OR of next 4 bits) */ +#define RX_FRAM 0x20 /* Rx: framing error */ +#define RX_OFLO 0x10 /* Rx: overflow error */ +#define RX_CRC 0x08 /* Rx: CRC error */ +#define RX_HBUF 0x04 /* Rx: buffer error */ +#define RX_STP 0x02 /* Rx: start of packet */ +#define RX_ENP 0x01 /* Rx: end of packet */ -/* Interrupts from the card are caused by various events and these are presented +/* Interrupts from the card are caused by various events which are presented * in a circular buffer as several events may be processed on one physical int */ #define MAX_CIRBUFF 32 struct cirbuff { - u8 rdindex; /* read, then increment and wrap */ - u8 wrindex; /* write, then increment and wrap */ - u8 evntbuff[MAX_CIRBUFF]; + u8 rdindex; /* read, then increment and wrap */ + u8 wrindex; /* write, then increment and wrap */ + u8 evntbuff[MAX_CIRBUFF]; }; /* Interrupt event codes. * Where appropriate the two low order bits indicate the port number */ -#define CTLA_CHG 0x18 /* Control signal changed */ +#define CTLA_CHG 0x18 /* Control signal changed */ #define CTLB_CHG 0x19 #define CTLC_CHG 0x1A #define CTLD_CHG 0x1B -#define INIT_CPLT 0x20 /* Initialisation complete */ -#define INIT_FAIL 0x21 /* Initialisation failed */ +#define INIT_CPLT 0x20 /* Initialisation complete */ +#define INIT_FAIL 0x21 /* Initialisation failed */ -#define ABTA_SENT 0x24 /* Abort sent */ +#define ABTA_SENT 0x24 /* Abort sent */ #define ABTB_SENT 0x25 #define ABTC_SENT 0x26 #define ABTD_SENT 0x27 -#define TXA_UNDF 0x28 /* Transmission underflow */ +#define TXA_UNDF 0x28 /* Transmission underflow */ #define TXB_UNDF 0x29 #define TXC_UNDF 0x2A #define TXD_UNDF 0x2B +#define F56_INT 0x2C +#define M32_INT 0x2D + +#define TE1_ALMA 0x30 /* Port physical configuration. See farsync.h for field values */ struct port_cfg { - u16 lineInterface; /* Physical interface type */ - u8 x25op; /* Unused at present */ - u8 internalClock; /* 1 => internal clock, 0 => external */ - u32 lineSpeed; /* Speed in bps */ + u16 lineInterface; /* Physical interface type */ + u8 x25op; /* Unused at present */ + u8 internalClock; /* 1 => internal clock, 0 => external */ + u8 transparentMode; /* 1 => on, 0 => off */ + u8 invertClock; /* 0 => normal, 1 => inverted */ + u8 padBytes[6]; /* Padding */ + u32 lineSpeed; /* Speed in bps */ +}; + +/* TE1 port physical configuration */ +struct su_config { + u32 dataRate; + u8 clocking; + u8 framing; + u8 structure; + u8 interface; + u8 coding; + u8 lineBuildOut; + u8 equalizer; + u8 transparentMode; + u8 loopMode; + u8 range; + u8 txBufferMode; + u8 rxBufferMode; + u8 startingSlot; + u8 losThreshold; + u8 enableIdleCode; + u8 idleCode; + u8 spare[44]; +}; + +/* TE1 Status */ +struct su_status { + u32 receiveBufferDelay; + u32 framingErrorCount; + u32 codeViolationCount; + u32 crcErrorCount; + u32 lineAttenuation; + u8 portStarted; + u8 lossOfSignal; + u8 receiveRemoteAlarm; + u8 alarmIndicationSignal; + u8 spare[40]; }; /* Finally sling all the above together into the shared memory structure. @@ -209,114 +265,151 @@ * See farsync.h for some field values. */ struct fst_shared { - /* DMA descriptor rings */ - struct rxdesc rxDescrRing[FST_MAX_PORTS][NUM_RX_BUFFER]; - struct txdesc txDescrRing[FST_MAX_PORTS][NUM_TX_BUFFER]; + /* DMA descriptor rings */ + struct rxdesc rxDescrRing[FST_MAX_PORTS][NUM_RX_BUFFER]; + struct txdesc txDescrRing[FST_MAX_PORTS][NUM_TX_BUFFER]; + + /* Obsolete small buffers */ + u8 smallRxBuffer[FST_MAX_PORTS][NUM_RX_BUFFER][LEN_SMALL_RX_BUFFER]; + u8 smallTxBuffer[FST_MAX_PORTS][NUM_TX_BUFFER][LEN_SMALL_TX_BUFFER]; - /* Obsolete small buffers */ - u8 smallRxBuffer[FST_MAX_PORTS][NUM_RX_BUFFER][LEN_SMALL_RX_BUFFER]; - u8 smallTxBuffer[FST_MAX_PORTS][NUM_TX_BUFFER][LEN_SMALL_TX_BUFFER]; + u8 taskStatus; /* 0x00 => initialising, 0x01 => running, + * 0xFF => halted + */ - u8 taskStatus; /* 0x00 => initialising, 0x01 => running, - * 0xFF => halted - */ + u8 interruptHandshake; /* Set to 0x01 by adapter to signal interrupt, + * set to 0xEE by host to acknowledge interrupt + */ - u8 interruptHandshake; /* Set to 0x01 by adapter to signal interrupt, - * set to 0xEE by host to acknowledge interrupt - */ + u16 smcVersion; /* Must match SMC_VERSION */ - u16 smcVersion; /* Must match SMC_VERSION */ + u32 smcFirmwareVersion; /* 0xIIVVRRBB where II = product ID, VV = major + * version, RR = revision and BB = build + */ - u32 smcFirmwareVersion; /* 0xIIVVRRBB where II = product ID, VV = major - * version, RR = revision and BB = build - */ + u16 txa_done; /* Obsolete completion flags */ + u16 rxa_done; + u16 txb_done; + u16 rxb_done; + u16 txc_done; + u16 rxc_done; + u16 txd_done; + u16 rxd_done; - u16 txa_done; /* Obsolete completion flags */ - u16 rxa_done; - u16 txb_done; - u16 rxb_done; - u16 txc_done; - u16 rxc_done; - u16 txd_done; - u16 rxd_done; + u16 mailbox[4]; /* Diagnostics mailbox. Not used */ - u16 mailbox[4]; /* Diagnostics mailbox. Not used */ + struct cirbuff interruptEvent; /* interrupt causes */ - struct cirbuff interruptEvent; /* interrupt causes */ + u32 v24IpSts[FST_MAX_PORTS]; /* V.24 control input status */ + u32 v24OpSts[FST_MAX_PORTS]; /* V.24 control output status */ - u32 v24IpSts[FST_MAX_PORTS]; /* V.24 control input status */ - u32 v24OpSts[FST_MAX_PORTS]; /* V.24 control output status */ + struct port_cfg portConfig[FST_MAX_PORTS]; - struct port_cfg portConfig[FST_MAX_PORTS]; + u16 clockStatus[FST_MAX_PORTS]; /* lsb: 0=> present, 1=> absent */ - u16 clockStatus[FST_MAX_PORTS]; /* lsb: 0=> present, 1=> absent */ + u16 cableStatus; /* lsb: 0=> present, 1=> absent */ - u16 cableStatus; /* lsb: 0=> present, 1=> absent */ + u16 txDescrIndex[FST_MAX_PORTS]; /* transmit descriptor ring index */ + u16 rxDescrIndex[FST_MAX_PORTS]; /* receive descriptor ring index */ - u16 txDescrIndex[FST_MAX_PORTS]; /* transmit descriptor ring index */ - u16 rxDescrIndex[FST_MAX_PORTS]; /* receive descriptor ring index */ + u16 portMailbox[FST_MAX_PORTS][2]; /* command, modifier */ + u16 cardMailbox[4]; /* Not used */ - u16 portMailbox[FST_MAX_PORTS][2]; /* command, modifier */ - u16 cardMailbox[4]; /* Not used */ + /* Number of times the card thinks the host has + * missed an interrupt by not acknowledging + * within 2mS (I guess NT has problems) + */ + u32 interruptRetryCount; - /* Number of times that card thinks the host has - * missed an interrupt by not acknowledging - * within 2mS (I guess NT has problems) - */ - u32 interruptRetryCount; + /* Driver private data used as an ID. We'll not + * use this as I'd rather keep such things + * in main memory rather than on the PCI bus + */ + u32 portHandle[FST_MAX_PORTS]; - /* Driver private data used as an ID. We'll not - * use this on Linux I'd rather keep such things - * in main memory rather than on the PCI bus - */ - u32 portHandle[FST_MAX_PORTS]; + /* Count of Tx underflows for stats */ + u32 transmitBufferUnderflow[FST_MAX_PORTS]; - /* Count of Tx underflows for stats */ - u32 transmitBufferUnderflow[FST_MAX_PORTS]; + /* Debounced V.24 control input status */ + u32 v24DebouncedSts[FST_MAX_PORTS]; - /* Debounced V.24 control input status */ - u32 v24DebouncedSts[FST_MAX_PORTS]; + /* Adapter debounce timers. Don't touch */ + u32 ctsTimer[FST_MAX_PORTS]; + u32 ctsTimerRun[FST_MAX_PORTS]; + u32 dcdTimer[FST_MAX_PORTS]; + u32 dcdTimerRun[FST_MAX_PORTS]; - /* Adapter debounce timers. Don't touch */ - u32 ctsTimer[FST_MAX_PORTS]; - u32 ctsTimerRun[FST_MAX_PORTS]; - u32 dcdTimer[FST_MAX_PORTS]; - u32 dcdTimerRun[FST_MAX_PORTS]; + u32 numberOfPorts; /* Number of ports detected at startup */ - u32 numberOfPorts; /* Number of ports detected at startup */ + u16 _reserved[64]; - u16 _reserved[64]; + u16 cardMode; /* Bit-mask to enable features: + * Bit 0: 1 enables LED identify mode + */ - u16 cardMode; /* Bit-mask to enable features: - * Bit 0: 1 enables LED identify mode - */ + u16 portScheduleOffset; - u16 portScheduleOffset; + struct su_config suConfig; /* TE1 Bits */ + struct su_status suStatus; - u32 endOfSmcSignature; /* endOfSmcSignature MUST be the last member of - * the structure and marks the end of the shared - * memory. Adapter code initializes its value as - * END_SIG. - */ + u32 endOfSmcSignature; /* endOfSmcSignature MUST be the last member of + * the structure and marks the end of shared + * memory. Adapter code initializes it as + * END_SIG. + */ }; /* endOfSmcSignature value */ #define END_SIG 0x12345678 /* Mailbox values. (portMailbox) */ -#define NOP 0 /* No operation */ -#define ACK 1 /* Positive acknowledgement to PC driver */ -#define NAK 2 /* Negative acknowledgement to PC driver */ -#define STARTPORT 3 /* Start an HDLC port */ -#define STOPPORT 4 /* Stop an HDLC port */ -#define ABORTTX 5 /* Abort the transmitter for a port */ -#define SETV24O 6 /* Set V24 outputs */ +#define NOP 0 /* No operation */ +#define ACK 1 /* Positive acknowledgement to PC driver */ +#define NAK 2 /* Negative acknowledgement to PC driver */ +#define STARTPORT 3 /* Start an HDLC port */ +#define STOPPORT 4 /* Stop an HDLC port */ +#define ABORTTX 5 /* Abort the transmitter for a port */ +#define SETV24O 6 /* Set V24 outputs */ + +/* PLX Chip Register Offsets */ +#define CNTRL_9052 0x50 /* Control Register */ +#define CNTRL_9054 0x6c /* Control Register */ +#define INTCSR_9052 0x4c /* Interrupt control/status register */ +#define INTCSR_9054 0x68 /* Interrupt control/status register */ + +/* 9054 DMA Registers */ +/* + * Note that we will be using DMA Channel 0 for copying rx data + * and Channel 1 for copying tx data + */ +#define DMAMODE0 0x80 +#define DMAPADR0 0x84 +#define DMALADR0 0x88 +#define DMASIZ0 0x8c +#define DMADPR0 0x90 +#define DMAMODE1 0x94 +#define DMAPADR1 0x98 +#define DMALADR1 0x9c +#define DMASIZ1 0xa0 +#define DMADPR1 0xa4 +#define DMACSR0 0xa8 +#define DMACSR1 0xa9 +#define DMAARB 0xac +#define DMATHR 0xb0 +#define DMADAC0 0xb4 +#define DMADAC1 0xb8 +#define DMAMARBR 0xac + +#define FST_MIN_DMA_LEN 64 +#define FST_RX_DMA_INT 0x01 +#define FST_TX_DMA_INT 0x02 +#define FST_CARD_INT 0x04 /* Larger buffers are positioned in memory at offset BFM_BASE */ struct buf_window { - u8 txBuffer[FST_MAX_PORTS][NUM_TX_BUFFER][LEN_TX_BUFFER]; - u8 rxBuffer[FST_MAX_PORTS][NUM_RX_BUFFER][LEN_RX_BUFFER]; + u8 txBuffer[FST_MAX_PORTS][NUM_TX_BUFFER][LEN_TX_BUFFER]; + u8 rxBuffer[FST_MAX_PORTS][NUM_RX_BUFFER][LEN_RX_BUFFER]; }; /* Calculate offset of a buffer object within the shared memory window */ @@ -324,39 +417,64 @@ #pragma pack() - /* Device driver private information * ================================= */ /* Per port (line or channel) information */ struct fst_port_info { - hdlc_device hdlc; /* HDLC device struct - must be first */ - struct fst_card_info *card; /* Card we're associated with */ - int index; /* Port index on the card */ - int hwif; /* Line hardware (lineInterface copy) */ - int run; /* Port is running */ - int rxpos; /* Next Rx buffer to use */ - int txpos; /* Next Tx buffer to use */ - int txipos; /* Next Tx buffer to check for free */ - int txcnt; /* Count of Tx buffers in use */ + hdlc_device hdlc; /* HDLC device struct - must be first */ + struct fst_card_info *card; /* Card we're associated with */ + int index; /* Port index on the card */ + int hwif; /* Line hardware (lineInterface copy) */ + int run; /* Port is running */ + int mode; /* Normal or FarSync raw */ + int rxpos; /* Next Rx buffer to use */ + int txpos; /* Next Tx buffer to use */ + int txipos; /* Next Tx buffer to check for free */ + int start; /* Indication of start/stop to network */ + /* + * A sixteen entry transmit queue + */ + int txqs; /* index to get next buffer to tx */ + int txqe; /* index to queue next packet */ + struct sk_buff *txq[FST_TXQ_DEPTH]; /* The queue */ + int rxqdepth; }; /* Per card information */ struct fst_card_info { - char *mem; /* Card memory mapped to kernel space */ - char *ctlmem; /* Control memory for PCI cards */ - unsigned int phys_mem; /* Physical memory window address */ - unsigned int phys_ctlmem; /* Physical control memory address */ - unsigned int irq; /* Interrupt request line number */ - unsigned int nports; /* Number of serial ports */ - unsigned int type; /* Type index of card */ - unsigned int state; /* State of card */ - spinlock_t card_lock; /* Lock for SMP access */ - unsigned short pci_conf; /* PCI card config in I/O space */ - /* Per port info */ - struct fst_port_info ports[ FST_MAX_PORTS ]; + char *mem; /* Card memory mapped to kernel space */ + char *ctlmem; /* Control memory for PCI cards */ + unsigned int phys_mem; /* Physical memory window address */ + unsigned int phys_ctlmem; /* Physical control memory address */ + unsigned int irq; /* Interrupt request line number */ + unsigned int nports; /* Number of serial ports */ + unsigned int type; /* Type index of card */ + unsigned int state; /* State of card */ + spinlock_t card_lock; /* Lock for SMP access */ + unsigned short pci_conf; /* PCI card config in I/O space */ + /* Per port info */ + struct fst_port_info ports[FST_MAX_PORTS]; + struct pci_dev *device; /* Information about the pci device */ + int card_no; /* Inst of the card on the system */ + int family; /* TxP or TxU */ + int dmarx_in_progress; + int dmatx_in_progress; + unsigned long int_count; + unsigned long int_time_ave; + void *rx_dma_handle_host; + dma_addr_t rx_dma_handle_card; + void *tx_dma_handle_host; + dma_addr_t tx_dma_handle_card; + struct sk_buff *dma_skb_rx; + struct fst_port_info *dma_port_rx; + struct fst_port_info *dma_port_tx; + int dma_len_rx; + int dma_len_tx; + int dma_txpos; + int dma_rxpos; }; /* Convert an HDLC device pointer into a port info pointer and similar */ @@ -364,7 +482,6 @@ #define dev_to_port(D) hdlc_to_port(dev_to_hdlc(D)) #define port_to_dev(P) hdlc_to_dev(&(P)->hdlc) - /* * Shared memory window access macros * @@ -384,7 +501,6 @@ #define FST_WRW(C,E,W) writew ((W), (C)->mem + WIN_OFFSET(E)) #define FST_WRL(C,E,L) writel ((L), (C)->mem + WIN_OFFSET(E)) - /* * Debug support */ @@ -403,30 +519,148 @@ printk ( KERN_DEBUG FST_NAME ": " fmt, ## A ) #else -# define dbg(X...) /* NOP */ +#define dbg(X...) /* NOP */ #endif - /* Printing short cuts */ #define printk_err(fmt,A...) printk ( KERN_ERR FST_NAME ": " fmt, ## A ) #define printk_warn(fmt,A...) printk ( KERN_WARNING FST_NAME ": " fmt, ## A ) #define printk_info(fmt,A...) printk ( KERN_INFO FST_NAME ": " fmt, ## A ) - /* * PCI ID lookup table */ static struct pci_device_id fst_pci_dev_id[] __devinitdata = { - { FSC_PCI_VENDOR_ID, T2P_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - FST_TYPE_T2P }, - { FSC_PCI_VENDOR_ID, T4P_PCI_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, - FST_TYPE_T4P }, - { 0, } /* End */ + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2P, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T2P}, + + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4P, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T4P}, + + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T1U, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T1U}, + + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T2U, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T2U}, + + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_T4U, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_T4U}, + + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, + + {PCI_VENDOR_ID_FARSITE, PCI_DEVICE_ID_FARSITE_TE1C, PCI_ANY_ID, + PCI_ANY_ID, 0, 0, FST_TYPE_TE1}, + {0,} /* End */ }; -MODULE_DEVICE_TABLE ( pci, fst_pci_dev_id ); +MODULE_DEVICE_TABLE(pci, fst_pci_dev_id); + +/* + * Device Driver Work Queues + * + * So that we don't spend too much time processing events in the + * Interrupt Service routine, we will declare a work queue per Card + * and make the ISR schedule a task in the queue for later execution. + */ + +static void do_bottom_half_tx(struct fst_card_info *card); +static void do_bottom_half_rx(struct fst_card_info *card); +static void fst_process_tx_work_q(unsigned long work_q); +static void fst_process_int_work_q(unsigned long work_q); + +DECLARE_TASKLET(fst_tx_task, fst_process_tx_work_q, 0); +DECLARE_TASKLET(fst_int_task, fst_process_int_work_q, 0); + +struct fst_card_info *fst_card_array[FST_MAX_CARDS]; +spinlock_t fst_work_q_lock; +u64 fst_work_txq; +u64 fst_work_intq; + +static void +fst_q_work_item(u64 * queue, int card_index) +{ + unsigned long flags; + u64 mask; + /* + * Grab the queue exclusively + */ + spin_lock_irqsave(&fst_work_q_lock, flags); + + /* + * Making an entry in the queue is simply a matter of setting + * a bit for the card indicating that there is work to do in the + * bottom half for the card. Note the limitation of 64 cards. + * That ought to be enough + */ + mask = 1 << card_index; + *queue |= mask; + spin_unlock_irqrestore(&fst_work_q_lock, flags); +} + +static void +fst_process_tx_work_q(unsigned long work_q) +{ + unsigned long flags; + u64 work_txq; + int i; + + /* + * Grab the queue exclusively + */ + dbg(DBG_TX, "fst_process_tx_work_q\n"); + spin_lock_irqsave(&fst_work_q_lock, flags); + work_txq = fst_work_txq; + fst_work_txq = 0; + spin_unlock_irqrestore(&fst_work_q_lock, flags); + + /* + * Call the bottom half for each card with work waiting + */ + for (i = 0; i < FST_MAX_CARDS; i++) { + if (work_txq & 0x01) { + if (fst_card_array[i] != NULL) { + dbg(DBG_TX, "Calling tx bh for card %d\n", i); + do_bottom_half_tx(fst_card_array[i]); + } + } + work_txq = work_txq >> 1; + } +} + +static void +fst_process_int_work_q(unsigned long work_q) +{ + unsigned long flags; + u64 work_intq; + int i; + + /* + * Grab the queue exclusively + */ + dbg(DBG_INTR, "fst_process_int_work_q\n"); + spin_lock_irqsave(&fst_work_q_lock, flags); + work_intq = fst_work_intq; + fst_work_intq = 0; + spin_unlock_irqrestore(&fst_work_q_lock, flags); + + /* + * Call the bottom half for each card with work waiting + */ + for (i = 0; i < FST_MAX_CARDS; i++) { + if (work_intq & 0x01) { + if (fst_card_array[i] != NULL) { + dbg(DBG_INTR, + "Calling rx & tx bh for card %d\n", i); + do_bottom_half_rx(fst_card_array[i]); + do_bottom_half_tx(fst_card_array[i]); + } + } + work_intq = work_intq >> 1; + } +} /* Card control functions * ====================== @@ -436,1002 +670,1709 @@ * Used to be a simple write to card control space but a glitch in the latest * AMD Am186CH processor means that we now have to do it by asserting and de- * asserting the PLX chip PCI Adapter Software Reset. Bit 30 in CNTRL register - * at offset 0x50. + * at offset 9052_CNTRL. Note the updates for the TXU. */ static inline void -fst_cpureset ( struct fst_card_info *card ) +fst_cpureset(struct fst_card_info *card) { - unsigned int regval; + unsigned char interrupt_line_register; + unsigned long j = jiffies + 1; + unsigned int regval; + + if (card->family == FST_FAMILY_TXU) { + if (pci_read_config_byte + (card->device, PCI_INTERRUPT_LINE, &interrupt_line_register)) { + dbg(DBG_ASS, + "Error in reading interrupt line register\n"); + } + /* + * Assert PLX software reset and Am186 hardware reset + * and then deassert the PLX software reset but 186 still in reset + */ + outw(0x440f, card->pci_conf + CNTRL_9054 + 2); + outw(0x040f, card->pci_conf + CNTRL_9054 + 2); + /* + * We are delaying here to allow the 9054 to reset itself + */ + j = jiffies + 1; + while (jiffies < j) + /* Do nothing */ ; + outw(0x240f, card->pci_conf + CNTRL_9054 + 2); + /* + * We are delaying here to allow the 9054 to reload its eeprom + */ + j = jiffies + 1; + while (jiffies < j) + /* Do nothing */ ; + outw(0x040f, card->pci_conf + CNTRL_9054 + 2); + + if (pci_write_config_byte + (card->device, PCI_INTERRUPT_LINE, interrupt_line_register)) { + dbg(DBG_ASS, + "Error in writing interrupt line register\n"); + } - regval = inl ( card->pci_conf + 0x50 ); + } else { + regval = inl(card->pci_conf + CNTRL_9052); - outl ( regval | 0x40000000, card->pci_conf + 0x50 ); - outl ( regval & ~0x40000000, card->pci_conf + 0x50 ); + outl(regval | 0x40000000, card->pci_conf + CNTRL_9052); + outl(regval & ~0x40000000, card->pci_conf + CNTRL_9052); + } } /* Release the processor from reset */ static inline void -fst_cpurelease ( struct fst_card_info *card ) +fst_cpurelease(struct fst_card_info *card) { - (void) readb ( card->ctlmem ); + if (card->family == FST_FAMILY_TXU) { + /* + * Force posted writes to complete + */ + (void) readb(card->mem); + + /* + * Release LRESET DO = 1 + * Then release Local Hold, DO = 1 + */ + outw(0x040e, card->pci_conf + CNTRL_9054 + 2); + outw(0x040f, card->pci_conf + CNTRL_9054 + 2); + } else { + (void) readb(card->ctlmem); + } } /* Clear the cards interrupt flag */ static inline void -fst_clear_intr ( struct fst_card_info *card ) +fst_clear_intr(struct fst_card_info *card) { - /* Poke the appropriate PLX chip register (same as enabling interrupts) - */ - outw ( 0x0543, card->pci_conf + 0x4C ); + if (card->family == FST_FAMILY_TXU) { + (void) readb(card->ctlmem); + } else { + /* Poke the appropriate PLX chip register (same as enabling interrupts) + */ + outw(0x0543, card->pci_conf + INTCSR_9052); + } +} + +/* Enable card interrupts + */ +static inline void +fst_enable_intr(struct fst_card_info *card) +{ + if (card->family == FST_FAMILY_TXU) { + outl(0x0f0c0900, card->pci_conf + INTCSR_9054); + } else { + outw(0x0543, card->pci_conf + INTCSR_9052); + } } /* Disable card interrupts */ static inline void -fst_disable_intr ( struct fst_card_info *card ) +fst_disable_intr(struct fst_card_info *card) { - outw ( 0x0000, card->pci_conf + 0x4C ); + if (card->family == FST_FAMILY_TXU) { + outl(0x00000000, card->pci_conf + INTCSR_9054); + } else { + outw(0x0000, card->pci_conf + INTCSR_9052); + } } +/* Process the result of trying to pass a recieved frame up the stack + */ +static void +fst_process_rx_status(int rx_status, char *name) +{ + switch (rx_status) { + case NET_RX_SUCCESS: + { + /* + * Nothing to do here + */ + break; + } + + case NET_RX_CN_LOW: + { + dbg(DBG_ASS, "%s: Receive Low Congestion\n", name); + break; + } + + case NET_RX_CN_MOD: + { + dbg(DBG_ASS, "%s: Receive Moderate Congestion\n", name); + break; + } + + case NET_RX_CN_HIGH: + { + dbg(DBG_ASS, "%s: Receive High Congestion\n", name); + break; + } + + case NET_RX_DROP: + { + dbg(DBG_ASS, "%s: Received packet dropped\n", name); + break; + } + } +} + +/* Initilaise DMA for PLX 9054 + */ +static inline void +fst_init_dma(struct fst_card_info *card) +{ + /* + * This is only required for the PLX 9054 + */ + if (card->family == FST_FAMILY_TXU) { + pci_set_master(card->device); + outl(0x00020441, card->pci_conf + DMAMODE0); + outl(0x00020441, card->pci_conf + DMAMODE1); + outl(0x0, card->pci_conf + DMATHR); + } +} + +/* Tx dma complete interrupt + */ +static void +fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, + int len, int txpos) +{ + /* + * Everything is now set, just tell the card to go + */ + dbg(DBG_TX, "fst_tx_dma_complete\n"); + FST_WRB(card, txDescrRing[port->index][txpos].bits, + DMA_OWN | TX_STP | TX_ENP); + port->hdlc.stats.tx_packets++; + port->hdlc.stats.tx_bytes += len; + port_to_dev(port)->trans_start = jiffies; +} + +/* Rx dma complete interrupt + */ +static void +fst_rx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, + int len, struct sk_buff *skb, int rxp) +{ + + int pi; + int rx_status; + + dbg(DBG_TX, "fst_rx_dma_complete\n"); + pi = port->index; + memcpy(skb_put(skb, len), card->rx_dma_handle_host, len); + + /* Reset buffer descriptor */ + FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + + /* Update stats */ + port->hdlc.stats.rx_packets++; + port->hdlc.stats.rx_bytes += len; + + /* Push upstream */ + dbg(DBG_RX, "Pushing the frame up the stack\n"); + skb->mac.raw = skb->data; + skb->dev = hdlc_to_dev(&port->hdlc); + if (port->mode == FST_RAW) { + /* + * Mark it for our own raw sockets interface + */ + skb->protocol = htons(ETH_P_CUST); + skb->pkt_type = PACKET_HOST; + } else { + skb->protocol = hdlc_type_trans(skb, skb->dev); + } + rx_status = netif_rx(skb); + fst_process_rx_status(rx_status, port_to_dev(port)->name); + if (rx_status == NET_RX_DROP) + port->hdlc.stats.rx_dropped++; + port_to_dev(port)->last_rx = jiffies; +} + +/* + * Receive a frame through the DMA + */ +static inline void +fst_rx_dma(struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len) +{ + /* + * This routine will setup the DMA and start it + */ + + dbg(DBG_RX, "In fst_rx_dma %p %p %d\n", skb, mem, len); + if (card->dmarx_in_progress) { + dbg(DBG_ASS, "In fst_rx_dma while dma in progress\n"); + } + + outl((unsigned long) skb, card->pci_conf + DMAPADR0); /* Copy to here */ + outl((unsigned long) mem, card->pci_conf + DMALADR0); /* from here */ + outl(len, card->pci_conf + DMASIZ0); /* for this length */ + outl(0x00000000c, card->pci_conf + DMADPR0); /* In this direction */ + + /* + * We use the dmarx_in_progress flag to flag the channel as busy + */ + card->dmarx_in_progress = 1; + outb(0x03, card->pci_conf + DMACSR0); /* Start the transfer */ +} + +/* + * Send a frame through the DMA + */ +static inline void +fst_tx_dma(struct fst_card_info *card, unsigned char *skb, + unsigned char *mem, int len) +{ + /* + * This routine will setup the DMA and start it. + */ + + dbg(DBG_TX, "In fst_tx_dma %p %p %d\n", skb, mem, len); + if (card->dmatx_in_progress) { + dbg(DBG_ASS, "In fst_tx_dma while dma in progress\n"); + } + + outl((unsigned long) skb, card->pci_conf + DMAPADR1); /* Copy from here */ + outl((unsigned long) mem, card->pci_conf + DMALADR1); /* to here */ + outl(len, card->pci_conf + DMASIZ1); /* for this length */ + outl(0x000000004, card->pci_conf + DMADPR1); /* In this direction */ + + /* + * We use the dmatx_in_progress to flag the channel as busy + */ + card->dmatx_in_progress = 1; + outb(0x03, card->pci_conf + DMACSR1); /* Start the transfer */ +} /* Issue a Mailbox command for a port. * Note we issue them on a fire and forget basis, not expecting to see an * error and not waiting for completion. */ static void -fst_issue_cmd ( struct fst_port_info *port, unsigned short cmd ) +fst_issue_cmd(struct fst_port_info *port, unsigned short cmd) { - struct fst_card_info *card; - unsigned short mbval; - unsigned long flags; - int safety; - - card = port->card; - spin_lock_irqsave ( &card->card_lock, flags ); - mbval = FST_RDW ( card, portMailbox[port->index][0]); - - safety = 0; - /* Wait for any previous command to complete */ - while ( mbval > NAK ) - { - spin_unlock_irqrestore ( &card->card_lock, flags ); - schedule_timeout ( 1 ); - spin_lock_irqsave ( &card->card_lock, flags ); - - if ( ++safety > 1000 ) - { - printk_err ("Mailbox safety timeout\n"); - break; - } - - mbval = FST_RDW ( card, portMailbox[port->index][0]); - } - if ( safety > 0 ) - { - dbg ( DBG_CMD,"Mailbox clear after %d jiffies\n", safety ); - } - if ( mbval == NAK ) - { - dbg ( DBG_CMD,"issue_cmd: previous command was NAK'd\n"); - } - - FST_WRW ( card, portMailbox[port->index][0], cmd ); - - if ( cmd == ABORTTX || cmd == STARTPORT ) - { - port->txpos = 0; - port->txipos = 0; - port->txcnt = 0; - } + struct fst_card_info *card; + unsigned short mbval; + unsigned long flags; + int safety; + + card = port->card; + spin_lock_irqsave(&card->card_lock, flags); + mbval = FST_RDW(card, portMailbox[port->index][0]); + + safety = 0; + /* Wait for any previous command to complete */ + while (mbval > NAK) { + spin_unlock_irqrestore(&card->card_lock, flags); + schedule_timeout(1); + spin_lock_irqsave(&card->card_lock, flags); + + if (++safety > 2000) { + printk_err("Mailbox safety timeout\n"); + break; + } - spin_unlock_irqrestore ( &card->card_lock, flags ); -} + mbval = FST_RDW(card, portMailbox[port->index][0]); + } + if (safety > 0) { + dbg(DBG_CMD, "Mailbox clear after %d jiffies\n", safety); + } + if (mbval == NAK) { + dbg(DBG_CMD, "issue_cmd: previous command was NAK'd\n"); + } + + FST_WRW(card, portMailbox[port->index][0], cmd); + if (cmd == ABORTTX || cmd == STARTPORT) { + port->txpos = 0; + port->txipos = 0; + port->start = 0; + } + + spin_unlock_irqrestore(&card->card_lock, flags); +} /* Port output signals control */ static inline void -fst_op_raise ( struct fst_port_info *port, unsigned int outputs ) +fst_op_raise(struct fst_port_info *port, unsigned int outputs) { - outputs |= FST_RDL ( port->card, v24OpSts[port->index]); - FST_WRL ( port->card, v24OpSts[port->index], outputs ); + outputs |= FST_RDL(port->card, v24OpSts[port->index]); + FST_WRL(port->card, v24OpSts[port->index], outputs); - if ( port->run ) - fst_issue_cmd ( port, SETV24O ); + if (port->run) + fst_issue_cmd(port, SETV24O); } static inline void -fst_op_lower ( struct fst_port_info *port, unsigned int outputs ) +fst_op_lower(struct fst_port_info *port, unsigned int outputs) { - outputs = ~outputs & FST_RDL ( port->card, v24OpSts[port->index]); - FST_WRL ( port->card, v24OpSts[port->index], outputs ); + outputs = ~outputs & FST_RDL(port->card, v24OpSts[port->index]); + FST_WRL(port->card, v24OpSts[port->index], outputs); - if ( port->run ) - fst_issue_cmd ( port, SETV24O ); + if (port->run) + fst_issue_cmd(port, SETV24O); } - /* * Setup port Rx buffers */ static void -fst_rx_config ( struct fst_port_info *port ) +fst_rx_config(struct fst_port_info *port) { - int i; - int pi; - unsigned int offset; - unsigned long flags; - struct fst_card_info *card; - - pi = port->index; - card = port->card; - spin_lock_irqsave ( &card->card_lock, flags ); - for ( i = 0 ; i < NUM_RX_BUFFER ; i++ ) - { - offset = BUF_OFFSET ( rxBuffer[pi][i][0]); - - FST_WRW ( card, rxDescrRing[pi][i].ladr, (u16) offset ); - FST_WRB ( card, rxDescrRing[pi][i].hadr, (u8)( offset >> 16 )); - FST_WRW ( card, rxDescrRing[pi][i].bcnt, - cnv_bcnt ( LEN_RX_BUFFER )); - FST_WRW ( card, rxDescrRing[pi][i].mcnt, 0 ); - FST_WRB ( card, rxDescrRing[pi][i].bits, DMA_OWN ); - } - port->rxpos = 0; - spin_unlock_irqrestore ( &card->card_lock, flags ); + int i; + int pi; + unsigned int offset; + unsigned long flags; + struct fst_card_info *card; + + pi = port->index; + card = port->card; + spin_lock_irqsave(&card->card_lock, flags); + for (i = 0; i < NUM_RX_BUFFER; i++) { + offset = BUF_OFFSET(rxBuffer[pi][i][0]); + + FST_WRW(card, rxDescrRing[pi][i].ladr, (u16) offset); + FST_WRB(card, rxDescrRing[pi][i].hadr, (u8) (offset >> 16)); + FST_WRW(card, rxDescrRing[pi][i].bcnt, cnv_bcnt(LEN_RX_BUFFER)); + FST_WRW(card, rxDescrRing[pi][i].mcnt, LEN_RX_BUFFER); + FST_WRB(card, rxDescrRing[pi][i].bits, DMA_OWN); + } + port->rxpos = 0; + spin_unlock_irqrestore(&card->card_lock, flags); } - /* * Setup port Tx buffers */ static void -fst_tx_config ( struct fst_port_info *port ) +fst_tx_config(struct fst_port_info *port) { - int i; - int pi; - unsigned int offset; - unsigned long flags; - struct fst_card_info *card; - - pi = port->index; - card = port->card; - spin_lock_irqsave ( &card->card_lock, flags ); - for ( i = 0 ; i < NUM_TX_BUFFER ; i++ ) - { - offset = BUF_OFFSET ( txBuffer[pi][i][0]); - - FST_WRW ( card, txDescrRing[pi][i].ladr, (u16) offset ); - FST_WRB ( card, txDescrRing[pi][i].hadr, (u8)( offset >> 16 )); - FST_WRW ( card, txDescrRing[pi][i].bcnt, 0 ); - FST_WRB ( card, txDescrRing[pi][i].bits, 0 ); - } - port->txpos = 0; - port->txipos = 0; - port->txcnt = 0; - spin_unlock_irqrestore ( &card->card_lock, flags ); + int i; + int pi; + unsigned int offset; + unsigned long flags; + struct fst_card_info *card; + + pi = port->index; + card = port->card; + spin_lock_irqsave(&card->card_lock, flags); + for (i = 0; i < NUM_TX_BUFFER; i++) { + offset = BUF_OFFSET(txBuffer[pi][i][0]); + + FST_WRW(card, txDescrRing[pi][i].ladr, (u16) offset); + FST_WRB(card, txDescrRing[pi][i].hadr, (u8) (offset >> 16)); + FST_WRW(card, txDescrRing[pi][i].bcnt, 0); + FST_WRB(card, txDescrRing[pi][i].bits, 0); + } + port->txpos = 0; + port->txipos = 0; + port->start = 0; + spin_unlock_irqrestore(&card->card_lock, flags); } +/* TE1 Alarm change interrupt event + */ +static void +fst_intr_te1_alarm(struct fst_card_info *card, struct fst_port_info *port) +{ + u8 los; + u8 rra; + u8 ais; + + los = FST_RDB(card, suStatus.lossOfSignal); + rra = FST_RDB(card, suStatus.receiveRemoteAlarm); + ais = FST_RDB(card, suStatus.alarmIndicationSignal); + + if (los) { + /* + * Lost the link + */ + if (netif_carrier_ok(port_to_dev(port))) { + dbg(DBG_INTR, "Net carrier off\n"); + netif_carrier_off(port_to_dev(port)); + } + } else { + /* + * Link available + */ + if (!netif_carrier_ok(port_to_dev(port))) { + dbg(DBG_INTR, "Net carrier on\n"); + netif_carrier_on(port_to_dev(port)); + } + } + + if (los) + dbg(DBG_INTR, "Assert LOS Alarm\n"); + else + dbg(DBG_INTR, "De-assert LOS Alarm\n"); + if (rra) + dbg(DBG_INTR, "Assert RRA Alarm\n"); + else + dbg(DBG_INTR, "De-assert RRA Alarm\n"); + + if (ais) + dbg(DBG_INTR, "Assert AIS Alarm\n"); + else + dbg(DBG_INTR, "De-assert AIS Alarm\n"); +} /* Control signal change interrupt event */ static void -fst_intr_ctlchg ( struct fst_card_info *card, struct fst_port_info *port ) +fst_intr_ctlchg(struct fst_card_info *card, struct fst_port_info *port) { - int signals; + int signals; + + signals = FST_RDL(card, v24DebouncedSts[port->index]); - signals = FST_RDL ( card, v24DebouncedSts[port->index]); + if (signals & (((port->hwif == X21) || (port->hwif == X21D)) + ? IPSTS_INDICATE : IPSTS_DCD)) { + if (!netif_carrier_ok(port_to_dev(port))) { + dbg(DBG_INTR, "DCD active\n"); + netif_carrier_on(port_to_dev(port)); + } + } else { + if (netif_carrier_ok(port_to_dev(port))) { + dbg(DBG_INTR, "DCD lost\n"); + netif_carrier_off(port_to_dev(port)); + } + } +} - if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD )) - { - if ( ! netif_carrier_ok ( port_to_dev ( port ))) - { - dbg ( DBG_INTR,"DCD active\n"); - netif_carrier_on ( port_to_dev ( port )); - } - } - else - { - if ( netif_carrier_ok ( port_to_dev ( port ))) - { - dbg ( DBG_INTR,"DCD lost\n"); - netif_carrier_off ( port_to_dev ( port )); - } - } +/* Log Rx Errors + */ +static void +fst_log_rx_error(struct fst_card_info *card, struct fst_port_info *port, + unsigned char dmabits, int rxp, unsigned short len) +{ + /* + * Increment the appropriate error counter + */ + port->hdlc.stats.rx_errors++; + if (dmabits & RX_OFLO) { + port->hdlc.stats.rx_fifo_errors++; + dbg(DBG_ASS, "Rx fifo error on card %d port %d buffer %d\n", + card->card_no, port->index, rxp); + } + if (dmabits & RX_CRC) { + port->hdlc.stats.rx_crc_errors++; + dbg(DBG_ASS, "Rx crc error on card %d port %d\n", + card->card_no, port->index); + } + if (dmabits & RX_FRAM) { + port->hdlc.stats.rx_frame_errors++; + dbg(DBG_ASS, "Rx frame error on card %d port %d\n", + card->card_no, port->index); + } + if (dmabits == (RX_STP | RX_ENP)) { + port->hdlc.stats.rx_length_errors++; + dbg(DBG_ASS, "Rx length error (%d) on card %d port %d\n", + len, card->card_no, port->index); + } } +/* Rx Error Recovery + */ +static void +fst_recover_rx_error(struct fst_card_info *card, struct fst_port_info *port, + unsigned char dmabits, int rxp, unsigned short len) +{ + int i; + int pi; + + pi = port->index; + /* + * Discard buffer descriptors until we see the start of the + * next frame. Note that for long frames this could be in + * a subsequent interrupt. + */ + i = 0; + while ((dmabits & (DMA_OWN | RX_STP)) == 0) { + FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + rxp = (rxp+1) % NUM_RX_BUFFER; + if (++i > NUM_RX_BUFFER) { + dbg(DBG_ASS, "intr_rx: Discarding more bufs" + " than we have\n"); + break; + } + dmabits = FST_RDB(card, rxDescrRing[pi][rxp].bits); + dbg(DBG_ASS, "DMA Bits of next buffer was %x\n", dmabits); + } + dbg(DBG_ASS, "There were %d subsequent buffers in error\n", i); + + /* Discard the terminal buffer */ + if (!(dmabits & DMA_OWN)) { + FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + rxp = (rxp+1) % NUM_RX_BUFFER; + } + port->rxpos = rxp; + return; + +} /* Rx complete interrupt */ static void -fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port ) +fst_intr_rx(struct fst_card_info *card, struct fst_port_info *port) { - unsigned char dmabits; - int pi; - int rxp; - unsigned short len; - struct sk_buff *skb; - int i; - - - /* Check we have a buffer to process */ - pi = port->index; - rxp = port->rxpos; - dmabits = FST_RDB ( card, rxDescrRing[pi][rxp].bits ); - if ( dmabits & DMA_OWN ) - { - dbg ( DBG_RX | DBG_INTR,"intr_rx: No buffer port %d pos %d\n", - pi, rxp ); - return; - } - - /* Get buffer length */ - len = FST_RDW ( card, rxDescrRing[pi][rxp].mcnt ); - /* Discard the CRC */ - len -= 2; - - /* Check buffer length and for other errors. We insist on one packet - * in one buffer. This simplifies things greatly and since we've - * allocated 8K it shouldn't be a real world limitation - */ - dbg ( DBG_RX,"intr_rx: %d,%d: flags %x len %d\n", pi, rxp, dmabits, - len ); - if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 ) - { - port->hdlc.stats.rx_errors++; - - /* Update error stats and discard buffer */ - if ( dmabits & RX_OFLO ) - { - port->hdlc.stats.rx_fifo_errors++; - } - if ( dmabits & RX_CRC ) - { - port->hdlc.stats.rx_crc_errors++; - } - if ( dmabits & RX_FRAM ) - { - port->hdlc.stats.rx_frame_errors++; - } - if ( dmabits == ( RX_STP | RX_ENP )) - { - port->hdlc.stats.rx_length_errors++; - } - - /* Discard buffer descriptors until we see the end of packet - * marker - */ - i = 0; - while (( dmabits & ( DMA_OWN | RX_ENP )) == 0 ) - { - FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); - if ( ++rxp >= NUM_RX_BUFFER ) - rxp = 0; - if ( ++i > NUM_RX_BUFFER ) - { - dbg ( DBG_ASS,"intr_rx: Discarding more bufs" - " than we have\n"); - break; - } - dmabits = FST_RDB ( card, rxDescrRing[pi][rxp].bits ); - } - - /* Discard the terminal buffer */ - if ( ! ( dmabits & DMA_OWN )) - { - FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); - if ( ++rxp >= NUM_RX_BUFFER ) - rxp = 0; - } - port->rxpos = rxp; - return; - } - - /* Allocate SKB */ - if (( skb = dev_alloc_skb ( len )) == NULL ) - { - dbg ( DBG_RX,"intr_rx: can't allocate buffer\n"); - - port->hdlc.stats.rx_dropped++; - - /* Return descriptor to card */ - FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); - - if ( ++rxp >= NUM_RX_BUFFER ) - port->rxpos = 0; - else - port->rxpos = rxp; - return; - } - - memcpy_fromio ( skb_put ( skb, len ), - card->mem + BUF_OFFSET ( rxBuffer[pi][rxp][0]), - len ); - - /* Reset buffer descriptor */ - FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN ); - if ( ++rxp >= NUM_RX_BUFFER ) - port->rxpos = 0; - else - port->rxpos = rxp; - - /* Update stats */ - port->hdlc.stats.rx_packets++; - port->hdlc.stats.rx_bytes += len; - - /* Push upstream */ - skb->mac.raw = skb->data; - skb->dev = hdlc_to_dev ( &port->hdlc ); - skb->protocol = hdlc_type_trans(skb, skb->dev); - netif_rx ( skb ); + unsigned char dmabits; + int pi; + int rxp; + int rx_status; + unsigned short len; + struct sk_buff *skb; + + /* Check we have a buffer to process */ + pi = port->index; + rxp = port->rxpos; + dmabits = FST_RDB(card, rxDescrRing[pi][rxp].bits); + if (dmabits & DMA_OWN) { + dbg(DBG_RX | DBG_INTR, "intr_rx: No buffer port %d pos %d\n", + pi, rxp); + return; + } + if (card->dmarx_in_progress) { + return; + } + + /* Get buffer length */ + len = FST_RDW(card, rxDescrRing[pi][rxp].mcnt); + /* Discard the CRC */ + len -= 2; + if (len == 0) { + /* + * This seems to happen on the TE1 interface sometimes + * so throw the frame away and log the event. + */ + printk_err("Frame received with 0 length. Card %d Port %d\n", + card->card_no, port->index); + /* Return descriptor to card */ + FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + + rxp = (rxp+1) % NUM_RX_BUFFER; + port->rxpos = rxp; + return; + } - port_to_dev ( port )->last_rx = jiffies; + /* Check buffer length and for other errors. We insist on one packet + * in one buffer. This simplifies things greatly and since we've + * allocated 8K it shouldn't be a real world limitation + */ + dbg(DBG_RX, "intr_rx: %d,%d: flags %x len %d\n", pi, rxp, dmabits, len); + if (dmabits != (RX_STP | RX_ENP) || len > LEN_RX_BUFFER - 2) { + fst_log_rx_error(card, port, dmabits, rxp, len); + fst_recover_rx_error(card, port, dmabits, rxp, len); + return; + } + + /* Allocate SKB */ + if ((skb = dev_alloc_skb(len)) == NULL) { + dbg(DBG_RX, "intr_rx: can't allocate buffer\n"); + + port->hdlc.stats.rx_dropped++; + + /* Return descriptor to card */ + FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + + rxp = (rxp+1) % NUM_RX_BUFFER; + port->rxpos = rxp; + return; + } + + /* + * We know the length we need to receive, len. + * It's not worth using the DMA for reads of less than + * FST_MIN_DMA_LEN + */ + + if ((len < FST_MIN_DMA_LEN) || (card->family == FST_FAMILY_TXP)) { + memcpy_fromio(skb_put(skb, len), + card->mem + BUF_OFFSET(rxBuffer[pi][rxp][0]), + len); + + /* Reset buffer descriptor */ + FST_WRB(card, rxDescrRing[pi][rxp].bits, DMA_OWN); + + /* Update stats */ + port->hdlc.stats.rx_packets++; + port->hdlc.stats.rx_bytes += len; + + /* Push upstream */ + dbg(DBG_RX, "Pushing frame up the stack\n"); + skb->mac.raw = skb->data; + skb->dev = hdlc_to_dev(&port->hdlc); + if (port->mode == FST_RAW) { + /* + * Mark it for our own raw sockets interface + */ + skb->protocol = htons(ETH_P_CUST); + skb->pkt_type = PACKET_HOST; + } else { + skb->protocol = hdlc_type_trans(skb, skb->dev); + } + rx_status = netif_rx(skb); + fst_process_rx_status(rx_status, port_to_dev(port)->name); + if (rx_status == NET_RX_DROP) { + port->hdlc.stats.rx_dropped++; + } + port_to_dev(port)->last_rx = jiffies; + } else { + card->dma_skb_rx = skb; + card->dma_port_rx = port; + card->dma_len_rx = len; + card->dma_rxpos = rxp; + fst_rx_dma(card, (char *) card->rx_dma_handle_card, + (char *) BUF_OFFSET(rxBuffer[pi][rxp][0]), len); + } + if (rxp != port->rxpos) { + dbg(DBG_ASS, "About to increment rxpos by more than 1\n"); + dbg(DBG_ASS, "rxp = %d rxpos = %d\n", rxp, port->rxpos); + } + rxp = (rxp+1) % NUM_RX_BUFFER; + port->rxpos = rxp; } +/* + * The bottom halfs to the ISR + * + */ + +static void +do_bottom_half_tx(struct fst_card_info *card) +{ + struct fst_port_info *port; + int pi; + int txq_length; + struct sk_buff *skb; + unsigned long flags; + + /* + * Find a free buffer for the transmit + * Step through each port on this card + */ + + dbg(DBG_TX, "do_bottom_half_tx\n"); + for (pi = 0, port = card->ports; pi < card->nports; pi++, port++) { + if (!port->run) + continue; + + while (! + (FST_RDB(card, txDescrRing[pi][port->txpos].bits) & + DMA_OWN) +&& !(card->dmatx_in_progress)) { + /* + * There doesn't seem to be a txdone event per-se + * We seem to have to deduce it, by checking the DMA_OWN + * bit on the next buffer we think we can use + */ + spin_lock_irqsave(&card->card_lock, flags); + if ((txq_length = port->txqe - port->txqs) < 0) { + /* + * This is the case where one has wrapped and the + * maths gives us a negative number + */ + txq_length = txq_length + FST_TXQ_DEPTH; + } + spin_unlock_irqrestore(&card->card_lock, flags); + if (txq_length > 0) { + /* + * There is something to send + */ + spin_lock_irqsave(&card->card_lock, flags); + skb = port->txq[port->txqs]; + port->txqs++; + if (port->txqs == FST_TXQ_DEPTH) { + port->txqs = 0; + } + spin_unlock_irqrestore(&card->card_lock, flags); + /* + * copy the data and set the required indicators on the + * card. + */ + FST_WRW(card, txDescrRing[pi][port->txpos].bcnt, + cnv_bcnt(skb->len)); + if ((skb->len < FST_MIN_DMA_LEN) + || (card->family == FST_FAMILY_TXP)) { + /* Enqueue the packet with normal io */ + memcpy_toio(card->mem + + BUF_OFFSET(txBuffer[pi] + [port-> + txpos][0]), + skb->data, skb->len); + FST_WRB(card, + txDescrRing[pi][port->txpos]. + bits, + DMA_OWN | TX_STP | TX_ENP); + port->hdlc.stats.tx_packets++; + port->hdlc.stats.tx_bytes += skb->len; + port_to_dev(port)->trans_start = + jiffies; + } else { + /* Or do it through dma */ + memcpy(card->tx_dma_handle_host, + skb->data, skb->len); + card->dma_port_tx = port; + card->dma_len_tx = skb->len; + card->dma_txpos = port->txpos; + fst_tx_dma(card, + (char *) card-> + tx_dma_handle_card, + (char *) + BUF_OFFSET(txBuffer[pi] + [port->txpos][0]), + skb->len); + } + if (++port->txpos >= NUM_TX_BUFFER) + port->txpos = 0; + /* + * If we have flow control on, can we now release it? + */ + if (port->start) { + if (txq_length < fst_txq_low) { + netif_wake_queue(port_to_dev + (port)); + port->start = 0; + } + } + dev_kfree_skb(skb); + } else { + /* + * Nothing to send so break out of the while loop + */ + break; + } + } + } +} + +static void +do_bottom_half_rx(struct fst_card_info *card) +{ + struct fst_port_info *port; + int pi; + int rx_count = 0; + + /* Check for rx completions on all ports on this card */ + dbg(DBG_RX, "do_bottom_half_rx\n"); + for (pi = 0, port = card->ports; pi < card->nports; pi++, port++) { + if (!port->run) + continue; + while (!(FST_RDB(card, rxDescrRing[pi][port->rxpos].bits) + & DMA_OWN) && !(card->dmarx_in_progress)) { + if (rx_count > fst_max_reads) { + /* + * Don't spend forever in receive processing + * Schedule another event + */ + fst_q_work_item(&fst_work_intq, card->card_no); + tasklet_schedule(&fst_int_task); + break; /* Leave the loop */ + } + fst_intr_rx(card, port); + rx_count++; + } + } +} /* * The interrupt service routine * Dev_id is our fst_card_info pointer */ static void -fst_intr ( int irq, void *dev_id, struct pt_regs *regs ) +fst_intr(int irq, void *dev_id, struct pt_regs *regs) { - struct fst_card_info *card; - struct fst_port_info *port; - int rdidx; /* Event buffer indices */ - int wridx; - int event; /* Actual event for processing */ - int pi; - - if (( card = dev_id ) == NULL ) - { - dbg ( DBG_INTR,"intr: spurious %d\n", irq ); - return; - } - - dbg ( DBG_INTR,"intr: %d %p\n", irq, card ); - - spin_lock ( &card->card_lock ); - - /* Clear and reprime the interrupt source */ - fst_clear_intr ( card ); - - /* Set the software acknowledge */ - FST_WRB ( card, interruptHandshake, 0xEE ); - - /* Drain the event queue */ - rdidx = FST_RDB ( card, interruptEvent.rdindex ); - wridx = FST_RDB ( card, interruptEvent.wrindex ); - while ( rdidx != wridx ) - { - event = FST_RDB ( card, interruptEvent.evntbuff[rdidx]); - - port = &card->ports[event & 0x03]; - - dbg ( DBG_INTR,"intr: %x\n", event ); - - switch ( event ) - { - case CTLA_CHG: - case CTLB_CHG: - case CTLC_CHG: - case CTLD_CHG: - if ( port->run ) - fst_intr_ctlchg ( card, port ); - break; - - case ABTA_SENT: - case ABTB_SENT: - case ABTC_SENT: - case ABTD_SENT: - dbg ( DBG_TX,"Abort complete port %d\n", event & 0x03 ); - break; - - case TXA_UNDF: - case TXB_UNDF: - case TXC_UNDF: - case TXD_UNDF: - /* Difficult to see how we'd get this given that we - * always load up the entire packet for DMA. - */ - dbg ( DBG_TX,"Tx underflow port %d\n", event & 0x03 ); - port->hdlc.stats.tx_errors++; - port->hdlc.stats.tx_fifo_errors++; - break; - - case INIT_CPLT: - dbg ( DBG_INIT,"Card init OK intr\n"); - break; - - case INIT_FAIL: - dbg ( DBG_INIT,"Card init FAILED intr\n"); - card->state = FST_IFAILED; - break; - - default: - printk_err ("intr: unknown card event code. ignored\n"); - break; - } - - /* Bump and wrap the index */ - if ( ++rdidx >= MAX_CIRBUFF ) - rdidx = 0; - } - FST_WRB ( card, interruptEvent.rdindex, rdidx ); - - for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ ) - { - if ( ! port->run ) - continue; - - /* Check for rx completions */ - while ( ! ( FST_RDB ( card, rxDescrRing[pi][port->rxpos].bits ) - & DMA_OWN )) - { - fst_intr_rx ( card, port ); - } - - /* Check for Tx completions */ - while ( port->txcnt > 0 && ! ( FST_RDB ( card, - txDescrRing[pi][port->txipos].bits ) & DMA_OWN )) - { - --port->txcnt; - if ( ++port->txipos >= NUM_TX_BUFFER ) - port->txipos = 0; - netif_wake_queue ( port_to_dev ( port )); - } - } + struct fst_card_info *card; + struct fst_port_info *port; + int rdidx; /* Event buffer indices */ + int wridx; + int event; /* Actual event for processing */ + unsigned int dma_intcsr = 0; + unsigned int do_card_interrupt; + unsigned int int_retry_count; + + if ((card = dev_id) == NULL) { + dbg(DBG_INTR, "intr: spurious %d\n", irq); + return; + } - spin_unlock ( &card->card_lock ); -} + /* + * Check to see if the interrupt was for this card + * return if not + * Note that the call to clear the interrupt is important + */ + dbg(DBG_INTR, "intr: %d %p\n", irq, card); + if (card->state != FST_RUNNING) { + printk_err + ("Interrupt received for card %d in a non running state (%d)\n", + card->card_no, card->state); + + /* + * It is possible to really be running, i.e. we have re-loaded + * a running card + * Clear and reprime the interrupt source + */ + fst_clear_intr(card); + return; + } + + /* Clear and reprime the interrupt source */ + fst_clear_intr(card); + + /* + * Is the interrupt for this card (handshake == 1) + */ + do_card_interrupt = 0; + if (FST_RDB(card, interruptHandshake) == 1) { + do_card_interrupt += FST_CARD_INT; + /* Set the software acknowledge */ + FST_WRB(card, interruptHandshake, 0xEE); + } + if (card->family == FST_FAMILY_TXU) { + /* + * Is it a DMA Interrupt + */ + dma_intcsr = inl(card->pci_conf + INTCSR_9054); + if (dma_intcsr & 0x00200000) { + /* + * DMA Channel 0 (Rx transfer complete) + */ + dbg(DBG_RX, "DMA Rx xfer complete\n"); + outb(0x8, card->pci_conf + DMACSR0); + fst_rx_dma_complete(card, card->dma_port_rx, + card->dma_len_rx, card->dma_skb_rx, + card->dma_rxpos); + card->dmarx_in_progress = 0; + do_card_interrupt += FST_RX_DMA_INT; + } + if (dma_intcsr & 0x00400000) { + /* + * DMA Channel 1 (Tx transfer complete) + */ + dbg(DBG_TX, "DMA Tx xfer complete\n"); + outb(0x8, card->pci_conf + DMACSR1); + fst_tx_dma_complete(card, card->dma_port_tx, + card->dma_len_tx, card->dma_txpos); + card->dmatx_in_progress = 0; + do_card_interrupt += FST_TX_DMA_INT; + } + } + /* + * Have we been missing Interrupts + */ + int_retry_count = FST_RDL(card, interruptRetryCount); + if (int_retry_count) { + dbg(DBG_ASS, "Card %d int_retry_count is %d\n", + card->card_no, int_retry_count); + FST_WRL(card, interruptRetryCount, 0); + } + + if (!do_card_interrupt) { + return; + } + + /* Scehdule the bottom half of the ISR */ + fst_q_work_item(&fst_work_intq, card->card_no); + tasklet_schedule(&fst_int_task); + + /* Drain the event queue */ + rdidx = FST_RDB(card, interruptEvent.rdindex) & 0x1f; + wridx = FST_RDB(card, interruptEvent.wrindex) & 0x1f; + while (rdidx != wridx) { + event = FST_RDB(card, interruptEvent.evntbuff[rdidx]); + port = &card->ports[event & 0x03]; + + dbg(DBG_INTR, "Processing Interrupt event: %x\n", event); + + switch (event) { + case TE1_ALMA: + dbg(DBG_INTR, "TE1 Alarm intr\n"); + if (port->run) + fst_intr_te1_alarm(card, port); + break; + + case CTLA_CHG: + case CTLB_CHG: + case CTLC_CHG: + case CTLD_CHG: + if (port->run) + fst_intr_ctlchg(card, port); + break; + + case ABTA_SENT: + case ABTB_SENT: + case ABTC_SENT: + case ABTD_SENT: + dbg(DBG_TX, "Abort complete port %d\n", port->index); + break; + + case TXA_UNDF: + case TXB_UNDF: + case TXC_UNDF: + case TXD_UNDF: + /* Difficult to see how we'd get this given that we + * always load up the entire packet for DMA. + */ + dbg(DBG_TX, "Tx underflow port %d\n", port->index); + + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_fifo_errors++; + dbg(DBG_ASS, "Tx underflow on card %d port %d\n", + card->card_no, port->index); + break; + + case INIT_CPLT: + dbg(DBG_INIT, "Card init OK intr\n"); + break; + + case INIT_FAIL: + dbg(DBG_INIT, "Card init FAILED intr\n"); + card->state = FST_IFAILED; + break; + + default: + printk_err("intr: unknown card event %d. ignored\n", + event); + break; + } + + /* Bump and wrap the index */ + if (++rdidx >= MAX_CIRBUFF) + rdidx = 0; + } + FST_WRB(card, interruptEvent.rdindex, rdidx); +} /* Check that the shared memory configuration is one that we can handle * and that some basic parameters are correct */ static void -check_started_ok ( struct fst_card_info *card ) +check_started_ok(struct fst_card_info *card) { - int i; + int i; - /* Check structure version and end marker */ - if ( FST_RDW ( card, smcVersion ) != SMC_VERSION ) - { - printk_err ("Bad shared memory version %d expected %d\n", - FST_RDW ( card, smcVersion ), SMC_VERSION ); - card->state = FST_BADVERSION; - return; - } - if ( FST_RDL ( card, endOfSmcSignature ) != END_SIG ) - { - printk_err ("Missing shared memory signature\n"); - card->state = FST_BADVERSION; - return; - } - /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */ - if (( i = FST_RDB ( card, taskStatus )) == 0x01 ) - { - card->state = FST_RUNNING; - } - else if ( i == 0xFF ) - { - printk_err ("Firmware initialisation failed. Card halted\n"); - card->state = FST_HALTED; - return; - } - else if ( i != 0x00 ) - { - printk_err ("Unknown firmware status 0x%x\n", i ); - card->state = FST_HALTED; - return; - } - - /* Finally check the number of ports reported by firmware against the - * number we assumed at card detection. Should never happen with - * existing firmware etc so we just report it for the moment. - */ - if ( FST_RDL ( card, numberOfPorts ) != card->nports ) - { - printk_warn ("Port count mismatch." - " Firmware thinks %d we say %d\n", - FST_RDL ( card, numberOfPorts ), card->nports ); - } -} + /* Check structure version and end marker */ + if (FST_RDW(card, smcVersion) != SMC_VERSION) { + printk_err("Bad shared memory version %d expected %d\n", + FST_RDW(card, smcVersion), SMC_VERSION); + card->state = FST_BADVERSION; + return; + } + if (FST_RDL(card, endOfSmcSignature) != END_SIG) { + printk_err("Missing shared memory signature\n"); + card->state = FST_BADVERSION; + return; + } + /* Firmware status flag, 0x00 = initialising, 0x01 = OK, 0xFF = fail */ + if ((i = FST_RDB(card, taskStatus)) == 0x01) { + card->state = FST_RUNNING; + } else if (i == 0xFF) { + printk_err("Firmware initialisation failed. Card halted\n"); + card->state = FST_HALTED; + return; + } else if (i != 0x00) { + printk_err("Unknown firmware status 0x%x\n", i); + card->state = FST_HALTED; + return; + } + /* Finally check the number of ports reported by firmware against the + * number we assumed at card detection. Should never happen with + * existing firmware etc so we just report it for the moment. + */ + if (FST_RDL(card, numberOfPorts) != card->nports) { + printk_warn("Port count mismatch on card %d." + " Firmware thinks %d we say %d\n", card->card_no, + FST_RDL(card, numberOfPorts), card->nports); + } +} static int -set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port, - struct fstioc_info *info ) +set_conf_from_info(struct fst_card_info *card, struct fst_port_info *port, + struct fstioc_info *info) { - int err; + int err; + unsigned char my_framing; - /* Set things according to the user set valid flags. - * Several of the old options have been invalidated/replaced by the - * generic HDLC package. - */ - err = 0; - if ( info->valid & FSTVAL_PROTO ) - err = -EINVAL; - if ( info->valid & FSTVAL_CABLE ) - err = -EINVAL; - if ( info->valid & FSTVAL_SPEED ) - err = -EINVAL; + /* Set things according to the user set valid flags + * Several of the old options have been invalidated/replaced by the + * generic hdlc package. + */ + err = 0; + if (info->valid & FSTVAL_PROTO) { + if (info->proto == FST_RAW) + port->mode = FST_RAW; + else + port->mode = FST_GEN_HDLC; + } + + if (info->valid & FSTVAL_CABLE) + err = -EINVAL; - if ( info->valid & FSTVAL_MODE ) - FST_WRW ( card, cardMode, info->cardMode ); + if (info->valid & FSTVAL_SPEED) + err = -EINVAL; + + if (info->valid & FSTVAL_PHASE) + FST_WRB(card, portConfig[port->index].invertClock, + info->invertClock); + if (info->valid & FSTVAL_MODE) + FST_WRW(card, cardMode, info->cardMode); + if (info->valid & FSTVAL_TE1) { + FST_WRL(card, suConfig.dataRate, info->lineSpeed); + FST_WRB(card, suConfig.clocking, info->clockSource); + my_framing = FRAMING_E1; + if (info->framing == E1) + my_framing = FRAMING_E1; + if (info->framing == T1) + my_framing = FRAMING_T1; + if (info->framing == J1) + my_framing = FRAMING_J1; + FST_WRB(card, suConfig.framing, my_framing); + FST_WRB(card, suConfig.structure, info->structure); + FST_WRB(card, suConfig.interface, info->interface); + FST_WRB(card, suConfig.coding, info->coding); + FST_WRB(card, suConfig.lineBuildOut, info->lineBuildOut); + FST_WRB(card, suConfig.equalizer, info->equalizer); + FST_WRB(card, suConfig.transparentMode, info->transparentMode); + FST_WRB(card, suConfig.loopMode, info->loopMode); + FST_WRB(card, suConfig.range, info->range); + FST_WRB(card, suConfig.txBufferMode, info->txBufferMode); + FST_WRB(card, suConfig.rxBufferMode, info->rxBufferMode); + FST_WRB(card, suConfig.startingSlot, info->startingSlot); + FST_WRB(card, suConfig.losThreshold, info->losThreshold); + if (info->idleCode) + FST_WRB(card, suConfig.enableIdleCode, 1); + else + FST_WRB(card, suConfig.enableIdleCode, 0); + FST_WRB(card, suConfig.idleCode, info->idleCode); #if FST_DEBUG - if ( info->valid & FSTVAL_DEBUG ) - fst_debug_mask = info->debug; + if (info->valid & FSTVAL_TE1) { + printk("Setting TE1 data\n"); + printk("Line Speed = %d\n", info->lineSpeed); + printk("Start slot = %d\n", info->startingSlot); + printk("Clock source = %d\n", info->clockSource); + printk("Framing = %d\n", my_framing); + printk("Structure = %d\n", info->structure); + printk("interface = %d\n", info->interface); + printk("Coding = %d\n", info->coding); + printk("Line build out = %d\n", info->lineBuildOut); + printk("Equaliser = %d\n", info->equalizer); + printk("Transparent mode = %d\n", + info->transparentMode); + printk("Loop mode = %d\n", info->loopMode); + printk("Range = %d\n", info->range); + printk("Tx Buffer mode = %d\n", info->txBufferMode); + printk("Rx Buffer mode = %d\n", info->rxBufferMode); + printk("LOS Threshold = %d\n", info->losThreshold); + printk("Idle Code = %d\n", info->idleCode); + } +#endif + } +#if FST_DEBUG + if (info->valid & FSTVAL_DEBUG) { + fst_debug_mask = info->debug; + } #endif - return err; + return err; } static void -gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port, - struct fstioc_info *info ) +gather_conf_info(struct fst_card_info *card, struct fst_port_info *port, + struct fstioc_info *info) { - int i; + int i; - memset ( info, 0, sizeof ( struct fstioc_info )); + memset(info, 0, sizeof (struct fstioc_info)); - i = port->index; - info->nports = card->nports; - info->type = card->type; - info->state = card->state; - info->proto = FST_GEN_HDLC; - info->index = i; + i = port->index; + info->kernelVersion = LINUX_VERSION_CODE; + info->nports = card->nports; + info->type = card->type; + info->state = card->state; + info->proto = FST_GEN_HDLC; + info->index = i; #if FST_DEBUG - info->debug = fst_debug_mask; + info->debug = fst_debug_mask; #endif - /* Only mark information as valid if card is running. - * Copy the data anyway in case it is useful for diagnostics - */ - info->valid - = (( card->state == FST_RUNNING ) ? FSTVAL_ALL : FSTVAL_CARD ) + /* Only mark information as valid if card is running. + * Copy the data anyway in case it is useful for diagnostics + */ + info->valid = ((card->state == FST_RUNNING) ? FSTVAL_ALL : FSTVAL_CARD) #if FST_DEBUG - | FSTVAL_DEBUG + | FSTVAL_DEBUG #endif - ; + ; - info->lineInterface = FST_RDW ( card, portConfig[i].lineInterface ); - info->internalClock = FST_RDB ( card, portConfig[i].internalClock ); - info->lineSpeed = FST_RDL ( card, portConfig[i].lineSpeed ); - info->v24IpSts = FST_RDL ( card, v24IpSts[i] ); - info->v24OpSts = FST_RDL ( card, v24OpSts[i] ); - info->clockStatus = FST_RDW ( card, clockStatus[i] ); - info->cableStatus = FST_RDW ( card, cableStatus ); - info->cardMode = FST_RDW ( card, cardMode ); - info->smcFirmwareVersion = FST_RDL ( card, smcFirmwareVersion ); + info->lineInterface = FST_RDW(card, portConfig[i].lineInterface); + info->internalClock = FST_RDB(card, portConfig[i].internalClock); + info->lineSpeed = FST_RDL(card, portConfig[i].lineSpeed); + info->invertClock = FST_RDB(card, portConfig[i].invertClock); + info->v24IpSts = FST_RDL(card, v24IpSts[i]); + info->v24OpSts = FST_RDL(card, v24OpSts[i]); + info->clockStatus = FST_RDW(card, clockStatus[i]); + info->cableStatus = FST_RDW(card, cableStatus); + info->cardMode = FST_RDW(card, cardMode); + info->smcFirmwareVersion = FST_RDL(card, smcFirmwareVersion); + + /* + * The T2U can report cable presence for both A or B + * in bits 0 and 1 of cableStatus. See which port we are and + * do the mapping. + */ + if (card->family == FST_FAMILY_TXU) { + if (port->index == 0) { + /* + * Port A + */ + info->cableStatus = info->cableStatus & 1; + } else { + /* + * Port B + */ + info->cableStatus = info->cableStatus >> 1; + info->cableStatus = info->cableStatus & 1; + } + } + /* + * Some additional bits if we are TE1 + */ + if (card->type == FST_TYPE_TE1) { + info->lineSpeed = FST_RDL(card, suConfig.dataRate); + info->clockSource = FST_RDB(card, suConfig.clocking); + info->framing = FST_RDB(card, suConfig.framing); + info->structure = FST_RDB(card, suConfig.structure); + info->interface = FST_RDB(card, suConfig.interface); + info->coding = FST_RDB(card, suConfig.coding); + info->lineBuildOut = FST_RDB(card, suConfig.lineBuildOut); + info->equalizer = FST_RDB(card, suConfig.equalizer); + info->loopMode = FST_RDB(card, suConfig.loopMode); + info->range = FST_RDB(card, suConfig.range); + info->txBufferMode = FST_RDB(card, suConfig.txBufferMode); + info->rxBufferMode = FST_RDB(card, suConfig.rxBufferMode); + info->startingSlot = FST_RDB(card, suConfig.startingSlot); + info->losThreshold = FST_RDB(card, suConfig.losThreshold); + if (FST_RDB(card, suConfig.enableIdleCode)) + info->idleCode = FST_RDB(card, suConfig.idleCode); + else + info->idleCode = 0; + info->receiveBufferDelay = + FST_RDL(card, suStatus.receiveBufferDelay); + info->framingErrorCount = + FST_RDL(card, suStatus.framingErrorCount); + info->codeViolationCount = + FST_RDL(card, suStatus.codeViolationCount); + info->crcErrorCount = FST_RDL(card, suStatus.crcErrorCount); + info->lineAttenuation = FST_RDL(card, suStatus.lineAttenuation); + info->lossOfSignal = FST_RDB(card, suStatus.lossOfSignal); + info->receiveRemoteAlarm = + FST_RDB(card, suStatus.receiveRemoteAlarm); + info->alarmIndicationSignal = + FST_RDB(card, suStatus.alarmIndicationSignal); + } } - static int -fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port, - struct ifreq *ifr ) +fst_set_iface(struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr) { - sync_serial_settings sync; - int i; + sync_serial_settings sync; + int i; + + if (ifr->ifr_settings.size != sizeof (sync)) { + return -ENOMEM; + } + + if (copy_from_user + (&sync, ifr->ifr_settings.ifs_ifsu.sync, sizeof (sync))) { + return -EFAULT; + } + + if (sync.loopback) + return -EINVAL; + + i = port->index; + + switch (ifr->ifr_settings.type) { + case IF_IFACE_V35: + FST_WRW(card, portConfig[i].lineInterface, V35); + port->hwif = V35; + break; + + case IF_IFACE_V24: + FST_WRW(card, portConfig[i].lineInterface, V24); + port->hwif = V24; + break; + + case IF_IFACE_X21: + FST_WRW(card, portConfig[i].lineInterface, X21); + port->hwif = X21; + break; + + case IF_IFACE_X21D: + FST_WRW(card, portConfig[i].lineInterface, X21D); + port->hwif = X21D; + break; + + case IF_IFACE_T1: + FST_WRW(card, portConfig[i].lineInterface, T1); + port->hwif = T1; + break; + + case IF_IFACE_E1: + FST_WRW(card, portConfig[i].lineInterface, E1); + port->hwif = E1; + break; + + case IF_IFACE_SYNC_SERIAL: + break; + + default: + return -EINVAL; + } - if (copy_from_user (&sync, ifr->ifr_settings.ifs_ifsu.sync, - sizeof (sync))) - return -EFAULT; - - if ( sync.loopback ) - return -EINVAL; - - i = port->index; - - switch (ifr->ifr_settings.type) - { - case IF_IFACE_V35: - FST_WRW ( card, portConfig[i].lineInterface, V35 ); - port->hwif = V35; - break; - - case IF_IFACE_V24: - FST_WRW ( card, portConfig[i].lineInterface, V24 ); - port->hwif = V24; - break; - - case IF_IFACE_X21: - FST_WRW ( card, portConfig[i].lineInterface, X21 ); - port->hwif = X21; - break; - - case IF_IFACE_SYNC_SERIAL: - break; - - default: - return -EINVAL; - } - - switch ( sync.clock_type ) - { - case CLOCK_EXT: - FST_WRB ( card, portConfig[i].internalClock, EXTCLK ); - break; - - case CLOCK_INT: - FST_WRB ( card, portConfig[i].internalClock, INTCLK ); - break; - - default: - return -EINVAL; - } - FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate ); - return 0; + switch (sync.clock_type) { + case CLOCK_EXT: + FST_WRB(card, portConfig[i].internalClock, EXTCLK); + break; + + case CLOCK_INT: + FST_WRB(card, portConfig[i].internalClock, INTCLK); + break; + + default: + return -EINVAL; + } + FST_WRL(card, portConfig[i].lineSpeed, sync.clock_rate); + return 0; } static int -fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port, - struct ifreq *ifr ) +fst_get_iface(struct fst_card_info *card, struct fst_port_info *port, + struct ifreq *ifr) { - sync_serial_settings sync; - int i; + sync_serial_settings sync; + int i; - /* First check what line type is set, we'll default to reporting X.21 - * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be - * changed - */ - switch ( port->hwif ) - { - case V35: - ifr->ifr_settings.type = IF_IFACE_V35; - break; - case V24: - ifr->ifr_settings.type = IF_IFACE_V24; - break; - case X21: - default: - ifr->ifr_settings.type = IF_IFACE_X21; - break; - } - - if (ifr->ifr_settings.size < sizeof(sync)) { - ifr->ifr_settings.size = sizeof(sync); /* data size wanted */ - return -ENOBUFS; - } - - i = port->index; - sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed ); - /* Lucky card and linux use same encoding here */ - sync.clock_type = FST_RDB ( card, portConfig[i].internalClock ); - sync.loopback = 0; - - if (copy_to_user (ifr->ifr_settings.ifs_ifsu.sync, &sync, - sizeof(sync))) - return -EFAULT; + /* First check what line type is set, we'll default to reporting X.21 + * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be + * changed + */ + switch (port->hwif) { + case E1: + ifr->ifr_settings.type = IF_IFACE_E1; + break; + case T1: + ifr->ifr_settings.type = IF_IFACE_T1; + break; + case V35: + ifr->ifr_settings.type = IF_IFACE_V35; + break; + case V24: + ifr->ifr_settings.type = IF_IFACE_V24; + break; + case X21D: + ifr->ifr_settings.type = IF_IFACE_X21D; + break; + case X21: + default: + ifr->ifr_settings.type = IF_IFACE_X21; + break; + } + if (ifr->ifr_settings.size == 0) { + return 0; /* only type requested */ + } + if (ifr->ifr_settings.size < sizeof (sync)) { + return -ENOMEM; + } - return 0; -} + i = port->index; + sync.clock_rate = FST_RDL(card, portConfig[i].lineSpeed); + /* Lucky card and linux use same encoding here */ + sync.clock_type = FST_RDB(card, portConfig[i].internalClock) == + INTCLK ? CLOCK_INT : CLOCK_EXT; + sync.loopback = 0; + + if (copy_to_user(ifr->ifr_settings.ifs_ifsu.sync, &sync, sizeof (sync))) { + return -EFAULT; + } + ifr->ifr_settings.size = sizeof (sync); + return 0; +} static int -fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) +fst_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct fst_card_info *card; - struct fst_port_info *port; - struct fstioc_write wrthdr; - struct fstioc_info info; - unsigned long flags; - - dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data ); - - port = dev_to_port ( dev ); - card = port->card; - - if ( !capable ( CAP_NET_ADMIN )) - return -EPERM; - - switch ( cmd ) - { - case FSTCPURESET: - fst_cpureset ( card ); - card->state = FST_RESET; - return 0; - - case FSTCPURELEASE: - fst_cpurelease ( card ); - card->state = FST_STARTING; - return 0; - - case FSTWRITE: /* Code write (download) */ - - /* First copy in the header with the length and offset of data - * to write - */ - if ( ifr->ifr_data == NULL ) - { - return -EINVAL; - } - if ( copy_from_user ( &wrthdr, ifr->ifr_data, - sizeof ( struct fstioc_write ))) - { - return -EFAULT; - } - - /* Sanity check the parameters. We don't support partial writes - * when going over the top - */ - if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE - || wrthdr.size + wrthdr.offset > FST_MEMSIZE ) - { - return -ENXIO; - } - - /* Now copy the data to the card. - * This will probably break on some architectures. - * I'll fix it when I have something to test on. - */ - if ( copy_from_user ( card->mem + wrthdr.offset, - ifr->ifr_data + sizeof ( struct fstioc_write ), - wrthdr.size )) - { - return -EFAULT; - } - - /* Writes to the memory of a card in the reset state constitute - * a download - */ - if ( card->state == FST_RESET ) - { - card->state = FST_DOWNLOAD; - } - return 0; - - case FSTGETCONF: - - /* If card has just been started check the shared memory config - * version and marker - */ - if ( card->state == FST_STARTING ) - { - check_started_ok ( card ); - - /* If everything checked out enable card interrupts */ - if ( card->state == FST_RUNNING ) - { - spin_lock_irqsave ( &card->card_lock, flags ); - fst_clear_intr ( card ); - FST_WRB ( card, interruptHandshake, 0xEE ); - spin_unlock_irqrestore ( &card->card_lock, - flags ); - } - } - - if ( ifr->ifr_data == NULL ) - { - return -EINVAL; - } - - gather_conf_info ( card, port, &info ); - - if ( copy_to_user ( ifr->ifr_data, &info, sizeof ( info ))) - { - return -EFAULT; - } - return 0; - - case FSTSETCONF: - - /* Most of the setting have been moved to the generic ioctls - * this just covers debug and board ident mode now - */ - if ( copy_from_user ( &info, ifr->ifr_data, sizeof ( info ))) - { - return -EFAULT; - } - - return set_conf_from_info ( card, port, &info ); - - case SIOCWANDEV: - switch (ifr->ifr_settings.type) - { - case IF_GET_IFACE: - return fst_get_iface ( card, port, ifr ); - - case IF_IFACE_SYNC_SERIAL: - case IF_IFACE_V35: - case IF_IFACE_V24: - case IF_IFACE_X21: - return fst_set_iface ( card, port, ifr ); - - default: - return hdlc_ioctl ( dev, ifr, cmd ); - } - - default: - /* Not one of ours. Pass through to HDLC package */ - return hdlc_ioctl ( dev, ifr, cmd ); - } -} - - -static void -fst_openport ( struct fst_port_info *port ) -{ - int signals; - - /* Only init things if card is actually running. This allows open to - * succeed for downloads etc. - */ - if ( port->card->state == FST_RUNNING ) - { - if ( port->run ) - { - dbg ( DBG_OPEN,"open: found port already running\n"); - - fst_issue_cmd ( port, STOPPORT ); - port->run = 0; - } - - fst_rx_config ( port ); - fst_tx_config ( port ); - fst_op_raise ( port, OPSTS_RTS | OPSTS_DTR ); - - fst_issue_cmd ( port, STARTPORT ); - port->run = 1; - - signals = FST_RDL ( port->card, v24DebouncedSts[port->index]); - if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE - : IPSTS_DCD )) - netif_carrier_on ( port_to_dev ( port )); - else - netif_carrier_off ( port_to_dev ( port )); - } -} - -static void -fst_closeport ( struct fst_port_info *port ) -{ - if ( port->card->state == FST_RUNNING ) - { - if ( port->run ) - { - port->run = 0; - fst_op_lower ( port, OPSTS_RTS | OPSTS_DTR ); - - fst_issue_cmd ( port, STOPPORT ); - } - else - { - dbg ( DBG_OPEN,"close: port not running\n"); - } - } + struct fst_card_info *card; + struct fst_port_info *port; + struct fstioc_write wrthdr; + struct fstioc_info info; + unsigned long flags; + + dbg(DBG_IOCTL, "ioctl: %x, %p\n", cmd, ifr->ifr_data); + + port = dev_to_port(dev); + card = port->card; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + switch (cmd) { + case FSTCPURESET: + fst_cpureset(card); + card->state = FST_RESET; + return 0; + + case FSTCPURELEASE: + fst_cpurelease(card); + card->state = FST_STARTING; + return 0; + + case FSTWRITE: /* Code write (download) */ + + /* First copy in the header with the length and offset of data + * to write + */ + if (ifr->ifr_data == NULL) { + return -EINVAL; + } + if (copy_from_user(&wrthdr, ifr->ifr_data, + sizeof (struct fstioc_write))) { + return -EFAULT; + } + + /* Sanity check the parameters. We don't support partial writes + * when going over the top + */ + if (wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE + || wrthdr.size + wrthdr.offset > FST_MEMSIZE) { + return -ENXIO; + } + + /* Now copy the data to the card. + * This will probably break on some architectures. + * I'll fix it when I have something to test on. + */ + if (copy_from_user(card->mem + wrthdr.offset, + ifr->ifr_data + sizeof (struct fstioc_write), + wrthdr.size)) { + return -EFAULT; + } + + /* Writes to the memory of a card in the reset state constitute + * a download + */ + if (card->state == FST_RESET) { + card->state = FST_DOWNLOAD; + } + return 0; + + case FSTGETCONF: + + /* If card has just been started check the shared memory config + * version and marker + */ + if (card->state == FST_STARTING) { + check_started_ok(card); + + /* If everything checked out enable card interrupts */ + if (card->state == FST_RUNNING) { + spin_lock_irqsave(&card->card_lock, flags); + fst_enable_intr(card); + FST_WRB(card, interruptHandshake, 0xEE); + spin_unlock_irqrestore(&card->card_lock, flags); + } + } + + if (ifr->ifr_data == NULL) { + return -EINVAL; + } + + gather_conf_info(card, port, &info); + + if (copy_to_user(ifr->ifr_data, &info, sizeof (info))) { + return -EFAULT; + } + return 0; + + case FSTSETCONF: + + /* + * Most of the settings have been moved to the generic ioctls + * this just covers debug and board ident now + */ + + if (card->state != FST_RUNNING) { + printk_err + ("Attempt to configure card %d in non-running state (%d)\n", + card->card_no, card->state); + return -EIO; + } + if (copy_from_user(&info, ifr->ifr_data, sizeof (info))) { + return -EFAULT; + } + + return set_conf_from_info(card, port, &info); + + case SIOCWANDEV: + switch (ifr->ifr_settings.type) { + case IF_GET_IFACE: + return fst_get_iface(card, port, ifr); + + case IF_IFACE_SYNC_SERIAL: + case IF_IFACE_V35: + case IF_IFACE_V24: + case IF_IFACE_X21: + case IF_IFACE_X21D: + case IF_IFACE_T1: + case IF_IFACE_E1: + return fst_set_iface(card, port, ifr); + + case IF_PROTO_RAW: + port->mode = FST_RAW; + return 0; + + case IF_GET_PROTO: + if (port->mode == FST_RAW) { + ifr->ifr_settings.type = IF_PROTO_RAW; + return 0; + } + return hdlc_ioctl(dev, ifr, cmd); + + default: + port->mode = FST_GEN_HDLC; + dbg(DBG_IOCTL, "Passing this type to hdlc %x\n", + ifr->ifr_settings.type); + return hdlc_ioctl(dev, ifr, cmd); + } + + default: + /* Not one of ours. Pass through to HDLC package */ + return hdlc_ioctl(dev, ifr, cmd); + } } - -static int -fst_open ( struct net_device *dev ) +static void +fst_openport(struct fst_port_info *port) { - int err; + int signals; + int txq_length; - err = hdlc_open ( dev_to_hdlc ( dev )); - if ( err ) - return err; + /* Only init things if card is actually running. This allows open to + * succeed for downloads etc. + */ + if (port->card->state == FST_RUNNING) { + if (port->run) { + dbg(DBG_OPEN, "open: found port already running\n"); + + fst_issue_cmd(port, STOPPORT); + port->run = 0; + } + + fst_rx_config(port); + fst_tx_config(port); + fst_op_raise(port, OPSTS_RTS | OPSTS_DTR); + + fst_issue_cmd(port, STARTPORT); + port->run = 1; + + signals = FST_RDL(port->card, v24DebouncedSts[port->index]); + if (signals & (((port->hwif == X21) || (port->hwif == X21D)) + ? IPSTS_INDICATE : IPSTS_DCD)) + netif_carrier_on(port_to_dev(port)); + else + netif_carrier_off(port_to_dev(port)); + + txq_length = port->txqe - port->txqs; + port->txqe = 0; + port->txqs = 0; + } - MOD_INC_USE_COUNT; +} - fst_openport ( dev_to_port ( dev )); - netif_wake_queue ( dev ); - return 0; +static void +fst_closeport(struct fst_port_info *port) +{ + if (port->card->state == FST_RUNNING) { + if (port->run) { + port->run = 0; + fst_op_lower(port, OPSTS_RTS | OPSTS_DTR); + + fst_issue_cmd(port, STOPPORT); + } else { + dbg(DBG_OPEN, "close: port not running\n"); + } + } } static int -fst_close ( struct net_device *dev ) +fst_open(struct net_device *dev) { - netif_stop_queue ( dev ); - fst_closeport ( dev_to_port ( dev )); - hdlc_close ( dev_to_hdlc ( dev )); - MOD_DEC_USE_COUNT; - return 0; + int err; + struct fst_port_info *port; + + port = dev_to_port(dev); + MOD_INC_USE_COUNT; + if (port->mode != FST_RAW) { + err = hdlc_open(dev_to_hdlc(dev)); + if (err) + return err; + } + + fst_openport(port); + netif_wake_queue(dev); + return 0; } static int -fst_attach ( hdlc_device *hdlc, unsigned short encoding, unsigned short parity ) +fst_close(struct net_device *dev) { - /* Setting currently fixed in FarSync card so we check and forget */ - if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT ) - return -EINVAL; - return 0; + struct fst_port_info *port; + struct fst_card_info *card; + unsigned char tx_dma_done; + unsigned char rx_dma_done; + + port = dev_to_port(dev); + card = port->card; + + tx_dma_done = inb(card->pci_conf + DMACSR1); + rx_dma_done = inb(card->pci_conf + DMACSR0); + dbg(DBG_OPEN, + "Port Close: tx_dma_in_progress = %d (%x) rx_dma_in_progress = %d (%x)\n", + card->dmatx_in_progress, tx_dma_done, card->dmarx_in_progress, + rx_dma_done); + + netif_stop_queue(dev); + fst_closeport(dev_to_port(dev)); + if (port->mode != FST_RAW) { + hdlc_close(dev_to_hdlc(dev)); + } + MOD_DEC_USE_COUNT; + return 0; } +static int +fst_attach(hdlc_device * hdlc, unsigned short encoding, unsigned short parity) +{ + /* + * Setting currently fixed in FarSync card so we check and forget + */ + if (encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT) + return -EINVAL; + return 0; +} static void -fst_tx_timeout ( struct net_device *dev ) +fst_tx_timeout(struct net_device *dev) { - struct fst_port_info *port; + struct fst_port_info *port; + struct fst_card_info *card; - dbg ( DBG_INTR | DBG_TX,"tx_timeout\n"); + port = dev_to_port(dev); + card = port->card; - port = dev_to_port ( dev ); - - port->hdlc.stats.tx_errors++; - port->hdlc.stats.tx_aborted_errors++; - - if ( port->txcnt > 0 ) - fst_issue_cmd ( port, ABORTTX ); - - dev->trans_start = jiffies; - netif_wake_queue ( dev ); + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_aborted_errors++; + dbg(DBG_ASS, "Tx timeout card %d port %d\n", + card->card_no, port->index); + fst_issue_cmd(port, ABORTTX); + + dev->trans_start = jiffies; + netif_wake_queue(dev); + port->start = 0; } - static int -fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) +fst_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct fst_card_info *card; - struct fst_port_info *port; - unsigned char dmabits; - unsigned long flags; - int pi; - int txp; - - port = dev_to_port ( dev ); - card = port->card; - - /* Drop packet with error if we don't have carrier */ - if ( ! netif_carrier_ok ( dev )) - { - dev_kfree_skb ( skb ); - port->hdlc.stats.tx_errors++; - port->hdlc.stats.tx_carrier_errors++; - return 0; - } - - /* Drop it if it's too big! MTU failure ? */ - if ( skb->len > LEN_TX_BUFFER ) - { - dbg ( DBG_TX,"Packet too large %d vs %d\n", skb->len, - LEN_TX_BUFFER ); - dev_kfree_skb ( skb ); - port->hdlc.stats.tx_errors++; - return 0; - } - - /* Check we have a buffer */ - pi = port->index; - spin_lock_irqsave ( &card->card_lock, flags ); - txp = port->txpos; - dmabits = FST_RDB ( card, txDescrRing[pi][txp].bits ); - if ( dmabits & DMA_OWN ) - { - spin_unlock_irqrestore ( &card->card_lock, flags ); - dbg ( DBG_TX,"Out of Tx buffers\n"); - dev_kfree_skb ( skb ); - port->hdlc.stats.tx_errors++; - return 0; - } - if ( ++port->txpos >= NUM_TX_BUFFER ) - port->txpos = 0; - - if ( ++port->txcnt >= NUM_TX_BUFFER ) - netif_stop_queue ( dev ); - - /* Release the card lock before we copy the data as we now have - * exclusive access to the buffer. - */ - spin_unlock_irqrestore ( &card->card_lock, flags ); - - /* Enqueue the packet */ - memcpy_toio ( card->mem + BUF_OFFSET ( txBuffer[pi][txp][0]), - skb->data, skb->len ); - FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len )); - FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP ); + struct fst_card_info *card; + struct fst_port_info *port; + unsigned long flags; + int txq_length; + + port = dev_to_port(dev); + card = port->card; + dbg(DBG_TX, "fst_start_xmit: length = %d\n", skb->len); + + /* Drop packet with error if we don't have carrier */ + if (!netif_carrier_ok(dev)) { + dev_kfree_skb(skb); + port->hdlc.stats.tx_errors++; + port->hdlc.stats.tx_carrier_errors++; + dbg(DBG_ASS, + "Tried to transmit but no carrier on card %d port %d\n", + card->card_no, port->index); + return 0; + } - port->hdlc.stats.tx_packets++; - port->hdlc.stats.tx_bytes += skb->len; + /* Drop it if it's too big! MTU failure ? */ + if (skb->len > LEN_TX_BUFFER) { + dbg(DBG_ASS, "Packet too large %d vs %d\n", skb->len, + LEN_TX_BUFFER); + dev_kfree_skb(skb); + port->hdlc.stats.tx_errors++; + return 0; + } - dev_kfree_skb ( skb ); + /* + * We are always going to queue the packet + * so that the bottom half is the only place we tx from + * Check there is room in the port txq + */ + spin_lock_irqsave(&card->card_lock, flags); + if ((txq_length = port->txqe - port->txqs) < 0) { + /* + * This is the case where the next free has wrapped but the + * last used hasn't + */ + txq_length = txq_length + FST_TXQ_DEPTH; + } + spin_unlock_irqrestore(&card->card_lock, flags); + if (txq_length > fst_txq_high) { + /* + * We have got enough buffers in the pipeline. Ask the network + * layer to stop sending frames down + */ + netif_stop_queue(dev); + port->start = 1; /* I'm using this to signal stop sent up */ + } - dev->trans_start = jiffies; - return 0; -} + if (txq_length == FST_TXQ_DEPTH - 1) { + /* + * This shouldn't have happened but such is life + */ + dev_kfree_skb(skb); + port->hdlc.stats.tx_errors++; + dbg(DBG_ASS, "Tx queue overflow card %d port %d\n", + card->card_no, port->index); + return 0; + } + /* + * queue the buffer + */ + spin_lock_irqsave(&card->card_lock, flags); + port->txq[port->txqe] = skb; + port->txqe++; + if (port->txqe == FST_TXQ_DEPTH) + port->txqe = 0; + spin_unlock_irqrestore(&card->card_lock, flags); + + /* Scehdule the bottom half which now does transmit processing */ + fst_q_work_item(&fst_work_txq, card->card_no); + tasklet_schedule(&fst_tx_task); + + return 0; +} /* * Card setup having checked hardware resources. @@ -1441,256 +2382,302 @@ * disabled. */ static char *type_strings[] __devinitdata = { - "no hardware", /* Should never be seen */ - "FarSync T2P", - "FarSync T4P" + "no hardware", /* Should never be seen */ + "FarSync T2P", + "FarSync T4P", + "FarSync T1U", + "FarSync T2U", + "FarSync T4U", + "FarSync TE1" }; static void __devinit -fst_init_card ( struct fst_card_info *card ) +fst_init_card(struct fst_card_info *card) { - int i; - int err; - struct net_device *dev; - - /* We're working on a number of ports based on the card ID. If the - * firmware detects something different later (should never happen) - * we'll have to revise it in some way then. - */ - for ( i = 0 ; i < card->nports ; i++ ) - { - card->ports[i].card = card; - card->ports[i].index = i; - card->ports[i].run = 0; - - dev = hdlc_to_dev ( &card->ports[i].hdlc ); - - /* Fill in the net device info */ - /* Since this is a PCI setup this is purely - * informational. Give them the buffer addresses - * and basic card I/O. - */ - dev->mem_start = card->phys_mem - + BUF_OFFSET ( txBuffer[i][0][0]); - dev->mem_end = card->phys_mem - + BUF_OFFSET ( txBuffer[i][NUM_TX_BUFFER][0]); - dev->rmem_start = card->phys_mem - + BUF_OFFSET ( rxBuffer[i][0][0]); - dev->rmem_end = card->phys_mem - + BUF_OFFSET ( rxBuffer[i][NUM_RX_BUFFER][0]); - dev->base_addr = card->pci_conf; - dev->irq = card->irq; - - dev->tx_queue_len = FST_TX_QUEUE_LEN; - dev->open = fst_open; - dev->stop = fst_close; - dev->do_ioctl = fst_ioctl; - dev->watchdog_timeo = FST_TX_TIMEOUT; - dev->tx_timeout = fst_tx_timeout; - card->ports[i].hdlc.attach = fst_attach; - card->ports[i].hdlc.xmit = fst_start_xmit; - - if (( err = register_hdlc_device ( &card->ports[i].hdlc )) < 0 ) - { - printk_err ("Cannot register HDLC device for port %d" - " (errno %d)\n", i, -err ); - card->nports = i; - break; - } - } - - spin_lock_init ( &card->card_lock ); - - printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n", - hdlc_to_dev(&card->ports[0].hdlc)->name, - hdlc_to_dev(&card->ports[card->nports-1].hdlc)->name, - type_strings[card->type], card->irq, card->nports ); -} + int i; + int err; + struct net_device *dev; + + /* We're working on a number of ports based on the card ID. If the + * firmware detects something different later (should never happen) + * we'll have to revise it in some way then. + */ + for (i = 0; i < card->nports; i++) { + card->ports[i].card = card; + card->ports[i].index = i; + card->ports[i].run = 0; + card->ports[i].mode = FST_GEN_HDLC; + + dev = hdlc_to_dev(&card->ports[i].hdlc); + + /* Fill in the net device info + * Since this is a PCI setup this is purely + * informational. Give them the buffer addresses + * and basic card I/O. + */ + dev->mem_start = card->phys_mem + BUF_OFFSET(txBuffer[i][0][0]); + dev->mem_end = card->phys_mem + + BUF_OFFSET(txBuffer[i][NUM_TX_BUFFER][0]); + dev->base_addr = card->pci_conf; + dev->irq = card->irq; + + dev->tx_queue_len = FST_TX_QUEUE_LEN; + dev->open = fst_open; + dev->stop = fst_close; + dev->do_ioctl = fst_ioctl; + dev->watchdog_timeo = FST_TX_TIMEOUT; + dev->tx_timeout = fst_tx_timeout; + card->ports[i].hdlc.attach = fst_attach; + card->ports[i].hdlc.xmit = fst_start_xmit; + + if ((err = register_hdlc_device(&card->ports[i].hdlc)) < 0) { + printk_err("Cannot register HDLC device for port %d" + " (errno %d)\n", i, -err); + card->nports = i; + break; + } + } + spin_lock_init(&card->card_lock); + + printk_info("%s-%s: %s IRQ%d, %d ports\n", + hdlc_to_dev(&card->ports[0].hdlc)->name, + hdlc_to_dev(&card->ports[card->nports - 1].hdlc)->name, + type_strings[card->type], card->irq, card->nports); +} /* * Initialise card when detected. * Returns 0 to indicate success, or errno otherwise. */ static int __devinit -fst_add_one ( struct pci_dev *pdev, const struct pci_device_id *ent ) +fst_add_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int firsttime_done = 0; - struct fst_card_info *card; - int err = 0; - - if ( ! firsttime_done ) - { - printk ( KERN_INFO "FarSync X21 driver " FST_USER_VERSION - " (c) 2001 FarSite Communications Ltd.\n"); - firsttime_done = 1; - } - - /* Allocate driver private data */ - card = kmalloc ( sizeof ( struct fst_card_info ), GFP_KERNEL); - if (card == NULL) - { - printk_err ("FarSync card found but insufficient memory for" - " driver storage\n"); - return -ENOMEM; - } - memset ( card, 0, sizeof ( struct fst_card_info )); - - /* Record info we need*/ - card->irq = pdev->irq; - card->pci_conf = pci_resource_start ( pdev, 1 ); - card->phys_mem = pci_resource_start ( pdev, 2 ); - card->phys_ctlmem = pci_resource_start ( pdev, 3 ); - - card->type = ent->driver_data; - card->nports = ( ent->driver_data == FST_TYPE_T2P ) ? 2 : 4; - - card->state = FST_UNINIT; - - dbg ( DBG_PCI,"type %d nports %d irq %d\n", card->type, - card->nports, card->irq ); - dbg ( DBG_PCI,"conf %04x mem %08x ctlmem %08x\n", - card->pci_conf, card->phys_mem, card->phys_ctlmem ); - - /* Check we can get access to the memory and I/O regions */ - if ( ! request_region ( card->pci_conf, 0x80,"PLX config regs")) - { - printk_err ("Unable to get config I/O @ 0x%04X\n", - card->pci_conf ); - err = -ENODEV; - goto error_free_card; - } - if ( ! request_mem_region ( card->phys_mem, FST_MEMSIZE,"Shared RAM")) - { - printk_err ("Unable to get main memory @ 0x%08X\n", - card->phys_mem ); - err = -ENODEV; - goto error_release_io; - } - if ( ! request_mem_region ( card->phys_ctlmem, 0x10,"Control memory")) - { - printk_err ("Unable to get control memory @ 0x%08X\n", - card->phys_ctlmem ); - err = -ENODEV; - goto error_release_mem; - } - - /* Try to enable the device */ - if (( err = pci_enable_device ( pdev )) != 0 ) - { - printk_err ("Failed to enable card. Err %d\n", -err ); - goto error_release_ctlmem; - } - - /* Get virtual addresses of memory regions */ - if (( card->mem = ioremap ( card->phys_mem, FST_MEMSIZE )) == NULL ) - { - printk_err ("Physical memory remap failed\n"); - err = -ENODEV; - goto error_release_ctlmem; - } - if (( card->ctlmem = ioremap ( card->phys_ctlmem, 0x10 )) == NULL ) - { - printk_err ("Control memory remap failed\n"); - err = -ENODEV; - goto error_unmap_mem; - } - dbg ( DBG_PCI,"kernel mem %p, ctlmem %p\n", card->mem, card->ctlmem); - - /* Reset the card's processor */ - fst_cpureset ( card ); - card->state = FST_RESET; - - /* Register the interrupt handler */ - if ( request_irq ( card->irq, fst_intr, SA_SHIRQ, FST_DEV_NAME, card )) - { - - printk_err ("Unable to register interrupt %d\n", card->irq ); - err = -ENODEV; - goto error_unmap_ctlmem; - } - - /* Record driver data for later use */ - pci_set_drvdata(pdev, card); - - /* Remainder of card setup */ - fst_init_card ( card ); - - return 0; /* Success */ - - - /* Failure. Release resources */ -error_unmap_ctlmem: - iounmap ( card->ctlmem ); - -error_unmap_mem: - iounmap ( card->mem ); - -error_release_ctlmem: - release_mem_region ( card->phys_ctlmem, 0x10 ); - -error_release_mem: - release_mem_region ( card->phys_mem, FST_MEMSIZE ); - -error_release_io: - release_region ( card->pci_conf, 0x80 ); - -error_free_card: - kfree ( card ); - return err; -} + static int firsttime_done = 0; + static int no_of_cards_added = 0; + struct fst_card_info *card; + int err = 0; + int i; + + if (!firsttime_done) { + printk_info("FarSync WAN driver " FST_USER_VERSION + " (c) 2001-2004 FarSite Communications Ltd.\n"); + firsttime_done = 1; + dbg(DBG_ASS, "The value of debug mask is %x\n", fst_debug_mask); + } + + /* + * We are going to be clever and allow certain cards not to be + * configured. An exclude list can be provided in /etc/modules.conf + */ + if (fst_excluded_cards != 0) { + /* + * There are cards to exclude + * + */ + for (i = 0; i < fst_excluded_cards; i++) { + if ((pdev->devfn) >> 3 == fst_excluded_list[i]) { + printk_info("FarSync PCI device %d not assigned\n", + (pdev->devfn) >> 3); + return -EBUSY; + } + } + } + + /* Allocate driver private data */ + card = kmalloc(sizeof (struct fst_card_info), GFP_KERNEL); + if (card == NULL) { + printk_err("FarSync card found but insufficient memory for" + " driver storage\n"); + return -ENOMEM; + } + memset(card, 0, sizeof (struct fst_card_info)); + + /* Try to enable the device */ + if ((err = pci_enable_device(pdev)) != 0) { + printk_err("Failed to enable card. Err %d\n", -err); + kfree(card); + return err; + } + if ((err = pci_request_regions(pdev, "FarSync")) !=0) { + printk_err("Failed to allocate regions. Err %d\n", -err); + pci_disable_device(pdev); + kfree(card); + return err; + } + + /* Get virtual addresses of memory regions */ + card->pci_conf = pci_resource_start(pdev, 1); + card->phys_mem = pci_resource_start(pdev, 2); + card->phys_ctlmem = pci_resource_start(pdev, 3); + if ((card->mem = ioremap(card->phys_mem, FST_MEMSIZE)) == NULL) { + printk_err("Physical memory remap failed\n"); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(card); + return -ENODEV; + } + if ((card->ctlmem = ioremap(card->phys_ctlmem, 0x10)) == NULL) { + printk_err("Control memory remap failed\n"); + pci_release_regions(pdev); + pci_disable_device(pdev); + kfree(card); + return -ENODEV; + } + dbg(DBG_PCI, "kernel mem %p, ctlmem %p\n", card->mem, card->ctlmem); + + /* Register the interrupt handler */ + if (request_irq(pdev->irq, fst_intr, SA_SHIRQ, FST_DEV_NAME, card)) { + printk_err("Unable to register interrupt %d\n", card->irq); + pci_release_regions(pdev); + pci_disable_device(pdev); + iounmap(card->ctlmem); + iounmap(card->mem); + kfree(card); + return -ENODEV; + } + + /* Record info we need */ + card->irq = pdev->irq; + card->type = ent->driver_data; + card->family = ((ent->driver_data == FST_TYPE_T2P) || + (ent->driver_data == FST_TYPE_T4P)) + ? FST_FAMILY_TXP : FST_FAMILY_TXU; + if ((ent->driver_data == FST_TYPE_T1U) || + (ent->driver_data == FST_TYPE_TE1)) + card->nports = 1; + else + card->nports = ((ent->driver_data == FST_TYPE_T2P) || + (ent->driver_data == FST_TYPE_T2U)) ? 2 : 4; + + card->state = FST_UNINIT; + card->device = pdev; + + dbg(DBG_PCI, "type %d nports %d irq %d\n", card->type, + card->nports, card->irq); + dbg(DBG_PCI, "conf %04x mem %08x ctlmem %08x\n", + card->pci_conf, card->phys_mem, card->phys_ctlmem); + + /* Reset the card's processor */ + fst_cpureset(card); + card->state = FST_RESET; + + /* Initialise DMA (if required) */ + fst_init_dma(card); + + /* Record driver data for later use */ + pci_set_drvdata(pdev, card); + + /* Remainder of card setup */ + fst_card_array[no_of_cards_added] = card; + card->card_no = no_of_cards_added++; /* Record instance and bump it */ + fst_init_card(card); + if (card->family == FST_FAMILY_TXU) { + /* + * Allocate a dma buffer for transmit and receives + */ + card->rx_dma_handle_host = + pci_alloc_consistent(card->device, FST_MAX_MTU, + &card->rx_dma_handle_card); + if (card->rx_dma_handle_host == NULL) { + printk_err("Could not allocate rx dma buffer\n"); + fst_disable_intr(card); + pci_release_regions(pdev); + pci_disable_device(pdev); + iounmap(card->ctlmem); + iounmap(card->mem); + kfree(card); + return -ENOMEM; + } + card->tx_dma_handle_host = + pci_alloc_consistent(card->device, FST_MAX_MTU, + &card->tx_dma_handle_card); + if (card->tx_dma_handle_host == NULL) { + printk_err("Could not allocate tx dma buffer\n"); + fst_disable_intr(card); + pci_release_regions(pdev); + pci_disable_device(pdev); + iounmap(card->ctlmem); + iounmap(card->mem); + kfree(card); + return -ENOMEM; + } + } + return 0; /* Success */ +} /* * Cleanup and close down a card */ static void __devexit -fst_remove_one ( struct pci_dev *pdev ) +fst_remove_one(struct pci_dev *pdev) { - struct fst_card_info *card; - int i; - - card = pci_get_drvdata(pdev); + struct fst_card_info *card; + int i; - for ( i = 0 ; i < card->nports ; i++ ) - { - unregister_hdlc_device ( &card->ports[i].hdlc ); - } + card = pci_get_drvdata(pdev); - fst_disable_intr ( card ); - free_irq ( card->irq, card ); + for (i = 0; i < card->nports; i++) { + unregister_hdlc_device(&card->ports[i].hdlc); + } - iounmap ( card->ctlmem ); - iounmap ( card->mem ); + fst_disable_intr(card); + free_irq(card->irq, card); - release_mem_region ( card->phys_ctlmem, 0x10 ); - release_mem_region ( card->phys_mem, FST_MEMSIZE ); - release_region ( card->pci_conf, 0x80 ); + iounmap(card->ctlmem); + iounmap(card->mem); - kfree ( card ); + pci_release_regions(pdev); +#if 0 + release_mem_region(card->phys_ctlmem, 0x10); + release_mem_region(card->phys_mem, FST_MEMSIZE); + if (card->family == FST_FAMILY_TXU) { + release_region(card->pci_conf, 0x100); + } else { + release_region(card->pci_conf, 0x80); + } +#endif + if (card->family == FST_FAMILY_TXU) { + /* + * Free dma buffers + */ + pci_free_consistent(card->device, FST_MAX_MTU, + card->rx_dma_handle_host, + card->rx_dma_handle_card); + pci_free_consistent(card->device, FST_MAX_MTU, + card->tx_dma_handle_host, + card->tx_dma_handle_card); + } + fst_card_array[card->card_no] = NULL; } static struct pci_driver fst_driver = { - name: FST_NAME, - id_table: fst_pci_dev_id, - probe: fst_add_one, - remove: __devexit_p(fst_remove_one), - suspend: NULL, - resume: NULL, + name:FST_NAME, + id_table:fst_pci_dev_id, + probe:fst_add_one, + remove:__devexit_p(fst_remove_one), + suspend:NULL, + resume:NULL, }; static int __init fst_init(void) { - return pci_module_init ( &fst_driver ); + int i; + + for (i = 0; i < FST_MAX_CARDS; i++) + fst_card_array[i] = NULL; + spin_lock_init(&fst_work_q_lock); + return pci_module_init(&fst_driver); } static void __exit fst_cleanup_module(void) { - pci_unregister_driver ( &fst_driver ); + printk_info("FarSync WAN driver unloading\n"); + pci_unregister_driver(&fst_driver); } -module_init ( fst_init ); -module_exit ( fst_cleanup_module ); - +module_init(fst_init); +module_exit(fst_cleanup_module); diff -urN linux-2.4.26/drivers/net/wan/farsync.h linux-2.4.27/drivers/net/wan/farsync.h --- linux-2.4.26/drivers/net/wan/farsync.h 2003-06-13 07:51:35.000000000 -0700 +++ linux-2.4.27/drivers/net/wan/farsync.h 2004-08-07 16:26:05.169367939 -0700 @@ -32,8 +32,13 @@ * A short common prefix is useful for routines within the driver to avoid * conflict with other similar drivers and I chosen to use "fst_" for this * purpose (FarSite T-series). + * + * Finally the device driver needs a short network interface name. Since + * "hdlc" is already in use I've chosen the even less informative "sync" + * for the present. */ #define FST_NAME "fst" /* In debug/info etc */ +#define FST_NDEV_NAME "sync" /* For net interface */ #define FST_DEV_NAME "farsync" /* For misc interfaces */ @@ -45,7 +50,7 @@ * have individual versions (or IDs) that move much faster than the * the release version as individual updates are tracked. */ -#define FST_USER_VERSION "0.09" +#define FST_USER_VERSION "1.04" /* Ioctl call command values @@ -100,6 +105,7 @@ unsigned int state; /* State of card */ unsigned int index; /* Index of port ioctl was issued on */ unsigned int smcFirmwareVersion; + unsigned long kernelVersion; /* What Kernel version we are working with */ unsigned short lineInterface; /* Physical interface type */ unsigned char proto; /* Line protocol */ unsigned char internalClock; /* 1 => internal clock, 0 => external */ @@ -110,6 +116,31 @@ unsigned short cableStatus; /* lsb: 0=> present, 1=> absent */ unsigned short cardMode; /* lsb: LED id mode */ unsigned short debug; /* Debug flags */ + unsigned char transparentMode; /* Not used always 0 */ + unsigned char invertClock; /* Invert clock feature for syncing */ + unsigned char startingSlot; /* Time slot to use for start of tx */ + unsigned char clockSource; /* External or internal */ + unsigned char framing; /* E1, T1 or J1 */ + unsigned char structure; /* unframed, double, crc4, f4, f12, */ + /* f24 f72 */ + unsigned char interface; /* rj48c or bnc */ + unsigned char coding; /* hdb3 b8zs */ + unsigned char lineBuildOut; /* 0, -7.5, -15, -22 */ + unsigned char equalizer; /* short or lon haul settings */ + unsigned char loopMode; /* various loopbacks */ + unsigned char range; /* cable lengths */ + unsigned char txBufferMode; /* tx elastic buffer depth */ + unsigned char rxBufferMode; /* rx elastic buffer depth */ + unsigned char losThreshold; /* Attenuation on LOS signal */ + unsigned char idleCode; /* Value to send as idle timeslot */ + unsigned int receiveBufferDelay; /* delay thro rx buffer timeslots */ + unsigned int framingErrorCount; /* framing errors */ + unsigned int codeViolationCount; /* code violations */ + unsigned int crcErrorCount; /* CRC errors */ + int lineAttenuation; /* in dB*/ + unsigned short lossOfSignal; + unsigned short receiveRemoteAlarm; + unsigned short alarmIndicationSignal; }; /* "valid" bitmask */ @@ -131,13 +162,23 @@ */ #define FSTVAL_PROTO 0x00000200 /* proto */ #define FSTVAL_MODE 0x00000400 /* cardMode */ +#define FSTVAL_PHASE 0x00000800 /* Clock phase */ +#define FSTVAL_TE1 0x00001000 /* T1E1 Configuration */ #define FSTVAL_DEBUG 0x80000000 /* debug */ -#define FSTVAL_ALL 0x000007FF /* Note: does not include DEBUG flag */ +#define FSTVAL_ALL 0x00001FFF /* Note: does not include DEBUG flag */ /* "type" */ #define FST_TYPE_NONE 0 /* Probably should never happen */ #define FST_TYPE_T2P 1 /* T2P X21 2 port card */ #define FST_TYPE_T4P 2 /* T4P X21 4 port card */ +#define FST_TYPE_T1U 3 /* T1U X21 1 port card */ +#define FST_TYPE_T2U 4 /* T2U X21 2 port card */ +#define FST_TYPE_T4U 5 /* T4U X21 4 port card */ +#define FST_TYPE_TE1 6 /* T1E1 X21 1 port card */ + +/* "family" */ +#define FST_FAMILY_TXP 0 /* T2P or T4P */ +#define FST_FAMILY_TXU 1 /* T1U or T2U or T4U */ /* "state" */ #define FST_UNINIT 0 /* Raw uninitialised state following @@ -155,6 +196,10 @@ #define V24 1 #define X21 2 #define V35 3 +#define X21D 4 +#define T1 5 +#define E1 6 +#define J1 7 /* "proto" */ #define FST_HDLC 1 /* Cisco compatible HDLC */ @@ -187,6 +232,97 @@ /* "cardMode" bitmask */ #define CARD_MODE_IDENTIFY 0x0001 +/* + * Constants for T1/E1 configuration + */ + +/* + * Clock source + */ +#define CLOCKING_SLAVE 0 +#define CLOCKING_MASTER 1 + +/* + * Framing + */ +#define FRAMING_E1 0 +#define FRAMING_J1 1 +#define FRAMING_T1 2 + +/* + * Structure + */ +#define STRUCTURE_UNFRAMED 0 +#define STRUCTURE_E1_DOUBLE 1 +#define STRUCTURE_E1_CRC4 2 +#define STRUCTURE_E1_CRC4M 3 +#define STRUCTURE_T1_4 4 +#define STRUCTURE_T1_12 5 +#define STRUCTURE_T1_24 6 +#define STRUCTURE_T1_72 7 + +/* + * Interface + */ +#define INTERFACE_RJ48C 0 +#define INTERFACE_BNC 1 + +/* + * Coding + */ + +#define CODING_HDB3 0 +#define CODING_NRZ 1 +#define CODING_CMI 2 +#define CODING_CMI_HDB3 3 +#define CODING_CMI_B8ZS 4 +#define CODING_AMI 5 +#define CODING_AMI_ZCS 6 +#define CODING_B8ZS 7 + +/* + * Line Build Out + */ +#define LBO_0dB 0 +#define LBO_7dB5 1 +#define LBO_15dB 2 +#define LBO_22dB5 3 + +/* + * Range for long haul t1 > 655ft + */ +#define RANGE_0_133_FT 0 +#define RANGE_0_40_M RANGE_0_133_FT +#define RANGE_133_266_FT 1 +#define RANGE_40_81_M RANGE_133_266_FT +#define RANGE_266_399_FT 2 +#define RANGE_81_122_M RANGE_266_399_FT +#define RANGE_399_533_FT 3 +#define RANGE_122_162_M RANGE_399_533_FT +#define RANGE_533_655_FT 4 +#define RANGE_162_200_M RANGE_533_655_FT +/* + * Receive Equaliser + */ +#define EQUALIZER_SHORT 0 +#define EQUALIZER_LONG 1 + +/* + * Loop modes + */ +#define LOOP_NONE 0 +#define LOOP_LOCAL 1 +#define LOOP_PAYLOAD_EXC_TS0 2 +#define LOOP_PAYLOAD_INC_TS0 3 +#define LOOP_REMOTE 4 + +/* + * Buffer modes + */ +#define BUFFER_2_FRAME 0 +#define BUFFER_1_FRAME 1 +#define BUFFER_96_BIT 2 +#define BUFFER_NONE 3 /* Debug support * diff -urN linux-2.4.26/drivers/net/wireless/airo.c linux-2.4.27/drivers/net/wireless/airo.c --- linux-2.4.26/drivers/net/wireless/airo.c 2004-04-14 06:05:30.000000000 -0700 +++ linux-2.4.27/drivers/net/wireless/airo.c 2004-08-07 16:26:05.233370569 -0700 @@ -3666,19 +3666,22 @@ size_t len, loff_t *offset ) { - int i; - int pos; + loff_t pos = *offset; struct proc_data *priv = (struct proc_data*)file->private_data; - if( !priv->rbuffer ) return -EINVAL; + if (!priv->rbuffer) + return -EINVAL; - pos = *offset; - for( i = 0; i+pos < priv->readlen && i < len; i++ ) { - if (put_user( priv->rbuffer[i+pos], buffer+i )) - return -EFAULT; - } - *offset += i; - return i; + if (pos < 0) + return -EINVAL; + if (pos >= priv->readlen) + return 0; + if (len > priv->readlen - pos) + len = priv->readlen - pos; + if (copy_to_user(buffer, priv->rbuffer + pos, len)) + return -EFAULT; + *offset = pos + len; + return len; } /* @@ -3690,24 +3693,24 @@ size_t len, loff_t *offset ) { - int i; - int pos; + loff_t pos = *offset; struct proc_data *priv = (struct proc_data*)file->private_data; - if ( !priv->wbuffer ) { + if (!priv->wbuffer) return -EINVAL; - } - - pos = *offset; - for( i = 0; i + pos < priv->maxwritelen && - i < len; i++ ) { - if (get_user( priv->wbuffer[i+pos], buffer + i )) - return -EFAULT; - } - if ( i+pos > priv->writelen ) priv->writelen = i+file->f_pos; - *offset += i; - return i; + if (pos < 0) + return -EINVAL; + if (pos >= priv->maxwritelen) + return 0; + if (len > priv->maxwritelen - pos) + len = priv->maxwritelen - pos; + if (copy_from_user(priv->wbuffer + pos, buffer, len)) + return -EFAULT; + if (pos + len > priv->writelen) + priv->writelen = pos + len; + *offset = pos + len; + return len; } static int proc_status_open( struct inode *inode, struct file *file ) { diff -urN linux-2.4.26/drivers/net/wireless/orinoco_pci.c linux-2.4.27/drivers/net/wireless/orinoco_pci.c --- linux-2.4.26/drivers/net/wireless/orinoco_pci.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/net/wireless/orinoco_pci.c 2004-08-07 16:26:05.234370610 -0700 @@ -360,6 +360,7 @@ static struct pci_device_id orinoco_pci_pci_id_table[] __devinitdata = { {0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,}, + {0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,}, {0,}, }; diff -urN linux-2.4.26/drivers/parport/ChangeLog linux-2.4.27/drivers/parport/ChangeLog --- linux-2.4.26/drivers/parport/ChangeLog 2003-06-13 07:51:35.000000000 -0700 +++ linux-2.4.27/drivers/parport/ChangeLog 2004-08-07 16:26:05.234370610 -0700 @@ -1,7 +1,16 @@ +2004-06-13 Marek Michalkiewicz + + * parport_serial.c: Move from here to ../char/, must be initialised + after serial.c for register_serial to work. + 2002-11-29 Tim Waugh * parport_pc.c: Fix ECP hang on Aladdin card. + 2001-10-11 Tim Waugh + * parport_pc.c, parport_serial.c: Support for NetMos cards. + Patch originally from Michael Reinelt . + 2002-04-25 Tim Waugh * parport_serial.c, parport_pc.c: Move some SIIG cards around. diff -urN linux-2.4.26/drivers/parport/Makefile linux-2.4.27/drivers/parport/Makefile --- linux-2.4.26/drivers/parport/Makefile 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/parport/Makefile 2004-08-07 16:26:05.235370651 -0700 @@ -22,7 +22,6 @@ obj-$(CONFIG_PARPORT) += parport.o obj-$(CONFIG_PARPORT_PC) += parport_pc.o -obj-$(CONFIG_PARPORT_SERIAL) += parport_serial.o obj-$(CONFIG_PARPORT_PC_PCMCIA) += parport_cs.o obj-$(CONFIG_PARPORT_AMIGA) += parport_amiga.o obj-$(CONFIG_PARPORT_MFC3) += parport_mfc3.o diff -urN linux-2.4.26/drivers/parport/parport_pc.c linux-2.4.27/drivers/parport/parport_pc.c --- linux-2.4.26/drivers/parport/parport_pc.c 2003-06-13 07:51:35.000000000 -0700 +++ linux-2.4.27/drivers/parport/parport_pc.c 2004-08-07 16:26:05.237370733 -0700 @@ -2699,6 +2699,10 @@ oxsemi_840, aks_0100, mobility_pp, + netmos_9705, + netmos_9805, + netmos_9815, + netmos_9855, }; @@ -2768,6 +2772,10 @@ /* oxsemi_840 */ { 1, { { 0, -1 }, } }, /* aks_0100 */ { 1, { { 0, -1 }, } }, /* mobility_pp */ { 1, { { 0, 1 }, } }, + /* netmos_9705 */ { 1, { { 0, -1 }, } }, /* untested */ + /* netmos_9805 */ { 1, { { 0, -1 }, } }, /* untested */ + /* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ + /* netmos_9855 */ { 2, { { 0, -1 }, { 2, -1 }, } }, /* untested */ }; static struct pci_device_id parport_pc_pci_tbl[] __devinitdata = { @@ -2836,6 +2844,15 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, oxsemi_840 }, { PCI_VENDOR_ID_AKS, PCI_DEVICE_ID_AKS_ALADDINCARD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, aks_0100 }, + /* NetMos communication controllers */ + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9705, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9705 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9805, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9805 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9815, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9815 }, + { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9855 }, { 0, } /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); diff -urN linux-2.4.26/drivers/parport/parport_serial.c linux-2.4.27/drivers/parport/parport_serial.c --- linux-2.4.26/drivers/parport/parport_serial.c 2002-08-02 17:39:44.000000000 -0700 +++ linux-2.4.27/drivers/parport/parport_serial.c 1969-12-31 16:00:00.000000000 -0800 @@ -1,426 +0,0 @@ -/* - * Support for common PCI multi-I/O cards (which is most of them) - * - * Copyright (C) 2001 Tim Waugh - * - * 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. - * - * - * Multi-function PCI cards are supposed to present separate logical - * devices on the bus. A common thing to do seems to be to just use - * one logical device with lots of base address registers for both - * parallel ports and serial ports. This driver is for dealing with - * that. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -enum parport_pc_pci_cards { - titan_110l = 0, - titan_210l, - avlab_1s1p, - avlab_1s1p_650, - avlab_1s1p_850, - avlab_1s2p, - avlab_1s2p_650, - avlab_1s2p_850, - avlab_2s1p, - avlab_2s1p_650, - avlab_2s1p_850, - siig_1s1p_10x, - siig_2s1p_10x, - siig_2p1s_20x, - siig_1s1p_20x, - siig_2s1p_20x, -}; - - -/* each element directly indexed from enum list, above */ -static struct parport_pc_pci { - int numports; - struct { /* BAR (base address registers) numbers in the config - space header */ - int lo; - int hi; /* -1 if not there, >6 for offset-method (max - BAR is 6) */ - } addr[4]; - - /* If set, this is called immediately after pci_enable_device. - * If it returns non-zero, no probing will take place and the - * ports will not be used. */ - int (*preinit_hook) (struct pci_dev *pdev, int autoirq, int autodma); - - /* If set, this is called after probing for ports. If 'failed' - * is non-zero we couldn't use any of the ports. */ - void (*postinit_hook) (struct pci_dev *pdev, int failed); -} cards[] __devinitdata = { - /* titan_110l */ { 1, { { 3, -1 }, } }, - /* titan_210l */ { 1, { { 3, -1 }, } }, - /* avlab_1s1p */ { 1, { { 1, 2}, } }, - /* avlab_1s1p_650 */ { 1, { { 1, 2}, } }, - /* avlab_1s1p_850 */ { 1, { { 1, 2}, } }, - /* avlab_1s2p */ { 2, { { 1, 2}, { 3, 4 },} }, - /* avlab_1s2p_650 */ { 2, { { 1, 2}, { 3, 4 },} }, - /* avlab_1s2p_850 */ { 2, { { 1, 2}, { 3, 4 },} }, - /* avlab_2s1p */ { 1, { { 2, 3}, } }, - /* avlab_2s1p_650 */ { 1, { { 2, 3}, } }, - /* avlab_2s1p_850 */ { 1, { { 2, 3}, } }, - /* siig_1s1p_10x */ { 1, { { 3, 4 }, } }, - /* siig_2s1p_10x */ { 1, { { 4, 5 }, } }, - /* siig_2p1s_20x */ { 2, { { 1, 2 }, { 3, 4 }, } }, - /* siig_1s1p_20x */ { 1, { { 1, 2 }, } }, - /* siig_2s1p_20x */ { 1, { { 2, 3 }, } }, -}; - -static struct pci_device_id parport_serial_pci_tbl[] __devinitdata = { - /* PCI cards */ - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_110L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_110l }, - { PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_210L, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_210l }, - /* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/ - { 0x14db, 0x2110, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p}, - { 0x14db, 0x2111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p_650}, - { 0x14db, 0x2112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s1p_850}, - { 0x14db, 0x2140, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p}, - { 0x14db, 0x2141, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p_650}, - { 0x14db, 0x2142, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1s2p_850}, - { 0x14db, 0x2160, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p}, - { 0x14db, 0x2161, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p_650}, - { 0x14db, 0x2162, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_2s1p_850}, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_10x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_10x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2P1S_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2p1s_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_1S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_1s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_550, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_650, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - { PCI_VENDOR_ID_SIIG, PCI_DEVICE_ID_SIIG_2S1P_20x_850, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, siig_2s1p_20x }, - - { 0, } /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci,parport_serial_pci_tbl); - -struct pci_board_no_ids { - int flags; - int num_ports; - int base_baud; - int uart_offset; - int reg_shift; - int (*init_fn)(struct pci_dev *dev, struct pci_board_no_ids *board, - int enable); - int first_uart_offset; -}; - -static int __devinit siig10x_init_fn(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) -{ - return pci_siig10x_fn(dev, NULL, enable); -} - -static int __devinit siig20x_init_fn(struct pci_dev *dev, struct pci_board_no_ids *board, int enable) -{ - return pci_siig20x_fn(dev, NULL, enable); -} - -static struct pci_board_no_ids pci_boards[] __devinitdata = { - /* - * PCI Flags, Number of Ports, Base (Maximum) Baud Rate, - * Offset to get to next UART's registers, - * Register shift to use for memory-mapped I/O, - * Initialization function, first UART offset - */ - -// Cards not tested are marked n/t -// If you have one of these cards and it works for you, please tell me.. - -/* titan_110l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 1, 921600 }, -/* titan_210l */ { SPCI_FL_BASE1 | SPCI_FL_BASE_TABLE, 2, 921600 }, -/* avlab_1s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s2p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s2p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_1s2p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 1, 115200 }, -/* avlab_2s1p (n/t) */ { SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* avlab_2s1p_650 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* avlab_2s1p_850 (nt)*/{ SPCI_FL_BASE0 | SPCI_FL_BASE_TABLE, 2, 115200 }, -/* siig_1s1p_10x */ { SPCI_FL_BASE2, 1, 460800, 0, 0, siig10x_init_fn }, -/* siig_2s1p_10x */ { SPCI_FL_BASE2, 1, 921600, 0, 0, siig10x_init_fn }, -/* siig_2p1s_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, -/* siig_1s1p_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, -/* siig_2s1p_20x */ { SPCI_FL_BASE0, 1, 921600, 0, 0, siig20x_init_fn }, -}; - -struct parport_serial_private { - int num_ser; - int line[20]; - struct pci_board_no_ids ser; - int num_par; - struct parport *port[PARPORT_MAX]; -}; - -static int __devinit get_pci_port (struct pci_dev *dev, - struct pci_board_no_ids *board, - struct serial_struct *req, - int idx) -{ - unsigned long port; - int base_idx; - int max_port; - int offset; - - base_idx = SPCI_FL_GET_BASE(board->flags); - if (board->flags & SPCI_FL_BASE_TABLE) - base_idx += idx; - - if (board->flags & SPCI_FL_REGION_SZ_CAP) { - max_port = pci_resource_len(dev, base_idx) / 8; - if (idx >= max_port) - return 1; - } - - offset = board->first_uart_offset; - - /* Timedia/SUNIX uses a mixture of BARs and offsets */ - /* Ugh, this is ugly as all hell --- TYT */ - if(dev->vendor == PCI_VENDOR_ID_TIMEDIA ) /* 0x1409 */ - switch(idx) { - case 0: base_idx=0; - break; - case 1: base_idx=0; offset=8; - break; - case 2: base_idx=1; - break; - case 3: base_idx=1; offset=8; - break; - case 4: /* BAR 2*/ - case 5: /* BAR 3 */ - case 6: /* BAR 4*/ - case 7: base_idx=idx-2; /* BAR 5*/ - } - - port = pci_resource_start(dev, base_idx) + offset; - - if ((board->flags & SPCI_FL_BASE_TABLE) == 0) - port += idx * (board->uart_offset ? board->uart_offset : 8); - - if (pci_resource_flags (dev, base_idx) & IORESOURCE_IO) { - int high_bits_offset = ((sizeof(long)-sizeof(int))*8); - req->port = port; - if (high_bits_offset) - req->port_high = port >> high_bits_offset; - else - req->port_high = 0; - return 0; - } - req->io_type = SERIAL_IO_MEM; - req->iomem_base = ioremap(port, board->uart_offset); - req->iomem_reg_shift = board->reg_shift; - req->port = 0; - return req->iomem_base ? 0 : 1; -} - -/* Register the serial port(s) of a PCI card. */ -static int __devinit serial_register (struct pci_dev *dev, - const struct pci_device_id *id) -{ - struct pci_board_no_ids *board = &pci_boards[id->driver_data]; - struct parport_serial_private *priv = pci_get_drvdata (dev); - struct serial_struct serial_req; - int base_baud; - int k; - int success = 0; - - priv->ser = *board; - if (board->init_fn && ((board->init_fn) (dev, board, 1) != 0)) - return 1; - - base_baud = board->base_baud; - if (!base_baud) - base_baud = BASE_BAUD; - memset (&serial_req, 0, sizeof (serial_req)); - - for (k = 0; k < board->num_ports; k++) { - int line; - serial_req.irq = dev->irq; - if (get_pci_port (dev, board, &serial_req, k)) - break; - serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; - serial_req.baud_base = base_baud; - line = register_serial (&serial_req); - if (line < 0) { - printk (KERN_DEBUG - "parport_serial: register_serial failed\n"); - continue; - } - priv->line[priv->num_ser++] = line; - success = 1; - } - - return success ? 0 : 1; -} - -/* Register the parallel port(s) of a PCI card. */ -static int __devinit parport_register (struct pci_dev *dev, - const struct pci_device_id *id) -{ - struct parport_serial_private *priv = pci_get_drvdata (dev); - int i = id->driver_data, n; - int success = 0; - - if (cards[i].preinit_hook && - cards[i].preinit_hook (dev, PARPORT_IRQ_NONE, PARPORT_DMA_NONE)) - return -ENODEV; - - for (n = 0; n < cards[i].numports; n++) { - struct parport *port; - int lo = cards[i].addr[n].lo; - int hi = cards[i].addr[n].hi; - unsigned long io_lo, io_hi; - io_lo = pci_resource_start (dev, lo); - io_hi = 0; - if ((hi >= 0) && (hi <= 6)) - io_hi = pci_resource_start (dev, hi); - else if (hi > 6) - io_lo += hi; /* Reinterpret the meaning of - "hi" as an offset (see SYBA - def.) */ - /* TODO: test if sharing interrupts works */ - printk (KERN_DEBUG "PCI parallel port detected: %04x:%04x, " - "I/O at %#lx(%#lx)\n", - parport_serial_pci_tbl[i].vendor, - parport_serial_pci_tbl[i].device, io_lo, io_hi); - port = parport_pc_probe_port (io_lo, io_hi, PARPORT_IRQ_NONE, - PARPORT_DMA_NONE, dev); - if (port) { - priv->port[priv->num_par++] = port; - success = 1; - } - } - - if (cards[i].postinit_hook) - cards[i].postinit_hook (dev, !success); - - return success ? 0 : 1; -} - -static int __devinit parport_serial_pci_probe (struct pci_dev *dev, - const struct pci_device_id *id) -{ - struct parport_serial_private *priv; - int err; - - priv = kmalloc (sizeof *priv, GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->num_ser = priv->num_par = 0; - pci_set_drvdata (dev, priv); - - err = pci_enable_device (dev); - if (err) { - pci_set_drvdata (dev, NULL); - kfree (priv); - return err; - } - - if (parport_register (dev, id)) { - pci_set_drvdata (dev, NULL); - kfree (priv); - return -ENODEV; - } - - if (serial_register (dev, id)) { - int i; - for (i = 0; i < priv->num_par; i++) - parport_pc_unregister_port (priv->port[i]); - pci_set_drvdata (dev, NULL); - kfree (priv); - return -ENODEV; - } - - return 0; -} - -static void __devexit parport_serial_pci_remove (struct pci_dev *dev) -{ - struct parport_serial_private *priv = pci_get_drvdata (dev); - int i; - - // Serial ports - for (i = 0; i < priv->num_ser; i++) { - unregister_serial (priv->line[i]); - - if (priv->ser.init_fn) - (priv->ser.init_fn) (dev, &priv->ser, 0); - } - pci_set_drvdata (dev, NULL); - - // Parallel ports - for (i = 0; i < priv->num_par; i++) - parport_pc_unregister_port (priv->port[i]); - - kfree (priv); - return; -} - -static struct pci_driver parport_serial_pci_driver = { - name: "parport_serial", - id_table: parport_serial_pci_tbl, - probe: parport_serial_pci_probe, - remove: __devexit_p(parport_serial_pci_remove), -}; - - -static int __init parport_serial_init (void) -{ - return pci_module_init (&parport_serial_pci_driver); -} - -static void __exit parport_serial_exit (void) -{ - pci_unregister_driver (&parport_serial_pci_driver); - return; -} - -MODULE_AUTHOR("Tim Waugh "); -MODULE_DESCRIPTION("Driver for common parallel+serial multi-I/O PCI cards"); -MODULE_LICENSE("GPL"); - -module_init(parport_serial_init); -module_exit(parport_serial_exit); diff -urN linux-2.4.26/drivers/pci/pci.c linux-2.4.27/drivers/pci/pci.c --- linux-2.4.26/drivers/pci/pci.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/pci/pci.c 2004-08-07 16:26:05.241370897 -0700 @@ -943,7 +943,7 @@ if (cacheline_size == pci_cache_line_size) return 0; - printk(KERN_WARNING "PCI: cache line size of %d is not supported " + printk(KERN_DEBUG "PCI: cache line size of %d is not supported " "by device %s\n", pci_cache_line_size << 2, dev->slot_name); return -EINVAL; @@ -2214,6 +2214,7 @@ EXPORT_SYMBOL(isa_dma_bridge_buggy); EXPORT_SYMBOL(pci_pci_problems); +EXPORT_SYMBOL(pciehp_msi_quirk); /* Pool allocator */ diff -urN linux-2.4.26/drivers/pci/pci.ids linux-2.4.27/drivers/pci/pci.ids --- linux-2.4.26/drivers/pci/pci.ids 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/pci/pci.ids 2004-08-07 16:26:05.246371103 -0700 @@ -476,6 +476,7 @@ 700f PCI Bridge [IGP 320M] 7010 PCI Bridge [IGP 340M] cab2 RS200/RS200M AGP Bridge [IGP 340M] + cbb2 RS200/RS200M AGP Bridge [IGP 345M] 1003 ULSI Systems 0201 US201 1004 VLSI Technology Inc @@ -5542,13 +5543,21 @@ 1649 NetXtreme BCM5704S Gigabit Ethernet 164d NetXtreme BCM5702FE Gigabit Ethernet 1653 NetXtreme BCM5705 Gigabit Ethernet - 1654 NetXtreme BCM5705 Gigabit Ethernet + 1654 NetXtreme BCM5705_2 Gigabit Ethernet + 1658 NetXtreme BCM5720 Gigabit Ethernet + 1659 NetXtreme BCM5721 Gigabit Ethernet 165d NetXtreme BCM5705M Gigabit Ethernet - 165e NetXtreme BCM5705M Gigabit Ethernet + 165e NetXtreme BCM5705M_2 Gigabit Ethernet 166e NetXtreme BCM5705F Gigabit Ethernet + 1676 NetXtreme BCM5750 Gigabit Ethernet + 1677 NetXtreme BCM5751 Gigabit Ethernet + 167c NetXtreme BCM5750M Gigabit Ethernet + 167d NetXtreme BCM5751M Gigabit Ethernet + 167e NetXtreme BCM5751F Gigabit Ethernet 1696 NetXtreme BCM5782 Gigabit Ethernet 14e4 000d NetXtreme BCM5782 1000Base-T 169c NetXtreme BCM5788 Gigabit Ethernet + 169d NetXtreme BCM5789 Gigabit Ethernet 16a6 NetXtreme BCM5702 Gigabit Ethernet 0e11 00bb NC7760 Gigabit Server Adapter (PCI-X, 10/100/1000-T) 1028 0126 BCM5702 1000Base-T @@ -5571,7 +5580,7 @@ 14e4 0009 NetXtreme BCM5703 1000Base-T 14e4 000a NetXtreme BCM5703 1000Base-SX 170d NetXtreme BCM5901 Gigabit Ethernet - 170e NetXtreme BCM5901 Gigabit Ethernet + 170e NetXtreme BCM5901_2 Gigabit Ethernet 4210 BCM4210 iLine10 HomePNA 2.0 4211 BCM4211 iLine10 HomePNA 2.0 + V.90 56k modem 4212 BCM4212 v.90 56k modem diff -urN linux-2.4.26/drivers/pci/proc.c linux-2.4.27/drivers/pci/proc.c --- linux-2.4.26/drivers/pci/proc.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.27/drivers/pci/proc.c 2004-08-07 16:26:05.247371144 -0700 @@ -47,7 +47,8 @@ const struct inode *ino = file->f_dentry->d_inode; const struct proc_dir_entry *dp = ino->u.generic_ip; struct pci_dev *dev = dp->data; - unsigned int pos = *ppos; + loff_t n = *ppos; + unsigned pos = n; unsigned int cnt, size; /* @@ -63,7 +64,7 @@ else size = 64; - if (pos >= size) + if (pos != n || pos >= size) return 0; if (nbytes >= size) nbytes = size; @@ -129,10 +130,11 @@ const struct inode *ino = file->f_dentry->d_inode; const struct proc_dir_entry *dp = ino->u.generic_ip; struct pci_dev *dev = dp->data; - int pos = *ppos; + loff_t n = *ppos; + unsigned pos = n; int cnt; - if (pos >= PCI_CFG_SPACE_SIZE) + if (pos != n || pos >= PCI_CFG_SPACE_SIZE) return 0; if (nbytes >= PCI_CFG_SPACE_SIZE) nbytes = PCI_CFG_SPACE_SIZE; diff -urN linux-2.4.26/drivers/pci/quirks.c linux-2.4.27/drivers/pci/quirks.c --- linux-2.4.26/drivers/pci/quirks.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/pci/quirks.c 2004-08-07 16:26:05.247371144 -0700 @@ -719,6 +719,13 @@ } } +int pciehp_msi_quirk; + +static void __devinit quirk_pciehp_msi(struct pci_dev *pdev) +{ + pciehp_msi_quirk = 1; +} + /* * The main table of quirks. */ @@ -808,6 +815,8 @@ { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc }, + { PCI_FIXUP_FINAL, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SMCH, quirk_pciehp_msi }, + { 0 } }; diff -urN linux-2.4.26/drivers/pcmcia/yenta.c linux-2.4.27/drivers/pcmcia/yenta.c --- linux-2.4.26/drivers/pcmcia/yenta.c 2004-04-14 06:05:31.000000000 -0700 +++ linux-2.4.27/drivers/pcmcia/yenta.c 2004-08-07 16:26:05.248371185 -0700 @@ -35,6 +35,8 @@ #define to_cycles(ns) ((ns)/120) #define to_ns(cycles) ((cycles)*120) +static int override_bios; + /* * Generate easy-to-use ways of reading a cardbus sockets * regular memory space ("cb_xxx"), configuration space @@ -753,7 +755,7 @@ start = config_readl(socket, offset) & mask; end = config_readl(socket, offset+4) | ~mask; - if (start && end > start) { + if (start && end > start && !override_bios) { res->start = start; res->end = end; if (request_resource(root, res) == 0) @@ -950,6 +952,9 @@ return 0; } + +MODULE_PARM (override_bios, "i"); +MODULE_PARM_DESC (override_bios, "yenta ignore bios resource allocation"); /* * Standard plain cardbus - no frills, no extensions diff -urN linux-2.4.26/drivers/pnp/isapnp_proc.c linux-2.4.27/drivers/pnp/isapnp_proc.c --- linux-2.4.26/drivers/pnp/isapnp_proc.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.27/drivers/pnp/isapnp_proc.c 2004-08-07 16:26:05.249371226 -0700 @@ -102,6 +102,7 @@ size_t count, loff_t * offset) { isapnp_info_buffer_t *buf; + loff_t pos = *offset; long size = 0, size1; int mode; @@ -111,15 +112,15 @@ buf = (isapnp_info_buffer_t *) file->private_data; if (!buf) return -EIO; - if (file->f_pos >= buf->size) + if (pos != (unsigned) pos || pos >= buf->size) return 0; size = buf->size < count ? buf->size : count; - size1 = buf->size - file->f_pos; + size1 = buf->size - pos; if (size1 < size) size = size1; - if (copy_to_user(buffer, buf->buffer + file->f_pos, size)) + if (copy_to_user(buffer, buf->buffer + pos, size)) return -EFAULT; - file->f_pos += size; + *offset = pos + size; return size; } @@ -128,6 +129,7 @@ { isapnp_info_buffer_t *buf; long size = 0, size1; + loff_t pos = *offset; int mode; mode = file->f_flags & O_ACCMODE; @@ -136,19 +138,19 @@ buf = (isapnp_info_buffer_t *) file->private_data; if (!buf) return -EIO; - if (file->f_pos < 0) + if (pos < 0) return -EINVAL; - if (file->f_pos >= buf->len) + if (pos >= buf->len) return -ENOMEM; size = buf->len < count ? buf->len : count; - size1 = buf->len - file->f_pos; + size1 = buf->len - pos; if (size1 < size) size = size1; - if (copy_from_user(buf->buffer + file->f_pos, buffer, size)) + if (copy_from_user(buf->buffer + pos, buffer, size)) return -EFAULT; - if (buf->size < file->f_pos + size) - buf->size = file->f_pos + size; - file->f_pos += size; + if (buf->size < pos + size) + buf->size = pos + size; + *offset = pos + size; return size; } @@ -240,14 +242,15 @@ struct inode *ino = file->f_dentry->d_inode; struct proc_dir_entry *dp = ino->u.generic_ip; struct pci_dev *dev = dp->data; - int pos = *ppos; + loff_t n = *ppos; + unsigned pos = n; int cnt, size = 256; - if (pos >= size) + if (pos != n || pos >= size) return 0; if (nbytes >= size) nbytes = size; - if (pos + nbytes > size) + if (nbytes > size - pos) nbytes = size - pos; cnt = nbytes; diff -urN linux-2.4.26/drivers/s390/block/dasd.c linux-2.4.27/drivers/s390/block/dasd.c --- linux-2.4.26/drivers/s390/block/dasd.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/s390/block/dasd.c 2004-08-07 16:26:05.319374102 -0700 @@ -4655,15 +4655,17 @@ loff_t * offset) { loff_t len; + loff_t n = *offset; + unsigned pos = n; tempinfo_t *p_info = (tempinfo_t *) file->private_data; - if (*offset >= p_info->len) { + if (n != pos || pos >= p_info->len) { return 0; /* EOF */ } else { - len = MIN (user_len, (p_info->len - *offset)); - if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) + len = MIN (user_len, (p_info->len - pos)); + if (copy_to_user (user_buf, &(p_info->data[pos]), len)) return -EFAULT; - (*offset) += len; + *offset = pos + len; return len; /* number of bytes "read" */ } } diff -urN linux-2.4.26/drivers/s390/char/tapechar.c linux-2.4.27/drivers/s390/char/tapechar.c --- linux-2.4.26/drivers/s390/char/tapechar.c 2001-11-09 14:05:02.000000000 -0800 +++ linux-2.4.27/drivers/s390/char/tapechar.c 2004-08-07 16:26:05.346375212 -0700 @@ -161,6 +161,7 @@ size_t block_size; ccw_req_t *cqr; int rc; + loff_t pos = *ppos; #ifdef TAPE_DEBUG debug_text_event (tape_debug_area,6,"c:read"); #endif /* TAPE_DEBUG */ @@ -230,7 +231,7 @@ debug_text_event (tape_debug_area,6,"c:rbytes:"); debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt); #endif /* TAPE_DEBUG */ - filp->f_pos += block_size - ti->devstat.rescnt; + *ppos = pos + (block_size - ti->devstat.rescnt); return block_size - ti->devstat.rescnt; } @@ -246,6 +247,8 @@ ccw_req_t *cqr; int nblocks, i, rc; size_t written = 0; + loff_t pos = *ppos; + #ifdef TAPE_DEBUG debug_text_event (tape_debug_area,6,"c:write"); #endif @@ -318,15 +321,17 @@ debug_text_event (tape_debug_area,6,"c:wbytes:"); debug_int_event (tape_debug_area,6,block_size - ti->devstat.rescnt); #endif - filp->f_pos += block_size - ti->devstat.rescnt; written += block_size - ti->devstat.rescnt; - if (ti->devstat.rescnt > 0) + if (ti->devstat.rescnt > 0) { + *ppos = pos + written; return written; + } } #ifdef TAPE_DEBUG debug_text_event (tape_debug_area,6,"c:wtotal:"); debug_int_event (tape_debug_area,6,written); #endif + *ppos = pos + written; return written; } diff -urN linux-2.4.26/drivers/s390/net/ctcmain.c linux-2.4.27/drivers/s390/net/ctcmain.c --- linux-2.4.26/drivers/s390/net/ctcmain.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/s390/net/ctcmain.c 2004-08-07 16:26:05.395377225 -0700 @@ -2934,6 +2934,7 @@ file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; + *(char *)file->private_data = '\0'; MOD_INC_USE_COUNT; return 0; } @@ -2999,6 +3000,7 @@ ctc_priv *privptr; ssize_t ret = 0; char *p = sbuf; + loff_t pos = *off; int l; if (!(dev = find_netdev_by_ino(ino))) @@ -3008,19 +3010,19 @@ privptr = (ctc_priv *)dev->priv; - if (file->f_pos == 0) + if (!*sbuf || pos == 0) sprintf(sbuf, "%d\n", privptr->channel[READ]->max_bufsize); l = strlen(sbuf); p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; + if (pos == (unsigned)pos && pos < l) { + p += pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; + *off = pos + ret; } - file->f_pos += ret; return ret; } @@ -3084,6 +3086,7 @@ ctc_priv *privptr; ssize_t ret = 0; char *p = sbuf; + loff_t pos = *off; int l; if (!(dev = find_netdev_by_ino(ino))) @@ -3093,19 +3096,19 @@ privptr = (ctc_priv *)dev->priv; - if (file->f_pos == 0) + if (!*sbus || pos == 0) sprintf(sbuf, "0x%02x\n", loglevel); l = strlen(sbuf); p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; + if (pos == (unsigned)pos && pos < l) { + p += pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; + *off = pos + ret; } - file->f_pos += ret; return ret; } @@ -3116,6 +3119,7 @@ file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; + *(char *)file->private_data = '\0'; MOD_INC_USE_COUNT; return 0; } @@ -3155,6 +3159,7 @@ ctc_priv *privptr; ssize_t ret = 0; char *p = sbuf; + loff_t pos = *off; int l; if (!(dev = find_netdev_by_ino(ino))) @@ -3164,7 +3169,7 @@ privptr = (ctc_priv *)dev->priv; - if (file->f_pos == 0) { + if (!*sbus || pos == 0) { p += sprintf(p, "Device FSM state: %s\n", fsm_getstate_str(privptr->fsm)); p += sprintf(p, "RX channel FSM state: %s\n", @@ -3186,14 +3191,14 @@ } l = strlen(sbuf); p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; + if (pos == (unsigned)pos && pos < l) { + p += pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; + *off = pos + ret; } - file->f_pos += ret; return ret; } diff -urN linux-2.4.26/drivers/s390/net/netiucv.c linux-2.4.27/drivers/s390/net/netiucv.c --- linux-2.4.26/drivers/s390/net/netiucv.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/s390/net/netiucv.c 2004-08-07 16:26:05.412377924 -0700 @@ -1375,6 +1375,7 @@ file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; + *(char *)file->private_data = '\0'; MOD_INC_USE_COUNT; return 0; } @@ -1440,6 +1441,7 @@ netiucv_priv *privptr; ssize_t ret = 0; char *p = sbuf; + loff_t pos = *ppos; int l; if (!(dev = find_netdev_by_ino(ino))) @@ -1449,19 +1451,20 @@ privptr = (netiucv_priv *)dev->priv; - if (file->f_pos == 0) + if (!*sbuf || pos == 0) sprintf(sbuf, "%d\n", privptr->conn->max_buffsize); l = strlen(sbuf); p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; + if (pos == (unsigned)pos && pos < l) { + p += pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; } - file->f_pos += ret; + pos += ret; + *ppos = pos; return ret; } @@ -1471,6 +1474,7 @@ file->private_data = kmalloc(CTRL_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; + *(char *)file->private_data = '\0'; MOD_INC_USE_COUNT; return 0; } @@ -1535,6 +1539,7 @@ netiucv_priv *privptr; ssize_t ret = 0; char *p = sbuf; + loff_t pos = *ppos; int l; if (!(dev = find_netdev_by_ino(ino))) @@ -1545,20 +1550,20 @@ privptr = (netiucv_priv *)dev->priv; - if (file->f_pos == 0) + if (!*sbuf || pos == 0) sprintf(sbuf, "%s\n", netiucv_printname(privptr->conn->userid)); l = strlen(sbuf); p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; + if (pos == (unsigned)pos && pos < l) { + p += pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; + *ppos = pos + ret; } - file->f_pos += ret; return ret; } @@ -1570,6 +1575,7 @@ file->private_data = kmalloc(STATS_BUFSIZE, GFP_KERNEL); if (file->private_data == NULL) return -ENOMEM; + *(char *)file->private_data = '\0'; MOD_INC_USE_COUNT; return 0; } @@ -1600,6 +1606,7 @@ netiucv_stat_read(struct file *file, char *buf, size_t count, loff_t *off) { unsigned int ino = ((struct inode *)file->f_dentry->d_inode)->i_ino; + loff_t pos = *ppos; char *sbuf = (char *)file->private_data; net_device *dev; netiucv_priv *privptr; @@ -1614,7 +1621,7 @@ privptr = (netiucv_priv *)dev->priv; - if (file->f_pos == 0) { + if (!*sbuf || pos == 0) { p += sprintf(p, "Device FSM state: %s\n", fsm_getstate_str(privptr->fsm)); p += sprintf(p, "Connection FSM state: %s\n", @@ -1638,14 +1645,14 @@ } l = strlen(sbuf); p = sbuf; - if (file->f_pos < l) { - p += file->f_pos; + if (pos == (unsigned)pos && pos < l) { + p += pos; l = strlen(p); ret = (count > l) ? l : count; if (copy_to_user(buf, p, ret)) return -EFAULT; + *ppos = pos + ret; } - file->f_pos += ret; return ret; } diff -urN linux-2.4.26/drivers/s390/net/qeth.c linux-2.4.27/drivers/s390/net/qeth.c --- linux-2.4.26/drivers/s390/net/qeth.c 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/s390/net/qeth.c 2004-08-07 16:26:05.482380800 -0700 @@ -9792,7 +9792,7 @@ int pos=0,end_pos; char dbf_text[15]; - if (*offset>0) return user_len; + if (*offset) return user_len; buffer=vmalloc(__max(user_len+1,QETH_DBF_MISC_LEN)); if (buffer == NULL) return -ENOMEM; @@ -10308,14 +10308,16 @@ { loff_t len; tempinfo_t *p_info = (tempinfo_t *) file->private_data; + loff_t n = *offset; + unsigned long pos = n; - if (*offset >= p_info->len) { + if (pos != n || pos >= p_info->len) { return 0; } else { - len = __min(user_len, (p_info->len - *offset)); - if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) + len = __min(user_len, (p_info->len - pos)); + if (copy_to_user (user_buf, &(p_info->data[pos]), len)) return -EFAULT; - (*offset) += len; + *offset = pos + len; return len; } } @@ -10350,7 +10352,7 @@ qeth_card_t *card; #define BUFFER_LEN (10+32+1+5+1+DEV_NAME_LEN+1) - if (*offset>0) return user_len; + if (*offset) return user_len; buffer=vmalloc(__max(__max(user_len+1,BUFFER_LEN),QETH_DBF_MISC_LEN)); if (buffer == NULL) @@ -10474,7 +10476,7 @@ PRINT_ERR("unknown ipato information command\n"); out: vfree(buffer); - *offset = *offset + user_len; + *offset = user_len; #undef BUFFER_LEN return user_len; } diff -urN linux-2.4.26/drivers/s390/s390io.c linux-2.4.27/drivers/s390/s390io.c --- linux-2.4.26/drivers/s390/s390io.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/s390/s390io.c 2004-08-07 16:26:05.488381047 -0700 @@ -8328,14 +8328,15 @@ { loff_t len; tempinfo_t *p_info = (tempinfo_t *) file->private_data; + loff_t pos = *offset; - if (*offset >= p_info->len) { + if (pos < 0 || pos >= p_info->len) { return 0; } else { - len = MIN (user_len, (p_info->len - *offset)); - if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) + len = MIN (user_len, (p_info->len - pos)); + if (copy_to_user (user_buf, &(p_info->data[pos]), len)) return -EFAULT; - (*offset) += len; + *offset = pos + len; return len; } } @@ -8410,14 +8411,15 @@ { loff_t len; tempinfo_t *p_info = (tempinfo_t *) file->private_data; + loff_t pos = *offset; - if (*offset >= p_info->len) { + if (pos < 0 || pos >= p_info->len) { return 0; } else { - len = MIN (user_len, (p_info->len - *offset)); - if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) + len = MIN (user_len, (p_info->len - pos)); + if (copy_to_user (user_buf, &(p_info->data[pos]), len)) return -EFAULT; - (*offset) += len; + *offset = pos + len; return len; } } @@ -8874,14 +8876,15 @@ { loff_t len; tempinfo_t *p_info = (tempinfo_t *) file->private_data; + loff_t pos = *offset; - if (*offset >= p_info->len) { + if (pos < 0 || pos >= p_info->len) { return 0; } else { len = MIN (user_len, (p_info->len - *offset)); if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) return -EFAULT; - (*offset) += len; + (*offset) = pos + len; return len; } } @@ -8994,14 +8997,15 @@ { loff_t len; tempinfo_t *p_info = (tempinfo_t *) file->private_data; + loff_t pos = *offset; - if (*offset >= p_info->len) { + if (pos < 0 || pos >= p_info->len) { return 0; } else { len = MIN (user_len, (p_info->len - *offset)); if (copy_to_user (user_buf, &(p_info->data[*offset]), len)) return -EFAULT; - (*offset) += len; + (*offset) = pos + len; return len; } } @@ -9123,14 +9127,15 @@ { loff_t len; tempinfo_t *p_info = (tempinfo_t *) file->private_data; + loff_t pos = *offset; - if ( *offset>=p_info->len) { + if (pos < 0 || pos >= p_info->len) { return 0; } else { - len = MIN(user_len, (p_info->len - *offset)); - if (copy_to_user( user_buf, &(p_info->data[*offset]), len)) + len = MIN(user_len, (p_info->len - pos)); + if (copy_to_user( user_buf, &(p_info->data[pos]), len)) return -EFAULT; - (* offset) += len; + *offset = pos + len; return len; } } diff -urN linux-2.4.26/drivers/sbus/char/flash.c linux-2.4.27/drivers/sbus/char/flash.c --- linux-2.4.26/drivers/sbus/char/flash.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/sbus/char/flash.c 2004-08-07 16:26:05.488381047 -0700 @@ -105,9 +105,15 @@ flash_read(struct file * file, char * buf, size_t count, loff_t *ppos) { - unsigned long p = file->f_pos; + loff_t p = *ppos; int i; + if (p > flash.read_size) + return 0; + + if (p < 0) + return -EINVAL; + if (count > flash.read_size - p) count = flash.read_size - p; @@ -118,7 +124,7 @@ buf++; } - file->f_pos += count; + *ppos = p + count; return count; } diff -urN linux-2.4.26/drivers/scsi/ChangeLog.ips linux-2.4.27/drivers/scsi/ChangeLog.ips --- linux-2.4.26/drivers/scsi/ChangeLog.ips 2003-06-13 07:51:36.000000000 -0700 +++ linux-2.4.27/drivers/scsi/ChangeLog.ips 2004-08-07 16:26:05.489381088 -0700 @@ -1,6 +1,13 @@ IBM ServeRAID driver Change Log ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 7.00.xx - Linux 2.6 Kernel Changes + + 6.11.07 - Make Logical Drive Info structure safe for DMA + - Get VersionInfo buffer off the stack ! + + 6.10.24 - Remove 1G Addressing Limitations + 6.00.00 - Add 6x Adapters and Battery Flash 5.30.00 - use __devexit_p() diff -urN linux-2.4.26/drivers/scsi/Config.in linux-2.4.27/drivers/scsi/Config.in --- linux-2.4.26/drivers/scsi/Config.in 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/Config.in 2004-08-07 16:26:05.490381129 -0700 @@ -68,6 +68,14 @@ dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI $CONFIG_PCI dep_tristate 'AMI MegaRAID support' CONFIG_SCSI_MEGARAID $CONFIG_SCSI dep_tristate 'AMI MegaRAID2 support' CONFIG_SCSI_MEGARAID2 $CONFIG_SCSI +dep_mbool 'Serial ATA (SATA) support' CONFIG_SCSI_SATA $CONFIG_SCSI +dep_tristate ' ServerWorks Frodo / Apple K2 SATA support (EXPERIMENTAL)' CONFIG_SCSI_SATA_SVW $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' Promise SATA TX2/TX4 support (EXPERIMENTAL)' CONFIG_SCSI_SATA_PROMISE $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' Promise SATA SX4 support (EXPERIMENTAL)' CONFIG_SCSI_SATA_SX4 $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' Silicon Image SATA support (EXPERIMENTAL)' CONFIG_SCSI_SATA_SIL $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' SiS 964/180 SATA support (EXPERIMENTAL)' CONFIG_SCSI_SATA_SIS $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' VIA SATA support (EXPERIMENTAL)' CONFIG_SCSI_SATA_VIA $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL +dep_tristate ' Vitesse VSC-7174 SATA support (EXPERIMENTAL)' CONFIG_SCSI_SATA_VITESSE $CONFIG_SCSI_SATA $CONFIG_PCI $CONFIG_EXPERIMENTAL dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then diff -urN linux-2.4.26/drivers/scsi/Makefile linux-2.4.27/drivers/scsi/Makefile --- linux-2.4.26/drivers/scsi/Makefile 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/Makefile 2004-08-07 16:26:05.490381129 -0700 @@ -21,7 +21,7 @@ O_TARGET := scsidrv.o -export-objs := scsi_syms.o 53c700.o +export-objs := scsi_syms.o 53c700.o libata-core.o mod-subdirs := pcmcia ../acorn/scsi @@ -130,6 +130,13 @@ obj-$(CONFIG_SCSI_CPQFCTS) += cpqfc.o obj-$(CONFIG_SCSI_LASI700) += lasi700.o 53c700.o obj-$(CONFIG_SCSI_NSP32) += nsp32.o +obj-$(CONFIG_SCSI_SATA_SVW) += libata.o sata_svw.o +obj-$(CONFIG_SCSI_SATA_PROMISE) += libata.o sata_promise.o +obj-$(CONFIG_SCSI_SATA_SIL) += libata.o sata_sil.o +obj-$(CONFIG_SCSI_SATA_VIA) += libata.o sata_via.o +obj-$(CONFIG_SCSI_SATA_VITESSE) += libata.o sata_vsc.o +obj-$(CONFIG_SCSI_SATA_SIS) += libata.o sata_sis.o +obj-$(CONFIG_SCSI_SATA_SX4) += libata.o sata_sx4.o subdir-$(CONFIG_ARCH_ACORN) += ../acorn/scsi obj-$(CONFIG_ARCH_ACORN) += ../acorn/scsi/acorn-scsi.o @@ -141,7 +148,7 @@ obj-$(CONFIG_CHR_DEV_SG) += sg.o list-multi := scsi_mod.o sd_mod.o sr_mod.o initio.o a100u2w.o cpqfc.o \ - zalon7xx_mod.o + zalon7xx_mod.o libata.o scsi_mod-objs := scsi.o hosts.o scsi_ioctl.o constants.o \ scsicam.o scsi_proc.o scsi_error.o \ scsi_obsolete.o scsi_queue.o scsi_lib.o \ @@ -154,6 +161,7 @@ zalon7xx_mod-objs := zalon7xx.o ncr53c8xx.o cpqfc-objs := cpqfcTSinit.o cpqfcTScontrol.o cpqfcTSi2c.o \ cpqfcTSworker.o cpqfcTStrigger.o +libata-objs := libata-core.o libata-scsi.o include $(TOPDIR)/Rules.make @@ -179,6 +187,9 @@ cpqfc.o: $(cpqfc-objs) $(LD) -r -o $@ $(cpqfc-objs) +libata.o: $(libata-objs) + $(LD) -r -o $@ $(libata-objs) + 53c8xx_d.h: 53c7,8xx.scr script_asm.pl ln -sf 53c7,8xx.scr fake8.c $(CPP) $(CPPFLAGS) -traditional -DCHIP=810 fake8.c | grep -v '^#' | $(PERL) script_asm.pl diff -urN linux-2.4.26/drivers/scsi/NCR53C9x.c linux-2.4.27/drivers/scsi/NCR53C9x.c --- linux-2.4.26/drivers/scsi/NCR53C9x.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.27/drivers/scsi/NCR53C9x.c 2004-08-07 16:26:05.492381211 -0700 @@ -917,7 +917,7 @@ if (esp->dma_mmu_get_scsi_one) esp->dma_mmu_get_scsi_one(esp, sp); else - sp->SCp.have_data_in = (int) sp->SCp.ptr = + sp->SCp.ptr = (char *) virt_to_phys(sp->request_buffer); } else { sp->SCp.buffer = (struct scatterlist *) sp->buffer; diff -urN linux-2.4.26/drivers/scsi/ips.c linux-2.4.27/drivers/scsi/ips.c --- linux-2.4.26/drivers/scsi/ips.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/ips.c 2004-08-07 16:26:05.502381622 -0700 @@ -170,12 +170,11 @@ #include #include -#include +#include #include #include -#include "sd.h" #include "scsi.h" #include "hosts.h" #include "ips.h" @@ -198,17 +197,22 @@ /* * DRIVER_VER */ -#define IPS_VERSION_HIGH "6.11" -#define IPS_VERSION_LOW ".07 " +#define IPS_VERSION_HIGH "7.00" +#define IPS_VERSION_LOW ".15 " #if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__) #warning "This driver has only been tested on the x86/ia64/x86_64 platforms" #endif -#if LINUX_VERSION_CODE <= LinuxVersionCode(2,5,0) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) +#include +#include "sd.h" #define IPS_SG_ADDRESS(sg) ((sg)->address) #define IPS_LOCK_SAVE(lock,flags) spin_lock_irqsave(&io_request_lock,flags) #define IPS_UNLOCK_RESTORE(lock,flags) spin_unlock_irqrestore(&io_request_lock,flags) +#ifndef __devexit_p +#define __devexit_p(x) x +#endif #else #define IPS_SG_ADDRESS(sg) (page_address((sg)->page) ? \ page_address((sg)->page)+(sg)->offset : 0) @@ -240,42 +244,70 @@ static unsigned int ips_next_controller; static unsigned int ips_num_controllers; static unsigned int ips_released_controllers; +static int ips_hotplug; static int ips_cmd_timeout = 60; static int ips_reset_timeout = 60 * 5; -static int ips_force_memio = 1; /* Always use Memory Mapped I/O */ +static int ips_force_memio = 1; /* Always use Memory Mapped I/O */ static int ips_force_i2o = 1; /* Always use I2O command delivery */ static int ips_ioctlsize = IPS_IOCTL_SIZE; /* Size of the ioctl buffer */ -static int ips_cd_boot; /* Booting from Manager CD */ +static int ips_cd_boot; /* Booting from Manager CD */ static char *ips_FlashData = NULL; /* CD Boot - Flash Data Buffer */ static dma_addr_t ips_flashbusaddr; -static long ips_FlashDataInUse; /* CD Boot - Flash Data In Use Flag */ +static long ips_FlashDataInUse; /* CD Boot - Flash Data In Use Flag */ static uint32_t MaxLiteCmds = 32; /* Max Active Cmds for a Lite Adapter */ -static Scsi_Host_Template ips_driver_template = IPS; +static Scsi_Host_Template ips_driver_template = { + .detect = ips_detect, + .release = ips_release, + .info = ips_info, + .queuecommand = ips_queue, + .eh_abort_handler = ips_eh_abort, + .eh_host_reset_handler = ips_eh_reset, + .proc_name = "ips", +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + .proc_info = ips_proc_info, + .slave_configure = ips_slave_configure, +#else + .proc_info = ips_proc24_info, + .select_queue_depths = ips_select_queue_depth, +#endif + .bios_param = ips_biosparam, + .this_id = -1, + .sg_tablesize = IPS_MAX_SG, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + .use_new_eh_code = 1, +#ifdef RHEL3 + .vary_io = 1, +#endif +#endif +}; + +IPS_DEFINE_COMPAT_TABLE( Compatable ); /* Version Compatability Table */ -IPS_DEFINE_COMPAT_TABLE(Compatable); /* Version Compatability Table */ - /* This table describes all ServeRAID Adapters */ -static struct pci_device_id ips_pci_table[] __devinitdata = { - {0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0}, - {0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0}, - {0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0}, - {0,} +/* This table describes all ServeRAID Adapters */ +static struct pci_device_id ips_pci_table[] = { + { 0x1014, 0x002E, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0x1014, 0x01BD, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0x9005, 0x0250, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, + { 0, } }; -MODULE_DEVICE_TABLE(pci, ips_pci_table); +MODULE_DEVICE_TABLE( pci, ips_pci_table ); static char ips_hot_plug_name[] = "ips"; - -static int __devinit ips_insert_device(struct pci_dev *pci_dev, - const struct pci_device_id *ent); -static void ips_remove_device(struct pci_dev *pci_dev); - + +static int __devinit ips_insert_device(struct pci_dev *pci_dev, const struct pci_device_id *ent); +static void __devexit ips_remove_device(struct pci_dev *pci_dev); + struct pci_driver ips_pci_driver = { - .name = ips_hot_plug_name, - .id_table = ips_pci_table, - .probe = ips_insert_device, - .remove = ips_remove_device, + .name = ips_hot_plug_name, + .id_table = ips_pci_table, + .probe = ips_insert_device, + .remove = __devexit_p(ips_remove_device), }; + /* * Necessary forward function protoypes @@ -299,7 +331,10 @@ "ServeRAID 5i", "ServeRAID 5i", "ServeRAID 6M", - "ServeRAID 6i" + "ServeRAID 6i", + "ServeRAID 7t", + "ServeRAID 7k", + "ServeRAID 7M" }; static struct notifier_block ips_notifier = { @@ -371,9 +406,8 @@ int ips_eh_abort(Scsi_Cmnd *); int ips_eh_reset(Scsi_Cmnd *); int ips_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); -int ips_biosparam(Disk *, kdev_t, int *); const char *ips_info(struct Scsi_Host *); -void do_ipsintr(int, void *, struct pt_regs *); +irqreturn_t do_ipsintr(int, void *, struct pt_regs *); static int ips_hainit(ips_ha_t *); static int ips_map_status(ips_ha_t *, ips_scb_t *, ips_stat_t *); static int ips_send_wait(ips_ha_t *, ips_scb_t *, int, int); @@ -424,8 +458,8 @@ static void ips_enable_int_copperhead(ips_ha_t *); static void ips_enable_int_copperhead_memio(ips_ha_t *); static void ips_enable_int_morpheus(ips_ha_t *); -static void ips_intr_copperhead(ips_ha_t *); -static void ips_intr_morpheus(ips_ha_t *); +static int ips_intr_copperhead(ips_ha_t *); +static int ips_intr_morpheus(ips_ha_t *); static void ips_next(ips_ha_t *, int); static void ipsintr_blocking(ips_ha_t *, struct ips_scb *); static void ipsintr_done(ips_ha_t *, struct ips_scb *); @@ -467,7 +501,7 @@ unsigned int count); static void ips_scmd_buf_read(Scsi_Cmnd * scmd, void *data, unsigned int count); -int ips_proc_info(char *, char **, off_t, int, int, int); +int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); static int ips_host_info(ips_ha_t *, char *, off_t, int); static void copy_mem_info(IPS_INFOSTR *, char *, int); static int copy_info(IPS_INFOSTR *, char *, ...); @@ -561,15 +595,12 @@ ips_setup(ips); #endif - SHT->proc_info = ips_proc_info; - SHT->proc_name = "ips"; - for (i = 0; i < ips_num_controllers; i++) { if (ips_register_scsi(i)) ips_free(ips_ha[i]); ips_released_controllers++; } - + ips_hotplug = 1; return (ips_num_controllers); } @@ -680,15 +711,13 @@ scb->cmd.flush_cache.reserved3 = 0; scb->cmd.flush_cache.reserved4 = 0; - printk(KERN_NOTICE "(%s%d) Flushing Cache.\n", ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n"); /* send command */ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == IPS_FAILURE) - printk(KERN_NOTICE "(%s%d) Incomplete Flush.\n", ips_name, - ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, "Incomplete Flush.\n"); - printk(KERN_NOTICE "(%s%d) Flushing Complete.\n", ips_name, - ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Complete.\n"); ips_sh[i] = NULL; ips_ha[i] = NULL; @@ -703,7 +732,8 @@ /* free IRQ */ free_irq(ha->irq, ha); - scsi_unregister(sh); + IPS_REMOVE_HOST(sh); + scsi_host_put(sh); ips_released_controllers++; @@ -727,7 +757,8 @@ int i; if ((event != SYS_RESTART) && (event != SYS_HALT) && - (event != SYS_POWER_OFF)) return (NOTIFY_DONE); + (event != SYS_POWER_OFF)) + return (NOTIFY_DONE); for (i = 0; i < ips_next_controller; i++) { ha = (ips_ha_t *) ips_ha[i]; @@ -754,17 +785,16 @@ scb->cmd.flush_cache.reserved3 = 0; scb->cmd.flush_cache.reserved4 = 0; - printk(KERN_NOTICE "(%s%d) Flushing Cache.\n", ips_name, - ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, "Flushing Cache.\n"); /* send command */ if (ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_ON) == - IPS_FAILURE) printk(KERN_NOTICE - "(%s%d) Incomplete Flush.\n", ips_name, - ha->host_num); + IPS_FAILURE) + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Incomplete Flush.\n"); else - printk(KERN_NOTICE "(%s%d) Flushing Complete.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Flushing Complete.\n"); } return (NOTIFY_OK); @@ -791,7 +821,7 @@ if (!SC) return (FAILED); - ha = (ips_ha_t *) SC->host->hostdata; + ha = (ips_ha_t *) SC->device->host->hostdata; if (!ha) return (FAILED); @@ -859,7 +889,7 @@ return (FAILED); } - ha = (ips_ha_t *) SC->host->hostdata; + ha = (ips_ha_t *) SC->device->host->hostdata; if (!ha) { DEBUG(1, "Reset called with NULL ha struct"); @@ -916,9 +946,8 @@ /* Attempt the flush command */ ret = ips_send_wait(ha, scb, ips_cmd_timeout, IPS_INTR_IORL); if (ret == IPS_SUCCESS) { - printk(KERN_NOTICE - "(%s%d) Reset Request - Flushed Cache\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_NOTICE, ha->pcidev, + "Reset Request - Flushed Cache\n"); return (SUCCESS); } } @@ -932,16 +961,14 @@ * command must have already been sent * reset the controller */ - printk(KERN_NOTICE "(%s%d) Resetting controller.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Resetting controller.\n"); ret = (*ha->func.reset) (ha); if (!ret) { Scsi_Cmnd *scsi_cmd; - printk(KERN_NOTICE - "(%s%d) Controller reset failed - controller now offline.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_NOTICE, ha->pcidev, + "Controller reset failed - controller now offline.\n"); /* Now fail all of the active commands */ DEBUG_VAR(1, "(%s%d) Failing active commands", @@ -969,9 +996,8 @@ if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { Scsi_Cmnd *scsi_cmd; - printk(KERN_NOTICE - "(%s%d) Controller reset failed - controller now offline.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_NOTICE, ha->pcidev, + "Controller reset failed - controller now offline.\n"); /* Now fail all of the active commands */ DEBUG_VAR(1, "(%s%d) Failing active commands", @@ -1050,7 +1076,7 @@ METHOD_TRACE("ips_queue", 1); - ha = (ips_ha_t *) SC->host->hostdata; + ha = (ips_ha_t *) SC->device->host->hostdata; if (!ha) return (1); @@ -1076,10 +1102,13 @@ DEBUG_VAR(2, "(%s%d): ips_queue: cmd 0x%X (%d %d %d)", ips_name, - ha->host_num, SC->cmnd[0], SC->channel, SC->target, SC->lun); + ha->host_num, + SC->cmnd[0], + SC->device->channel, SC->device->id, SC->device->lun); /* Check for command to initiator IDs */ - if ((SC->channel > 0) && (SC->target == ha->ha_id[SC->channel])) { + if ((SC->device->channel > 0) + && (SC->device->id == ha->ha_id[SC->device->channel])) { SC->result = DID_NO_CONNECT << 16; done(SC); @@ -1140,18 +1169,24 @@ /* Set bios geometry for the controller */ /* */ /****************************************************************************/ -int +static int +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) ips_biosparam(Disk * disk, kdev_t dev, int geom[]) { - ips_ha_t *ha; + ips_ha_t *ha = (ips_ha_t *) disk->device->host->hostdata; + unsigned long capacity = disk->capacity; +#else +ips_biosparam(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int geom[]) +{ + ips_ha_t *ha = (ips_ha_t *) sdev->host->hostdata; +#endif int heads; int sectors; int cylinders; METHOD_TRACE("ips_biosparam", 1); - ha = (ips_ha_t *) disk->device->host->hostdata; - if (!ha) /* ?!?! host adater info invalid */ return (0); @@ -1163,7 +1198,7 @@ /* ?!?! Enquiry command failed */ return (0); - if ((disk->capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) { + if ((capacity > 0x400000) && ((ha->enq->ucMiscFlag & 0x8) == 0)) { heads = IPS_NORM_HEADS; sectors = IPS_NORM_SECTORS; } else { @@ -1171,7 +1206,7 @@ sectors = IPS_COMP_SECTORS; } - cylinders = disk->capacity / (heads * sectors); + cylinders = (unsigned long) capacity / (heads * sectors); DEBUG_VAR(2, "Geometry: heads: %d, sectors: %d, cylinders: %d", heads, sectors, cylinders); @@ -1183,7 +1218,25 @@ return (0); } -#if LINUX_VERSION_CODE < LinuxVersionCode(2,5,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + +/* ips_proc24_info is a wrapper around ips_proc_info * + * for compatibility with the 2.4 scsi parameters */ +static int +ips_proc24_info(char *buffer, char **start, off_t offset, int length, + int hostno, int func) +{ + int i; + + for (i = 0; i < ips_next_controller; i++) { + if (ips_sh[i] && ips_sh[i]->host_no == hostno) { + return ips_proc_info(ips_sh[i], buffer, start, + offset, length, func); + } + } + return -EINVAL; +} + /****************************************************************************/ /* */ /* Routine Name: ips_select_queue_depth */ @@ -1264,38 +1317,40 @@ /* Wrapper for the interrupt handler */ /* */ /****************************************************************************/ -void -do_ipsintr(int irq, void *dev_id, struct pt_regs *regs) +irqreturn_t +do_ipsintr(int irq, void *dev_id, struct pt_regs * regs) { ips_ha_t *ha; unsigned long cpu_flags; struct Scsi_Host *host; + int irqstatus; METHOD_TRACE("do_ipsintr", 2); ha = (ips_ha_t *) dev_id; if (!ha) - return; + return IRQ_NONE; host = ips_sh[ha->host_num]; /* interrupt during initialization */ if (!host) { (*ha->func.intr) (ha); - return; + return IRQ_HANDLED; } IPS_LOCK_SAVE(host->host_lock, cpu_flags); if (!ha->active) { IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags); - return; + return IRQ_HANDLED; } - (*ha->func.intr) (ha); + irqstatus = (*ha->func.intr) (ha); IPS_UNLOCK_RESTORE(host->host_lock, cpu_flags); /* start the next command */ ips_next(ha, IPS_INTR_ON); + return IRQ_RETVAL(irqstatus); } /****************************************************************************/ @@ -1309,7 +1364,7 @@ /* ASSUMES interrupts are disabled */ /* */ /****************************************************************************/ -void +int ips_intr_copperhead(ips_ha_t * ha) { ips_stat_t *sp; @@ -1320,10 +1375,10 @@ METHOD_TRACE("ips_intr", 2); if (!ha) - return; + return 0; if (!ha->active) - return; + return 0; intrstatus = (*ha->func.isintr) (ha); @@ -1332,7 +1387,7 @@ * Unexpected/Shared interrupt */ - return; + return 0; } while (TRUE) { @@ -1359,6 +1414,7 @@ */ (*scb->callback) (ha, scb); } /* end while */ + return 1; } /****************************************************************************/ @@ -1372,7 +1428,7 @@ /* ASSUMES interrupts are disabled */ /* */ /****************************************************************************/ -void +int ips_intr_morpheus(ips_ha_t * ha) { ips_stat_t *sp; @@ -1383,10 +1439,10 @@ METHOD_TRACE("ips_intr_morpheus", 2); if (!ha) - return; + return 0; if (!ha->active) - return; + return 0; intrstatus = (*ha->func.isintr) (ha); @@ -1395,7 +1451,7 @@ * Unexpected/Shared interrupt */ - return; + return 0; } while (TRUE) { @@ -1413,9 +1469,8 @@ break; if (cstatus.fields.command_id > (IPS_MAX_CMDS - 1)) { - printk(KERN_WARNING - "(%s%d) Spurious interrupt; no ccb.\n", ips_name, - ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Spurious interrupt; no ccb.\n"); continue; } @@ -1429,6 +1484,7 @@ */ (*scb->callback) (ha, scb); } /* end while */ + return 1; } /****************************************************************************/ @@ -1479,8 +1535,8 @@ /* */ /****************************************************************************/ int -ips_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int func) +ips_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, + int length, int func) { int i; int ret; @@ -1491,7 +1547,7 @@ /* Find our host structure */ for (i = 0; i < ips_next_controller; i++) { if (ips_sh[i]) { - if (ips_sh[i]->host_no == hostno) { + if (ips_sh[i] == host) { ha = (ips_ha_t *) ips_sh[i]->hostdata; break; } @@ -1537,9 +1593,9 @@ return (0); if ((SC->cmnd[0] == IPS_IOCTL_COMMAND) && - (SC->channel == 0) && - (SC->target == IPS_ADAPTER_ID) && - (SC->lun == 0) && SC->request_buffer) { + (SC->device->channel == 0) && + (SC->device->id == IPS_ADAPTER_ID) && + (SC->device->lun == 0) && SC->request_buffer) { if ((!SC->use_sg) && SC->request_bufflen && (((char *) SC->request_buffer)[0] == 'C') && (((char *) SC->request_buffer)[1] == 'O') && @@ -1671,7 +1727,8 @@ } if (ha->device_id == IPS_DEVICEID_COPPERHEAD && - pt->CoppCP.cmd.flashfw.op_code == IPS_CMD_RW_BIOSFW) { + pt->CoppCP.cmd.flashfw.op_code == + IPS_CMD_RW_BIOSFW) { ret = ips_flash_copperhead(ha, pt, scb); ips_scmd_buf_write(SC, ha->ioctl_data, sizeof (ips_passthru_t)); @@ -1741,7 +1798,8 @@ if (pt->CoppCP.cmd.flashfw.count + ha->flash_datasize > ha->flash_len) { ips_free_flash_copperhead(ha); - printk(KERN_WARNING "failed size sanity check\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "failed size sanity check\n"); return IPS_FAILURE; } } @@ -1773,24 +1831,29 @@ if (pt->CoppCP.cmd.flashfw.type == IPS_BIOS_IMAGE && pt->CoppCP.cmd.flashfw.direction == IPS_WRITE_BIOS) { if ((!ha->func.programbios) || (!ha->func.erasebios) || - (!ha->func.verifybios)) goto error; + (!ha->func.verifybios)) + goto error; if ((*ha->func.erasebios) (ha)) { DEBUG_VAR(1, "(%s%d) flash bios failed - unable to erase flash", ips_name, ha->host_num); goto error; } else - if ((*ha->func.programbios) - (ha, ha->flash_data + IPS_BIOS_HEADER, - ha->flash_datasize - IPS_BIOS_HEADER, 0)) { + if ((*ha->func.programbios) (ha, + ha->flash_data + + IPS_BIOS_HEADER, + ha->flash_datasize - + IPS_BIOS_HEADER, 0)) { DEBUG_VAR(1, "(%s%d) flash bios failed - unable to flash", ips_name, ha->host_num); goto error; } else - if ((*ha->func.verifybios) - (ha, ha->flash_data + IPS_BIOS_HEADER, - ha->flash_datasize - IPS_BIOS_HEADER, 0)) { + if ((*ha->func.verifybios) (ha, + ha->flash_data + + IPS_BIOS_HEADER, + ha->flash_datasize - + IPS_BIOS_HEADER, 0)) { DEBUG_VAR(1, "(%s%d) flash bios failed - unable to verify flash", ips_name, ha->host_num); @@ -1823,7 +1886,7 @@ /* */ /* Routine Description: */ /* Fill in a single scb sg_list element from an address */ -/* return a -1 if a breakup occured */ +/* return a -1 if a breakup occurred */ /****************************************************************************/ static inline int ips_fill_scb_sg_single(ips_ha_t * ha, dma_addr_t busaddr, @@ -1888,9 +1951,9 @@ /* FIX stuff that might be wrong */ scb->sg_list.list = sg_list.list; scb->scb_busaddr = cmd_busaddr; - scb->bus = scb->scsi_cmd->channel; - scb->target_id = scb->scsi_cmd->target; - scb->lun = scb->scsi_cmd->lun; + scb->bus = scb->scsi_cmd->device->channel; + scb->target_id = scb->scsi_cmd->device->id; + scb->lun = scb->scsi_cmd->device->lun; scb->sg_len = 0; scb->data_len = 0; scb->flags = 0; @@ -1957,9 +2020,9 @@ /* FIX stuff that might be wrong */ scb->sg_list.list = sg_list.list; scb->scb_busaddr = cmd_busaddr; - scb->bus = scb->scsi_cmd->channel; - scb->target_id = scb->scsi_cmd->target; - scb->lun = scb->scsi_cmd->lun; + scb->bus = scb->scsi_cmd->device->channel; + scb->target_id = scb->scsi_cmd->device->id; + scb->lun = scb->scsi_cmd->device->lun; scb->sg_len = 0; scb->data_len = 0; scb->flags = 0; @@ -2103,27 +2166,60 @@ copy_info(&info, "\tIRQ number : %d\n", ha->irq); - if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) - copy_info(&info, - "\tBIOS Version : %c%c%c%c%c%c%c%c\n", - ha->nvram->bios_high[0], ha->nvram->bios_high[1], - ha->nvram->bios_high[2], ha->nvram->bios_high[3], - ha->nvram->bios_low[0], ha->nvram->bios_low[1], - ha->nvram->bios_low[2], ha->nvram->bios_low[3]); - - copy_info(&info, - "\tFirmware Version : %c%c%c%c%c%c%c%c\n", - ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1], - ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3], - ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5], - ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]); - - copy_info(&info, - "\tBoot Block Version : %c%c%c%c%c%c%c%c\n", - ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1], - ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3], - ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5], - ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]); + /* For the Next 3 lines Check for Binary 0 at the end and don't include it if it's there. */ + /* That keeps everything happy for "text" operations on the proc file. */ + + if (le32_to_cpu(ha->nvram->signature) == IPS_NVRAM_P5_SIG) { + if (ha->nvram->bios_low[3] == 0) { + copy_info(&info, + "\tBIOS Version : %c%c%c%c%c%c%c\n", + ha->nvram->bios_high[0], ha->nvram->bios_high[1], + ha->nvram->bios_high[2], ha->nvram->bios_high[3], + ha->nvram->bios_low[0], ha->nvram->bios_low[1], + ha->nvram->bios_low[2]); + + } else { + copy_info(&info, + "\tBIOS Version : %c%c%c%c%c%c%c%c\n", + ha->nvram->bios_high[0], ha->nvram->bios_high[1], + ha->nvram->bios_high[2], ha->nvram->bios_high[3], + ha->nvram->bios_low[0], ha->nvram->bios_low[1], + ha->nvram->bios_low[2], ha->nvram->bios_low[3]); + } + + } + + if (ha->enq->CodeBlkVersion[7] == 0) { + copy_info(&info, + "\tFirmware Version : %c%c%c%c%c%c%c\n", + ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1], + ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3], + ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5], + ha->enq->CodeBlkVersion[6]); + } else { + copy_info(&info, + "\tFirmware Version : %c%c%c%c%c%c%c%c\n", + ha->enq->CodeBlkVersion[0], ha->enq->CodeBlkVersion[1], + ha->enq->CodeBlkVersion[2], ha->enq->CodeBlkVersion[3], + ha->enq->CodeBlkVersion[4], ha->enq->CodeBlkVersion[5], + ha->enq->CodeBlkVersion[6], ha->enq->CodeBlkVersion[7]); + } + + if (ha->enq->BootBlkVersion[7] == 0) { + copy_info(&info, + "\tBoot Block Version : %c%c%c%c%c%c%c\n", + ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1], + ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3], + ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5], + ha->enq->BootBlkVersion[6]); + } else { + copy_info(&info, + "\tBoot Block Version : %c%c%c%c%c%c%c%c\n", + ha->enq->BootBlkVersion[0], ha->enq->BootBlkVersion[1], + ha->enq->BootBlkVersion[2], ha->enq->BootBlkVersion[3], + ha->enq->BootBlkVersion[4], ha->enq->BootBlkVersion[5], + ha->enq->BootBlkVersion[6], ha->enq->BootBlkVersion[7]); + } copy_info(&info, "\tDriver Version : %s%s\n", IPS_VERSION_HIGH, IPS_VERSION_LOW); @@ -2285,6 +2381,12 @@ case IPS_SUBDEVICEID_6I: ha->ad_type = IPS_ADTYPE_SERVERAID6I; break; + case IPS_SUBDEVICEID_7k: + ha->ad_type = IPS_ADTYPE_SERVERAID7k; + break; + case IPS_SUBDEVICEID_7M: + ha->ad_type = IPS_ADTYPE_SERVERAID7M; + break; } break; } @@ -2310,7 +2412,7 @@ uint8_t *buffer; char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', - 'D', 'E', 'F' }; + 'D', 'E', 'F' }; METHOD_TRACE("ips_get_bios_version", 1); @@ -2422,9 +2524,10 @@ scb->cmd.flashfw.buffer_addr = ha->ioctl_busaddr; /* issue the command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_cmd_timeout, + intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) { /* Error occurred */ @@ -2485,17 +2588,15 @@ ips_ffdc_reset(ha, IPS_INTR_IORL); if (!ips_read_config(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING - "(%s%d) unable to read config from controller.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "unable to read config from controller.\n"); return (0); } /* end if */ if (!ips_read_adapter_status(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING - "(%s%d) unable to read controller status.\n", ips_name, - ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "unable to read controller status.\n"); return (0); } @@ -2504,18 +2605,16 @@ ips_identify_controller(ha); if (!ips_read_subsystem_parameters(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING - "(%s%d) unable to read subsystem parameters.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "unable to read subsystem parameters.\n"); return (0); } /* write nvram user page 5 */ if (!ips_write_driver_status(ha, IPS_INTR_IORL)) { - printk(KERN_WARNING - "(%s%d) unable to write driver info to controller.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "unable to write driver info to controller.\n"); return (0); } @@ -2713,8 +2812,10 @@ p = ha->scb_waitlist.head; while ((p) && (scb = ips_getscb(ha))) { - if ((p->channel > 0) - && (ha->dcdb_active[p->channel - 1] & (1 << p->target))) { + if ((p->device->channel > 0) + && (ha-> + dcdb_active[p->device->channel - + 1] & (1 << p->device->id))) { ips_freescb(ha, scb); p = (Scsi_Cmnd *) p->host_scribble; continue; @@ -2731,9 +2832,9 @@ memset(SC->sense_buffer, 0, sizeof (SC->sense_buffer)); - scb->target_id = SC->target; - scb->lun = SC->lun; - scb->bus = SC->channel; + scb->target_id = SC->device->id; + scb->lun = SC->device->lun; + scb->bus = SC->device->channel; scb->scsi_cmd = SC; scb->breakup = 0; scb->data_len = 0; @@ -3301,17 +3402,16 @@ METHOD_TRACE("ipsintr_done", 2); if (!scb) { - printk(KERN_WARNING "(%s%d) Spurious interrupt; scb NULL.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Spurious interrupt; scb NULL.\n"); return; } if (scb->scsi_cmd == NULL) { /* unexpected interrupt */ - printk(KERN_WARNING - "(%s%d) Spurious interrupt; scsi_cmd not set.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Spurious interrupt; scsi_cmd not set.\n"); return; } @@ -3465,8 +3565,9 @@ if (scb->bus) { DEBUG_VAR(2, "(%s%d) Physical device error (%d %d %d): %x %x, Sense Key: %x, ASC: %x, ASCQ: %x", - ips_name, ha->host_num, scb->scsi_cmd->channel, - scb->scsi_cmd->target, scb->scsi_cmd->lun, + ips_name, ha->host_num, + scb->scsi_cmd->device->channel, + scb->scsi_cmd->device->id, scb->scsi_cmd->device->lun, scb->basic_status, scb->extended_status, scb->extended_status == IPS_ERR_CKCOND ? scb->dcdb.sense_info[2] & 0xf : 0, @@ -3502,7 +3603,8 @@ case IPS_ERR_OU_RUN: if ((scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB) || - (scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB_SG)) { + (scb->cmd.dcdb.op_code == + IPS_CMD_EXTENDED_DCDB_SG)) { tapeDCDB = (IPS_DCDB_TABLE_TAPE *) & scb->dcdb; transfer_len = tapeDCDB->transfer_length; } else { @@ -3516,9 +3618,8 @@ /* Restrict access to physical DASD */ if ((scb->scsi_cmd->cmnd[0] == INQUIRY) && - ((((char - *) scb->scsi_cmd->buffer)[0] & 0x1f) == - TYPE_DISK)) { + ((((char *) scb->scsi_cmd-> + buffer)[0] & 0x1f) == TYPE_DISK)) { /* underflow -- no error */ /* restrict access to physical DASD */ errcode = DID_TIME_OUT; @@ -3543,8 +3644,7 @@ case IPS_ERR_CKCOND: if (scb->bus) { - if ( - (scb->cmd.dcdb.op_code == + if ((scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB) || (scb->cmd.dcdb.op_code == IPS_CMD_EXTENDED_DCDB_SG)) { @@ -3822,13 +3922,10 @@ sector_count)); else scb->cmd.basic_io.lba = - (((scb-> - scsi_cmd->cmnd[1] & 0x1f) << 16) | (scb-> - scsi_cmd-> - cmnd - [2] - << 8) - | (scb->scsi_cmd->cmnd[3])); + (((scb->scsi_cmd-> + cmnd[1] & 0x1f) << 16) | (scb->scsi_cmd-> + cmnd[2] << 8) | + (scb->scsi_cmd->cmnd[3])); scb->cmd.basic_io.sector_count = cpu_to_le16(scb->data_len / IPS_BLKSIZE); @@ -3873,11 +3970,11 @@ sector_count)); else scb->cmd.basic_io.lba = - ((scb-> - scsi_cmd->cmnd[2] << 24) | (scb-> - scsi_cmd-> - cmnd[3] << 16) - | (scb->scsi_cmd->cmnd[4] << 8) | scb-> + ((scb->scsi_cmd->cmnd[2] << 24) | (scb-> + scsi_cmd-> + cmnd[3] + << 16) | + (scb->scsi_cmd->cmnd[4] << 8) | scb-> scsi_cmd->cmnd[5]); scb->cmd.basic_io.sector_count = @@ -4253,7 +4350,8 @@ && ha->logical_drive_info->drive_info[scb->target_id].state != IPS_LD_CRS && ha->logical_drive_info->drive_info[scb->target_id].state != - IPS_LD_SYS) return (1); + IPS_LD_SYS) + return (1); else return (0); } @@ -4397,6 +4495,13 @@ mdata.pdata.pg4.RotationalOffset = 0; mdata.pdata.pg4.MediumRotationRate = 0; break; + case 0x8: + mdata.pdata.pg8.PageCode = 8; + mdata.pdata.pg8.PageLength = sizeof (IPS_SCSI_MODE_PAGE8); + mdata.hdr.DataLength = + 3 + mdata.hdr.BlockDescLength + mdata.pdata.pg8.PageLength; + /* everything else is left set to 0 */ + break; default: return (0); @@ -4469,9 +4574,9 @@ sizeof (IPS_IO_CMD), ha->adapt, ha->adapt->hw_status_start); ha->adapt = NULL; - } - - if (ha->logical_drive_info) { + } + + if (ha->logical_drive_info) { pci_free_consistent(ha->pcidev, sizeof (IPS_LD_INFO), ha->logical_drive_info, @@ -4800,7 +4905,7 @@ METHOD_TRACE("ips_enable_int_copperhead", 1); outb(ha->io_addr + IPS_REG_HISR, IPS_BIT_EI); - inb(ha->io_addr + IPS_REG_HISR); // Ensure PCI Posting Completes + inb(ha->io_addr + IPS_REG_HISR); /*Ensure PCI Posting Completes*/ } /****************************************************************************/ @@ -4817,7 +4922,7 @@ METHOD_TRACE("ips_enable_int_copperhead_memio", 1); writeb(IPS_BIT_EI, ha->mem_ptr + IPS_REG_HISR); - readb(ha->mem_ptr + IPS_REG_HISR); // Ensure PCI Posting Completes + readb(ha->mem_ptr + IPS_REG_HISR); /*Ensure PCI Posting Completes*/ } /****************************************************************************/ @@ -4838,7 +4943,7 @@ Oimr = readl(ha->mem_ptr + IPS_REG_I960_OIMR); Oimr &= ~0x08; writel(Oimr, ha->mem_ptr + IPS_REG_I960_OIMR); - readl(ha->mem_ptr + IPS_REG_I960_OIMR); // Ensure PCI Posting Completes + readl(ha->mem_ptr + IPS_REG_I960_OIMR); /*Ensure PCI Posting Completes*/ } /****************************************************************************/ @@ -4880,9 +4985,9 @@ } if (PostByte[0] < IPS_GOOD_POST_STATUS) { - printk(KERN_WARNING - "(%s%d) reset controller fails (post status %x %x).\n", - ips_name, ha->host_num, PostByte[0], PostByte[1]); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "reset controller fails (post status %x %x).\n", + PostByte[0], PostByte[1]); return (0); } @@ -4974,9 +5079,9 @@ } if (PostByte[0] < IPS_GOOD_POST_STATUS) { - printk(KERN_WARNING - "(%s%d) reset controller fails (post status %x %x).\n", - ips_name, ha->host_num, PostByte[0], PostByte[1]); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "reset controller fails (post status %x %x).\n", + PostByte[0], PostByte[1]); return (0); } @@ -5063,8 +5168,8 @@ if (i >= 45) { /* error occurred */ - printk(KERN_WARNING "(%s%d) timeout waiting for post.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "timeout waiting for post.\n"); return (0); } @@ -5072,7 +5177,8 @@ Post = readl(ha->mem_ptr + IPS_REG_I960_MSG0); if (Post == 0x4F00) { /* If Flashing the Battery PIC */ - printk(KERN_WARNING "Flashing Battery PIC, Please wait ...\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Flashing Battery PIC, Please wait ...\n"); /* Clear the interrupt bit */ Isr = (uint32_t) IPS_BIT_I960_MSG0I; @@ -5087,9 +5193,8 @@ } if (i >= 120) { - printk(KERN_WARNING - "(%s%d) timeout waiting for Battery PIC Flash\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "timeout waiting for Battery PIC Flash\n"); return (0); } @@ -5100,9 +5205,8 @@ writel(Isr, ha->mem_ptr + IPS_REG_I2O_HIR); if (Post < (IPS_GOOD_POST_STATUS << 8)) { - printk(KERN_WARNING - "(%s%d) reset controller fails (post status %x).\n", - ips_name, ha->host_num, Post); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "reset controller fails (post status %x).\n", Post); return (0); } @@ -5120,8 +5224,8 @@ if (i >= 240) { /* error occurred */ - printk(KERN_WARNING "(%s%d) timeout waiting for config.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "timeout waiting for config.\n"); return (0); } @@ -5450,19 +5554,18 @@ TimeOut = 0; - while ((val = le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & - IPS_BIT_SEM) { + while ((val = + le32_to_cpu(inl(ha->io_addr + IPS_REG_CCCR))) & IPS_BIT_SEM) { udelay(1000); if (++TimeOut >= IPS_SEM_TIMEOUT) { if (!(val & IPS_BIT_START_STOP)) break; - printk(KERN_WARNING "(%s%d) ips_issue val [0x%x].\n", - ips_name, ha->host_num, val); - printk(KERN_WARNING - "(%s%d) ips_issue semaphore chk timeout.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "ips_issue val [0x%x].\n", val); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "ips_issue semaphore chk timeout.\n"); return (IPS_FAILURE); } /* end if */ @@ -5512,11 +5615,10 @@ if (!(val & IPS_BIT_START_STOP)) break; - printk(KERN_WARNING "(%s%d) ips_issue val [0x%x].\n", - ips_name, ha->host_num, val); - printk(KERN_WARNING - "(%s%d) ips_issue semaphore chk timeout.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "ips_issue val [0x%x].\n", val); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "ips_issue semaphore chk timeout.\n"); return (IPS_FAILURE); } /* end if */ @@ -5755,8 +5857,8 @@ METHOD_TRACE("ips_write_driver_status", 1); if (!ips_readwrite_page5(ha, FALSE, intr)) { - printk(KERN_WARNING "(%s%d) unable to read NVRAM page 5.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "unable to read NVRAM page 5.\n"); return (0); } @@ -5793,8 +5895,8 @@ /* now update the page */ if (!ips_readwrite_page5(ha, TRUE, intr)) { - printk(KERN_WARNING "(%s%d) unable to write NVRAM page 5.\n", - ips_name, ha->host_num); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "unable to write NVRAM page 5.\n"); return (0); } @@ -5839,9 +5941,9 @@ scb->cmd.basic_io.sg_addr = ha->enq_busaddr; /* send command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); @@ -5882,9 +5984,9 @@ scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr; /* send command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); @@ -5927,9 +6029,9 @@ scb->cmd.basic_io.sg_addr = ha->ioctl_busaddr; /* send command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) { memset(ha->conf, 0, sizeof (IPS_CONF)); @@ -5940,7 +6042,8 @@ /* Allow Completed with Errors, so JCRM can access the Adapter to fix the problems */ if ((scb->basic_status & IPS_GSC_STATUS_MASK) == - IPS_CMD_CMPLT_WERROR) return (1); + IPS_CMD_CMPLT_WERROR) + return (1); return (0); } @@ -5985,9 +6088,9 @@ memcpy(ha->ioctl_data, ha->nvram, sizeof(*ha->nvram)); /* issue the command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) { memset(ha->nvram, 0, sizeof (IPS_NVRAM_P5)); @@ -6032,9 +6135,9 @@ scb->cmd.config_sync.reserved3 = 0; /* issue command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_reset_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_reset_timeout, intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); @@ -6053,9 +6156,9 @@ scb->cmd.unlock_stripe.reserved3 = 0; /* issue command */ - if ( - ((ret = ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == - IPS_FAILURE) || (ret == IPS_SUCCESS_IMM) + if (((ret = + ips_send_wait(ha, scb, ips_cmd_timeout, intr)) == IPS_FAILURE) + || (ret == IPS_SUCCESS_IMM) || ((scb->basic_status & IPS_GSC_STATUS_MASK) > 1)) return (0); @@ -6722,7 +6825,7 @@ uint8_t FirmwareVersion[IPS_COMPAT_ID_LENGTH + 1]; uint8_t BiosVersion[IPS_COMPAT_ID_LENGTH + 1]; int MatchError; - int rc; + int rc; char BiosString[10]; char FirmwareString[10]; @@ -6739,8 +6842,8 @@ rc = IPS_FAILURE; if (ha->subsys->param[4] & IPS_GET_VERSION_SUPPORT) { /* If Versioning is Supported */ - memset( VersionInfo, 0, sizeof (IPS_VERSION_DATA)); /* Get the Version Info with a Get Version Command */ + memset( VersionInfo, 0, sizeof (IPS_VERSION_DATA)); rc = ips_get_version_info(ha, ha->ioctl_busaddr, intr); if (rc == IPS_SUCCESS) memcpy(FirmwareVersion, VersionInfo->compatibilityId, @@ -6780,14 +6883,14 @@ strncpy(&FirmwareString[0], ha->enq->CodeBlkVersion, 8); FirmwareString[8] = 0; - printk(KERN_WARNING - "Warning ! ! ! ServeRAID Version Mismatch\n"); - printk(KERN_WARNING - "Bios = %s, Firmware = %s, Device Driver = %s%s\n", - BiosString, FirmwareString, IPS_VERSION_HIGH, - IPS_VERSION_LOW); - printk(KERN_WARNING - "These levels should match to avoid possible compatibility problems.\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Warning ! ! ! ServeRAID Version Mismatch\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Bios = %s, Firmware = %s, Device Driver = %s%s\n", + BiosString, FirmwareString, IPS_VERSION_HIGH, + IPS_VERSION_LOW); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "These levels should match to avoid possible compatibility problems.\n"); } } else { ha->nvram->version_mismatch = 0; @@ -6825,7 +6928,7 @@ scb->cmd.version_info.count = sizeof (IPS_VERSION_DATA); scb->cmd.version_info.reserved2 = 0; scb->data_len = sizeof (IPS_VERSION_DATA); - scb->data_busaddr = Buffer; + scb->data_busaddr = Buffer; scb->cmd.version_info.buffer_addr = Buffer; scb->flags = 0; @@ -6896,6 +6999,8 @@ for (j = position; j < ips_num_controllers; j++) { switch (ips_ha[j]->ad_type) { case IPS_ADTYPE_SERVERAID6M: + case IPS_ADTYPE_SERVERAID7k: + case IPS_ADTYPE_SERVERAID7M: if (nvram->adapter_order[i] == 'M') { ips_shift_controllers(position, j); @@ -6979,28 +7084,28 @@ ips_register_scsi(int index) { struct Scsi_Host *sh; - ips_ha_t *ha, *oldha; - sh = scsi_register(&ips_driver_template, sizeof (ips_ha_t)); + ips_ha_t *ha, *oldha = ips_ha[index]; + sh = scsi_host_alloc(&ips_driver_template, sizeof (ips_ha_t)); if (!sh) { - printk(KERN_WARNING - "Unable to register controller with SCSI subsystem\n"); + IPS_PRINTK(KERN_WARNING, oldha->pcidev, + "Unable to register controller with SCSI subsystem\n"); return -1; } - oldha = ips_ha[index]; ha = IPS_HA(sh); memcpy(ha, oldha, sizeof (ips_ha_t)); free_irq(oldha->irq, oldha); /* Install the interrupt handler with the new ha */ if (request_irq(ha->irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) { - printk(KERN_WARNING "Unable to install interrupt handler\n"); - scsi_unregister(sh); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Unable to install interrupt handler\n"); + scsi_host_put(sh); return -1; } kfree(oldha); ips_sh[index] = sh; ips_ha[index] = ha; - scsi_set_pci_device(sh, ha->pcidev); + IPS_SCSI_SET_DEVICE(sh, ha); /* Store away needed values for later use */ sh->io_port = ha->io_addr; @@ -7013,7 +7118,7 @@ sh->unchecked_isa_dma = sh->hostt->unchecked_isa_dma; sh->use_clustering = sh->hostt->use_clustering; -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,7) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7) sh->max_sectors = 128; #endif @@ -7022,6 +7127,7 @@ sh->max_channel = ha->nbus - 1; sh->can_queue = ha->max_cmds - 1; + IPS_ADD_HOST(sh, NULL); return 0; } @@ -7031,7 +7137,7 @@ /* Routine Description: */ /* Remove one Adapter ( Hot Plugging ) */ /*---------------------------------------------------------------------------*/ -static void +static void __devexit ips_remove_device(struct pci_dev *pci_dev) { int i; @@ -7064,7 +7170,7 @@ return -ENODEV; ips_driver_template.module = THIS_MODULE; ips_order_controllers(); - if (scsi_register_module(MODULE_SCSI_HA, &ips_driver_template)) { + if (IPS_REGISTER_HOSTS(&ips_driver_template)) { pci_unregister_driver(&ips_pci_driver); return -ENODEV; } @@ -7082,7 +7188,7 @@ static void __exit ips_module_exit(void) { - scsi_unregister_module(MODULE_SCSI_HA, &ips_driver_template); + IPS_UNREGISTER_HOSTS(&ips_driver_template); pci_unregister_driver(&ips_pci_driver); unregister_reboot_notifier(&ips_notifier); } @@ -7106,7 +7212,6 @@ int rc; METHOD_TRACE("ips_insert_device", 1); - if (pci_enable_device(pci_dev)) return -1; @@ -7114,6 +7219,12 @@ if (rc == SUCCESS) rc = ips_init_phase2(index); + if (ips_hotplug) + if (ips_register_scsi(index)) { + ips_free(ips_ha[index]); + rc = -1; + } + if (rc == SUCCESS) ips_num_controllers++; @@ -7192,9 +7303,9 @@ uint32_t offs; if (!request_mem_region(mem_addr, mem_len, "ips")) { - printk(KERN_WARNING - "Couldn't allocate IO Memory space %x len %d.\n", - mem_addr, mem_len); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Couldn't allocate IO Memory space %x len %d.\n", + mem_addr, mem_len); return -1; } @@ -7210,16 +7321,16 @@ /* setup I/O mapped area (if applicable) */ if (io_addr) { if (!request_region(io_addr, io_len, "ips")) { - printk(KERN_WARNING - "Couldn't allocate IO space %x len %d.\n", - io_addr, io_len); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Couldn't allocate IO space %x len %d.\n", + io_addr, io_len); return -1; } } /* get the revision ID */ if (pci_read_config_byte(pci_dev, PCI_REVISION_ID, &revision_id)) { - printk(KERN_WARNING "Can't get revision id.\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, "Can't get revision id.\n"); return -1; } @@ -7228,7 +7339,8 @@ /* found a controller */ ha = kmalloc(sizeof (ips_ha_t), GFP_KERNEL); if (ha == NULL) { - printk(KERN_WARNING "Unable to allocate temporary ha struct\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate temporary ha struct\n"); return -1; } @@ -7272,54 +7384,57 @@ ips_FlashData = pci_alloc_consistent(pci_dev, PAGE_SIZE << 7, &ips_flashbusaddr); } - + ha->enq = pci_alloc_consistent(pci_dev, sizeof (IPS_ENQ), &ha->enq_busaddr); if (!ha->enq) { - printk(KERN_WARNING - "Unable to allocate host inquiry structure\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate host inquiry structure\n"); return ips_abort_init(ha, index); } ha->adapt = pci_alloc_consistent(pci_dev, sizeof (IPS_ADAPTER) + sizeof (IPS_IO_CMD), &dma_address); if (!ha->adapt) { - printk(KERN_WARNING - "Unable to allocate host adapt & dummy structures\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate host adapt & dummy structures\n"); return ips_abort_init(ha, index); } ha->adapt->hw_status_start = dma_address; ha->dummy = (void *) (ha->adapt + 1); - - ha->logical_drive_info = pci_alloc_consistent(pci_dev, sizeof (IPS_LD_INFO), - &dma_address); + + + + ha->logical_drive_info = pci_alloc_consistent(pci_dev, sizeof (IPS_LD_INFO), &dma_address); if (!ha->logical_drive_info) { - printk(KERN_WARNING - "Unable to allocate host logical drive info structure\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate logical drive info structure\n"); return ips_abort_init(ha, index); } ha->logical_drive_info_dma_addr = dma_address; - + + ha->conf = kmalloc(sizeof (IPS_CONF), GFP_KERNEL); if (!ha->conf) { - printk(KERN_WARNING "Unable to allocate host conf structure\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate host conf structure\n"); return ips_abort_init(ha, index); } ha->nvram = kmalloc(sizeof (IPS_NVRAM_P5), GFP_KERNEL); if (!ha->nvram) { - printk(KERN_WARNING - "Unable to allocate host NVRAM structure\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate host NVRAM structure\n"); return ips_abort_init(ha, index); } ha->subsys = kmalloc(sizeof (IPS_SUBSYS), GFP_KERNEL); if (!ha->subsys) { - printk(KERN_WARNING - "Unable to allocate host subsystem structure\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate host subsystem structure\n"); return ips_abort_init(ha, index); } @@ -7327,13 +7442,13 @@ * successful allocation is now required */ if (ips_ioctlsize < PAGE_SIZE) ips_ioctlsize = PAGE_SIZE; - + ha->ioctl_data = pci_alloc_consistent(pci_dev, ips_ioctlsize, &ha->ioctl_busaddr); ha->ioctl_len = ips_ioctlsize; - if (!ha->ioctl_data) { - printk(KERN_WARNING "Unable to allocate IOCTL data\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to allocate IOCTL data\n"); return ips_abort_init(ha, index); } @@ -7359,8 +7474,8 @@ /* * Initialization failed */ - printk(KERN_WARNING - "Unable to initialize controller\n"); + IPS_PRINTK(KERN_WARNING, pci_dev, + "Unable to initialize controller\n"); return ips_abort_init(ha, index); } } @@ -7393,7 +7508,8 @@ /* Install the interrupt handler */ if (request_irq(ha->irq, do_ipsintr, SA_SHIRQ, ips_name, ha)) { - printk(KERN_WARNING "Unable to install interrupt handler\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Unable to install interrupt handler\n"); return ips_abort_init(ha, index); } @@ -7402,13 +7518,15 @@ */ ha->max_cmds = 1; if (!ips_allocatescbs(ha)) { - printk(KERN_WARNING "Unable to allocate a CCB\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Unable to allocate a CCB\n"); free_irq(ha->irq, ha); return ips_abort_init(ha, index); } if (!ips_hainit(ha)) { - printk(KERN_WARNING "Unable to initialize controller\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Unable to initialize controller\n"); free_irq(ha->irq, ha); return ips_abort_init(ha, index); } @@ -7417,7 +7535,8 @@ /* allocate CCBs */ if (!ips_allocatescbs(ha)) { - printk(KERN_WARNING "Unable to allocate CCBs\n"); + IPS_PRINTK(KERN_WARNING, ha->pcidev, + "Unable to allocate CCBs\n"); free_irq(ha->irq, ha); return ips_abort_init(ha, index); } @@ -7425,7 +7544,7 @@ return SUCCESS; } -#if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,9) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,9) MODULE_LICENSE("GPL"); #endif diff -urN linux-2.4.26/drivers/scsi/ips.h linux-2.4.27/drivers/scsi/ips.h --- linux-2.4.26/drivers/scsi/ips.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/ips.h 2004-08-07 16:26:05.504381704 -0700 @@ -59,21 +59,13 @@ extern int ips_eh_abort(Scsi_Cmnd *); extern int ips_eh_reset(Scsi_Cmnd *); extern int ips_queue(Scsi_Cmnd *, void (*) (Scsi_Cmnd *)); - extern int ips_biosparam(Disk *, kdev_t, int *); extern const char * ips_info(struct Scsi_Host *); /* * Some handy macros */ - #ifndef LinuxVersionCode - #define LinuxVersionCode(x,y,z) (((x)<<16)+((y)<<8)+(z)) - #endif - - #if LINUX_VERSION_CODE >= LinuxVersionCode(2,4,20) || defined CONFIG_HIGHIO + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) || defined CONFIG_HIGHIO #define IPS_HIGHIO - #define IPS_HIGHMEM_IO .highmem_io = 1, - #else - #define IPS_HIGHMEM_IO #endif #define IPS_HA(x) ((ips_ha_t *) x->hostdata) @@ -98,11 +90,39 @@ #define IPS_SGLIST_SIZE(ha) (IPS_USE_ENH_SGLIST(ha) ? \ sizeof(IPS_ENH_SG_LIST) : sizeof(IPS_STD_SG_LIST)) - #if LINUX_VERSION_CODE < LinuxVersionCode(2,4,4) + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) #define pci_set_dma_mask(dev,mask) ( mask > 0xffffffff ? 1:0 ) #define scsi_set_pci_device(sh,dev) (0) #endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23) + typedef void irqreturn_t; + #define IRQ_NONE + #define IRQ_HANDLED + #define IRQ_RETVAL(x) + #endif + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + #define IPS_REGISTER_HOSTS(SHT) scsi_register_module(MODULE_SCSI_HA,SHT) + #define IPS_UNREGISTER_HOSTS(SHT) scsi_unregister_module(MODULE_SCSI_HA,SHT) + #define IPS_ADD_HOST(shost,device) + #define IPS_REMOVE_HOST(shost) + #define IPS_SCSI_SET_DEVICE(sh,ha) scsi_set_pci_device(sh, (ha)->pcidev) + #define IPS_PRINTK(level, pcidev, format, arg...) \ + printk(level "%s %s:" format , "ips" , \ + (pcidev)->slot_name , ## arg) + #define scsi_host_alloc(sh,size) scsi_register(sh,size) + #define scsi_host_put(sh) scsi_unregister(sh) + #else + #define IPS_REGISTER_HOSTS(SHT) (!ips_detect(SHT)) + #define IPS_UNREGISTER_HOSTS(SHT) + #define IPS_ADD_HOST(shost,device) do { scsi_add_host(shost,device); scsi_scan_host(shost); } while (0) + #define IPS_REMOVE_HOST(shost) scsi_remove_host(shost) + #define IPS_SCSI_SET_DEVICE(sh,ha) scsi_set_device(sh, &(ha)->pcidev->dev) + #define IPS_PRINTK(level, pcidev, format, arg...) \ + dev_printk(level , &((pcidev)->dev) , format , ## arg) + #endif + #ifndef MDELAY #define MDELAY mdelay #endif @@ -229,6 +249,8 @@ #define IPS_SUBDEVICEID_5I1 0x0258 #define IPS_SUBDEVICEID_6M 0x0279 #define IPS_SUBDEVICEID_6I 0x028C + #define IPS_SUBDEVICEID_7k 0x028E + #define IPS_SUBDEVICEID_7M 0x028F #define IPS_IOCTL_SIZE 8192 #define IPS_STATUS_SIZE 4 #define IPS_STATUS_Q_SIZE (IPS_MAX_CMDS+1) * IPS_STATUS_SIZE @@ -313,6 +335,9 @@ #define IPS_ADTYPE_SERVERAID5I1 0x0D #define IPS_ADTYPE_SERVERAID6M 0x0E #define IPS_ADTYPE_SERVERAID6I 0x0F + #define IPS_ADTYPE_SERVERAID7t 0x10 + #define IPS_ADTYPE_SERVERAID7k 0x11 + #define IPS_ADTYPE_SERVERAID7M 0x12 /* * Adapter Command/Status Packet Definitions @@ -426,46 +451,15 @@ /* * Scsi_Host Template */ -#if LINUX_VERSION_CODE < LinuxVersionCode(2,5,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + static int ips_proc24_info(char *, char **, off_t, int, int, int); static void ips_select_queue_depth(struct Scsi_Host *, Scsi_Device *); -#define IPS { \ - .detect = ips_detect, \ - .release = ips_release, \ - .info = ips_info, \ - .queuecommand = ips_queue, \ - .eh_abort_handler = ips_eh_abort, \ - .eh_host_reset_handler = ips_eh_reset, \ - .bios_param = ips_biosparam,\ - .select_queue_depths = ips_select_queue_depth, \ - .can_queue = 0, \ - .this_id = -1, \ - .sg_tablesize = IPS_MAX_SG, \ - .cmd_per_lun = 16, \ - .present = 0, \ - .unchecked_isa_dma = 0, \ - .use_clustering = ENABLE_CLUSTERING,\ - .use_new_eh_code = 1, \ - IPS_HIGHMEM_IO \ -} + static int ips_biosparam(Disk *disk, kdev_t dev, int geom[]); #else -#define IPS { \ - .detect = ips_detect, \ - .release = ips_release, \ - .info = ips_info, \ - .queuecommand = ips_queue, \ - .eh_abort_handler = ips_eh_abort, \ - .eh_host_reset_handler = ips_eh_reset, \ - .slave_configure = ips_slave_configure, \ - .bios_param = ips_biosparam, \ - .can_queue = 0, \ - .this_id = -1, \ - .sg_tablesize = IPS_MAX_SG, \ - .cmd_per_lun = 3, \ - .present = 0, \ - .unchecked_isa_dma = 0, \ - .use_clustering = ENABLE_CLUSTERING, \ - .highmem_io = 1 \ -} + int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); + static int ips_biosparam(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int geom[]); + int ips_slave_configure(Scsi_Device *SDptr); #endif /* @@ -941,6 +935,20 @@ } IPS_SCSI_MODE_PAGE4; /* + * Sense Data Format - Page 8 + */ +typedef struct { + uint8_t PageCode; + uint8_t PageLength; + uint8_t flags; + uint8_t RetentPrio; + uint16_t DisPrefetchLen; + uint16_t MinPrefetchLen; + uint16_t MaxPrefetchLen; + uint16_t MaxPrefetchCeiling; +} IPS_SCSI_MODE_PAGE8; + +/* * Sense Data Format - Block Descriptor (DASD) */ typedef struct { @@ -967,6 +975,7 @@ union { IPS_SCSI_MODE_PAGE3 pg3; IPS_SCSI_MODE_PAGE4 pg4; + IPS_SCSI_MODE_PAGE8 pg8; } pdata; } IPS_SCSI_MODE_PAGE_DATA; @@ -1056,7 +1065,7 @@ int (*programbios)(struct ips_ha *, char *, uint32_t, uint32_t); int (*verifybios)(struct ips_ha *, char *, uint32_t, uint32_t); void (*statinit)(struct ips_ha *); - void (*intr)(struct ips_ha *); + int (*intr)(struct ips_ha *); void (*enableint)(struct ips_ha *); uint32_t (*statupd)(struct ips_ha *); } ips_hw_func_t; @@ -1082,7 +1091,7 @@ ips_scb_queue_t scb_activelist; /* Active SCB list */ IPS_IO_CMD *dummy; /* dummy command */ IPS_ADAPTER *adapt; /* Adapter status area */ - IPS_LD_INFO *logical_drive_info; /* Logical Drive Info */ + IPS_LD_INFO *logical_drive_info; /* Adapter Logical Drive Info */ dma_addr_t logical_drive_info_dma_addr; /* Logical Drive Info DMA Address */ IPS_ENQ *enq; /* Adapter Enquiry data */ IPS_CONF *conf; /* Adapter config data */ @@ -1131,8 +1140,8 @@ uint8_t bus; uint8_t lun; uint8_t cdb[12]; - uint32_t scb_busaddr; - uint32_t old_data_busaddr; // Obsolete field left in to not break utilities + uint32_t scb_busaddr; + uint32_t old_data_busaddr; // Obsolete, but kept for old utility compatibility uint32_t timeout; uint8_t basic_status; uint8_t extended_status; @@ -1202,29 +1211,30 @@ * *************************************************************************/ -#define IPS_VER_MAJOR 6 -#define IPS_VER_MAJOR_STRING "6" -#define IPS_VER_MINOR 11 -#define IPS_VER_MINOR_STRING "11" -#define IPS_VER_BUILD 07 -#define IPS_VER_BUILD_STRING "07" -#define IPS_VER_STRING "6.11.07" -#define IPS_RELEASE_ID 0x00010001 -#define IPS_BUILD_IDENT 2224 -#define IPS_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2003. All Rights Reserved." -#define IPS_ADAPTECCOPYRIGHT_STRING "(c) Copyright Adaptec, Inc. 2002 to present. All Rights Reserved." -#define IPS_NT_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2003." +#define IPS_VER_MAJOR 7 +#define IPS_VER_MAJOR_STRING "7" +#define IPS_VER_MINOR 00 +#define IPS_VER_MINOR_STRING "00" +#define IPS_VER_BUILD 15 +#define IPS_VER_BUILD_STRING "15" +#define IPS_VER_STRING "7.00.15" +#define IPS_RELEASE_ID 0x00020000 +#define IPS_BUILD_IDENT 625 +#define IPS_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002. All Rights Reserved." +#define IPS_ADAPTECCOPYRIGHT_STRING "(c) Copyright Adaptec, Inc. 2002 to 2004. All Rights Reserved." +#define IPS_DELLCOPYRIGHT_STRING "(c) Copyright Dell 2004. All Rights Reserved." +#define IPS_NT_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002." /* Version numbers for various adapters */ #define IPS_VER_SERVERAID1 "2.25.01" #define IPS_VER_SERVERAID2 "2.88.13" #define IPS_VER_NAVAJO "2.88.13" #define IPS_VER_SERVERAID3 "6.10.24" -#define IPS_VER_SERVERAID4H "6.11.07" -#define IPS_VER_SERVERAID4MLx "6.11.07" -#define IPS_VER_SARASOTA "6.11.07" -#define IPS_VER_MARCO "6.11.07" -#define IPS_VER_SEBRING "6.11.07" +#define IPS_VER_SERVERAID4H "7.00.15" +#define IPS_VER_SERVERAID4MLx "7.00.15" +#define IPS_VER_SARASOTA "7.00.15" +#define IPS_VER_MARCO "7.00.15" +#define IPS_VER_SEBRING "7.00.15" /* Compatability IDs for various adapters */ #define IPS_COMPAT_UNKNOWN "" diff -urN linux-2.4.26/drivers/scsi/libata-core.c linux-2.4.27/drivers/scsi/libata-core.c --- linux-2.4.26/drivers/scsi/libata-core.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/libata-core.c 2004-08-07 16:26:05.509381909 -0700 @@ -0,0 +1,3527 @@ +/* + libata-core.c - helper library for ATA + + Copyright 2003-2004 Red Hat, Inc. All rights reserved. + Copyright 2003-2004 Jeff Garzik + + The contents of this file are subject to the Open + Software License version 1.1 that can be found at + http://www.opensource.org/licenses/osl-1.1.txt and is included herein + by reference. + + Alternatively, the contents of this file may be used under the terms + of the GNU General Public License version 2 (the "GPL") as distributed + in the kernel source COPYING file, in which case the provisions of + the GPL are applicable instead of the above. If you wish to allow + the use of your version of this file only under the terms of the + GPL and not to allow others to use your version of this file under + the OSL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the GPL. + If you do not delete the provisions above, a recipient may use your + version of this file under either the OSL or the GPL. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include +#include +#include + +#include "libata.h" + +static unsigned int ata_busy_sleep (struct ata_port *ap, + unsigned long tmout_pat, + unsigned long tmout); +static void __ata_dev_select (struct ata_port *ap, unsigned int device); +static void ata_dma_complete(struct ata_queued_cmd *qc, u8 host_stat); +static void ata_host_set_pio(struct ata_port *ap); +static void ata_host_set_udma(struct ata_port *ap); +static void ata_dev_set_pio(struct ata_port *ap, unsigned int device); +static void ata_dev_set_udma(struct ata_port *ap, unsigned int device); +static void ata_set_mode(struct ata_port *ap); +static int ata_qc_issue_prot(struct ata_queued_cmd *qc); + +static unsigned int ata_unique_id = 1; +static LIST_HEAD(ata_probe_list); +static spinlock_t ata_module_lock = SPIN_LOCK_UNLOCKED; + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("Library module for ATA devices"); +MODULE_LICENSE("GPL"); + +static const char * thr_state_name[] = { + "THR_UNKNOWN", + "THR_PORT_RESET", + "THR_AWAIT_DEATH", + "THR_PROBE_FAILED", + "THR_IDLE", + "THR_PROBE_SUCCESS", + "THR_PROBE_START", +}; + +/** + * ata_thr_state_name - convert thread state enum to string + * @thr_state: thread state to be converted to string + * + * Converts the specified thread state id to a constant C string. + * + * LOCKING: + * None. + * + * RETURNS: + * The THR_xxx-prefixed string naming the specified thread + * state id, or the string "". + */ + +static const char *ata_thr_state_name(unsigned int thr_state) +{ + if (thr_state < ARRAY_SIZE(thr_state_name)) + return thr_state_name[thr_state]; + return ""; +} + +/** + * msleep - sleep for a number of milliseconds + * @msecs: number of milliseconds to sleep + * + * Issues schedule_timeout call for the specified number + * of milliseconds. + * + * LOCKING: + * None. + */ + +static void msleep(unsigned long msecs) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(msecs) + 1); +} + +/** + * ata_tf_load_pio - send taskfile registers to host controller + * @ap: Port to which output is sent + * @tf: ATA taskfile register set + * + * Outputs ATA taskfile to standard ATA host controller using PIO. + * + * LOCKING: + * Inherited from caller. + */ + +void ata_tf_load_pio(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + outb(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + outb(tf->hob_feature, ioaddr->feature_addr); + outb(tf->hob_nsect, ioaddr->nsect_addr); + outb(tf->hob_lbal, ioaddr->lbal_addr); + outb(tf->hob_lbam, ioaddr->lbam_addr); + outb(tf->hob_lbah, ioaddr->lbah_addr); + VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", + tf->hob_feature, + tf->hob_nsect, + tf->hob_lbal, + tf->hob_lbam, + tf->hob_lbah); + } + + if (is_addr) { + outb(tf->feature, ioaddr->feature_addr); + outb(tf->nsect, ioaddr->nsect_addr); + outb(tf->lbal, ioaddr->lbal_addr); + outb(tf->lbam, ioaddr->lbam_addr); + outb(tf->lbah, ioaddr->lbah_addr); + VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", + tf->feature, + tf->nsect, + tf->lbal, + tf->lbam, + tf->lbah); + } + + if (tf->flags & ATA_TFLAG_DEVICE) { + outb(tf->device, ioaddr->device_addr); + VPRINTK("device 0x%X\n", tf->device); + } + + ata_wait_idle(ap); +} + +/** + * ata_tf_load_mmio - send taskfile registers to host controller + * @ap: Port to which output is sent + * @tf: ATA taskfile register set + * + * Outputs ATA taskfile to standard ATA host controller using MMIO. + * + * LOCKING: + * Inherited from caller. + */ + +void ata_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + writeb(tf->ctl, ap->ioaddr.ctl_addr); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + writeb(tf->hob_feature, (void *) ioaddr->feature_addr); + writeb(tf->hob_nsect, (void *) ioaddr->nsect_addr); + writeb(tf->hob_lbal, (void *) ioaddr->lbal_addr); + writeb(tf->hob_lbam, (void *) ioaddr->lbam_addr); + writeb(tf->hob_lbah, (void *) ioaddr->lbah_addr); + VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", + tf->hob_feature, + tf->hob_nsect, + tf->hob_lbal, + tf->hob_lbam, + tf->hob_lbah); + } + + if (is_addr) { + writeb(tf->feature, (void *) ioaddr->feature_addr); + writeb(tf->nsect, (void *) ioaddr->nsect_addr); + writeb(tf->lbal, (void *) ioaddr->lbal_addr); + writeb(tf->lbam, (void *) ioaddr->lbam_addr); + writeb(tf->lbah, (void *) ioaddr->lbah_addr); + VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", + tf->feature, + tf->nsect, + tf->lbal, + tf->lbam, + tf->lbah); + } + + if (tf->flags & ATA_TFLAG_DEVICE) { + writeb(tf->device, (void *) ioaddr->device_addr); + VPRINTK("device 0x%X\n", tf->device); + } + + ata_wait_idle(ap); +} + +/** + * ata_exec_command_pio - issue ATA command to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues PIO write to ATA command register, with proper + * synchronization with interrupt handler / other threads. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_exec_command_pio(struct ata_port *ap, struct ata_taskfile *tf) +{ + DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); + + outb(tf->command, ap->ioaddr.command_addr); + ata_pause(ap); +} + + +/** + * ata_exec_command_mmio - issue ATA command to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues MMIO write to ATA command register, with proper + * synchronization with interrupt handler / other threads. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); + + writeb(tf->command, (void *) ap->ioaddr.command_addr); + ata_pause(ap); +} + +/** + * ata_exec - issue ATA command to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues PIO write to ATA command register, with proper + * synchronization with interrupt handler / other threads. + * + * LOCKING: + * Obtains host_set lock. + */ + +static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf) +{ + unsigned long flags; + + DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); + spin_lock_irqsave(&ap->host_set->lock, flags); + ap->ops->exec_command(ap, tf); + spin_unlock_irqrestore(&ap->host_set->lock, flags); +} + +/** + * ata_tf_to_host - issue ATA taskfile to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues ATA taskfile register set to ATA host controller, + * via PIO, with proper synchronization with interrupt handler and + * other threads. + * + * LOCKING: + * Obtains host_set lock. + */ + +static void ata_tf_to_host(struct ata_port *ap, struct ata_taskfile *tf) +{ + ap->ops->tf_load(ap, tf); + + ata_exec(ap, tf); +} + +/** + * ata_tf_to_host_nolock - issue ATA taskfile to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Issues ATA taskfile register set to ATA host controller, + * via PIO, with proper synchronization with interrupt handler and + * other threads. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf) +{ + ap->ops->tf_load(ap, tf); + ap->ops->exec_command(ap, tf); +} + +/** + * ata_tf_read_pio - input device's ATA taskfile shadow registers + * @ap: Port from which input is read + * @tf: ATA taskfile register set for storing input + * + * Reads ATA taskfile registers for currently-selected device + * into @tf via PIO. + * + * LOCKING: + * Inherited from caller. + */ + +void ata_tf_read_pio(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + tf->nsect = inb(ioaddr->nsect_addr); + tf->lbal = inb(ioaddr->lbal_addr); + tf->lbam = inb(ioaddr->lbam_addr); + tf->lbah = inb(ioaddr->lbah_addr); + tf->device = inb(ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + outb(tf->ctl | ATA_HOB, ioaddr->ctl_addr); + tf->hob_feature = inb(ioaddr->error_addr); + tf->hob_nsect = inb(ioaddr->nsect_addr); + tf->hob_lbal = inb(ioaddr->lbal_addr); + tf->hob_lbam = inb(ioaddr->lbam_addr); + tf->hob_lbah = inb(ioaddr->lbah_addr); + } +} + +/** + * ata_tf_read_mmio - input device's ATA taskfile shadow registers + * @ap: Port from which input is read + * @tf: ATA taskfile register set for storing input + * + * Reads ATA taskfile registers for currently-selected device + * into @tf via MMIO. + * + * LOCKING: + * Inherited from caller. + */ + +void ata_tf_read_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + tf->nsect = readb((void *)ioaddr->nsect_addr); + tf->lbal = readb((void *)ioaddr->lbal_addr); + tf->lbam = readb((void *)ioaddr->lbam_addr); + tf->lbah = readb((void *)ioaddr->lbah_addr); + tf->device = readb((void *)ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + writeb(tf->ctl | ATA_HOB, ap->ioaddr.ctl_addr); + tf->hob_feature = readb((void *)ioaddr->error_addr); + tf->hob_nsect = readb((void *)ioaddr->nsect_addr); + tf->hob_lbal = readb((void *)ioaddr->lbal_addr); + tf->hob_lbam = readb((void *)ioaddr->lbam_addr); + tf->hob_lbah = readb((void *)ioaddr->lbah_addr); + } +} + +/** + * ata_check_status_pio - Read device status reg & clear interrupt + * @ap: port where the device is + * + * Reads ATA taskfile status register for currently-selected device + * via PIO and return it's value. This also clears pending interrupts + * from this device + * + * LOCKING: + * Inherited from caller. + */ +u8 ata_check_status_pio(struct ata_port *ap) +{ + return inb(ap->ioaddr.status_addr); +} + +/** + * ata_check_status_mmio - Read device status reg & clear interrupt + * @ap: port where the device is + * + * Reads ATA taskfile status register for currently-selected device + * via MMIO and return it's value. This also clears pending interrupts + * from this device + * + * LOCKING: + * Inherited from caller. + */ +u8 ata_check_status_mmio(struct ata_port *ap) +{ + return readb((void *) ap->ioaddr.status_addr); +} + +/** + * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure + * @tf: Taskfile to convert + * @fis: Buffer into which data will output + * @pmp: Port multiplier port + * + * Converts a standard ATA taskfile to a Serial ATA + * FIS structure (Register - Host to Device). + * + * LOCKING: + * Inherited from caller. + */ + +void ata_tf_to_fis(struct ata_taskfile *tf, u8 *fis, u8 pmp) +{ + fis[0] = 0x27; /* Register - Host to Device FIS */ + fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number, + bit 7 indicates Command FIS */ + fis[2] = tf->command; + fis[3] = tf->feature; + + fis[4] = tf->lbal; + fis[5] = tf->lbam; + fis[6] = tf->lbah; + fis[7] = tf->device; + + fis[8] = tf->hob_lbal; + fis[9] = tf->hob_lbam; + fis[10] = tf->hob_lbah; + fis[11] = tf->hob_feature; + + fis[12] = tf->nsect; + fis[13] = tf->hob_nsect; + fis[14] = 0; + fis[15] = tf->ctl; + + fis[16] = 0; + fis[17] = 0; + fis[18] = 0; + fis[19] = 0; +} + +/** + * ata_tf_from_fis - Convert SATA FIS to ATA taskfile + * @fis: Buffer from which data will be input + * @tf: Taskfile to output + * + * Converts a standard ATA taskfile to a Serial ATA + * FIS structure (Register - Host to Device). + * + * LOCKING: + * Inherited from caller. + */ + +void ata_tf_from_fis(u8 *fis, struct ata_taskfile *tf) +{ + tf->command = fis[2]; /* status */ + tf->feature = fis[3]; /* error */ + + tf->lbal = fis[4]; + tf->lbam = fis[5]; + tf->lbah = fis[6]; + tf->device = fis[7]; + + tf->hob_lbal = fis[8]; + tf->hob_lbam = fis[9]; + tf->hob_lbah = fis[10]; + + tf->nsect = fis[12]; + tf->hob_nsect = fis[13]; +} + +/** + * ata_prot_to_cmd - determine which read/write opcodes to use + * @protocol: ATA_PROT_xxx taskfile protocol + * @lba48: true is lba48 is present + * + * Given necessary input, determine which read/write commands + * to use to transfer data. + * + * LOCKING: + * None. + */ +static int ata_prot_to_cmd(int protocol, int lba48) +{ + int rcmd = 0, wcmd = 0; + + switch (protocol) { + case ATA_PROT_PIO: + if (lba48) { + rcmd = ATA_CMD_PIO_READ_EXT; + wcmd = ATA_CMD_PIO_WRITE_EXT; + } else { + rcmd = ATA_CMD_PIO_READ; + wcmd = ATA_CMD_PIO_WRITE; + } + break; + + case ATA_PROT_DMA: + if (lba48) { + rcmd = ATA_CMD_READ_EXT; + wcmd = ATA_CMD_WRITE_EXT; + } else { + rcmd = ATA_CMD_READ; + wcmd = ATA_CMD_WRITE; + } + break; + + default: + return -1; + } + + return rcmd | (wcmd << 8); +} + +/** + * ata_dev_set_protocol - set taskfile protocol and r/w commands + * @dev: device to examine and configure + * + * Examine the device configuration, after we have + * read the identify-device page and configured the + * data transfer mode. Set internal state related to + * the ATA taskfile protocol (pio, pio mult, dma, etc.) + * and calculate the proper read/write commands to use. + * + * LOCKING: + * caller. + */ +static void ata_dev_set_protocol(struct ata_device *dev) +{ + int pio = (dev->flags & ATA_DFLAG_PIO); + int lba48 = (dev->flags & ATA_DFLAG_LBA48); + int proto, cmd; + + if (pio) + proto = dev->xfer_protocol = ATA_PROT_PIO; + else + proto = dev->xfer_protocol = ATA_PROT_DMA; + + cmd = ata_prot_to_cmd(proto, lba48); + if (cmd < 0) + BUG(); + + dev->read_cmd = cmd & 0xff; + dev->write_cmd = (cmd >> 8) & 0xff; +} + +static const char * udma_str[] = { + "UDMA/16", + "UDMA/25", + "UDMA/33", + "UDMA/44", + "UDMA/66", + "UDMA/100", + "UDMA/133", + "UDMA7", +}; + +/** + * ata_udma_string - convert UDMA bit offset to string + * @udma_mask: mask of bits supported; only highest bit counts. + * + * Determine string which represents the highest speed + * (highest bit in @udma_mask). + * + * LOCKING: + * None. + * + * RETURNS: + * Constant C string representing highest speed listed in + * @udma_mask, or the constant C string "". + */ + +static const char *ata_udma_string(unsigned int udma_mask) +{ + int i; + + for (i = 7; i >= 0; i--) { + if (udma_mask & (1 << i)) + return udma_str[i]; + } + + return ""; +} + +/** + * ata_pio_devchk - PATA device presence detection + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * This technique was originally described in + * Hale Landis's ATADRVR (www.ata-atapi.com), and + * later found its way into the ATA/ATAPI spec. + * + * Write a pattern to the ATA shadow registers, + * and if a device is present, it will respond by + * correctly storing and echoing back the + * ATA shadow register contents. + * + * LOCKING: + * caller. + */ + +static unsigned int ata_pio_devchk(struct ata_port *ap, + unsigned int device) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u8 nsect, lbal; + + __ata_dev_select(ap, device); + + outb(0x55, ioaddr->nsect_addr); + outb(0xaa, ioaddr->lbal_addr); + + outb(0xaa, ioaddr->nsect_addr); + outb(0x55, ioaddr->lbal_addr); + + outb(0x55, ioaddr->nsect_addr); + outb(0xaa, ioaddr->lbal_addr); + + nsect = inb(ioaddr->nsect_addr); + lbal = inb(ioaddr->lbal_addr); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return 1; /* we found a device */ + + return 0; /* nothing found */ +} + +/** + * ata_mmio_devchk - PATA device presence detection + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * This technique was originally described in + * Hale Landis's ATADRVR (www.ata-atapi.com), and + * later found its way into the ATA/ATAPI spec. + * + * Write a pattern to the ATA shadow registers, + * and if a device is present, it will respond by + * correctly storing and echoing back the + * ATA shadow register contents. + * + * LOCKING: + * caller. + */ + +static unsigned int ata_mmio_devchk(struct ata_port *ap, + unsigned int device) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u8 nsect, lbal; + + __ata_dev_select(ap, device); + + writeb(0x55, (void *) ioaddr->nsect_addr); + writeb(0xaa, (void *) ioaddr->lbal_addr); + + writeb(0xaa, (void *) ioaddr->nsect_addr); + writeb(0x55, (void *) ioaddr->lbal_addr); + + writeb(0x55, (void *) ioaddr->nsect_addr); + writeb(0xaa, (void *) ioaddr->lbal_addr); + + nsect = readb((void *) ioaddr->nsect_addr); + lbal = readb((void *) ioaddr->lbal_addr); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return 1; /* we found a device */ + + return 0; /* nothing found */ +} + +/** + * ata_dev_devchk - PATA device presence detection + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * Dispatch ATA device presence detection, depending + * on whether we are using PIO or MMIO to talk to the + * ATA shadow registers. + * + * LOCKING: + * caller. + */ + +static unsigned int ata_dev_devchk(struct ata_port *ap, + unsigned int device) +{ + if (ap->flags & ATA_FLAG_MMIO) + return ata_mmio_devchk(ap, device); + return ata_pio_devchk(ap, device); +} + +/** + * ata_dev_classify - determine device type based on ATA-spec signature + * @tf: ATA taskfile register set for device to be identified + * + * Determine from taskfile register contents whether a device is + * ATA or ATAPI, as per "Signature and persistence" section + * of ATA/PI spec (volume 1, sect 5.14). + * + * LOCKING: + * None. + * + * RETURNS: + * Device type, %ATA_DEV_ATA, %ATA_DEV_ATAPI, or %ATA_DEV_UNKNOWN + * the event of failure. + */ + +static unsigned int ata_dev_classify(struct ata_taskfile *tf) +{ + /* Apple's open source Darwin code hints that some devices only + * put a proper signature into the LBA mid/high registers, + * So, we only check those. It's sufficient for uniqueness. + */ + + if (((tf->lbam == 0) && (tf->lbah == 0)) || + ((tf->lbam == 0x3c) && (tf->lbah == 0xc3))) { + DPRINTK("found ATA device by sig\n"); + return ATA_DEV_ATA; + } + + if (((tf->lbam == 0x14) && (tf->lbah == 0xeb)) || + ((tf->lbam == 0x69) && (tf->lbah == 0x96))) { + DPRINTK("found ATAPI device by sig\n"); + return ATA_DEV_ATAPI; + } + + DPRINTK("unknown device\n"); + return ATA_DEV_UNKNOWN; +} + +/** + * ata_dev_try_classify - Parse returned ATA device signature + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * After an event -- SRST, E.D.D., or SATA COMRESET -- occurs, + * an ATA/ATAPI-defined set of values is placed in the ATA + * shadow registers, indicating the results of device detection + * and diagnostics. + * + * Select the ATA device, and read the values from the ATA shadow + * registers. Then parse according to the Error register value, + * and the spec-defined values examined by ata_dev_classify(). + * + * LOCKING: + * caller. + */ + +static u8 ata_dev_try_classify(struct ata_port *ap, unsigned int device) +{ + struct ata_device *dev = &ap->device[device]; + struct ata_taskfile tf; + unsigned int class; + u8 err; + + __ata_dev_select(ap, device); + + memset(&tf, 0, sizeof(tf)); + + err = ata_chk_err(ap); + ap->ops->tf_read(ap, &tf); + + dev->class = ATA_DEV_NONE; + + /* see if device passed diags */ + if (err == 1) + /* do nothing */ ; + else if ((device == 0) && (err == 0x81)) + /* do nothing */ ; + else + return err; + + /* determine if device if ATA or ATAPI */ + class = ata_dev_classify(&tf); + if (class == ATA_DEV_UNKNOWN) + return err; + if ((class == ATA_DEV_ATA) && (ata_chk_status(ap) == 0)) + return err; + + dev->class = class; + + return err; +} + +/** + * ata_dev_id_string - Convert IDENTIFY DEVICE page into string + * @dev: Device whose IDENTIFY DEVICE results we will examine + * @s: string into which data is output + * @ofs: offset into identify device page + * @len: length of string to return. must be an even number. + * + * The strings in the IDENTIFY DEVICE page are broken up into + * 16-bit chunks. Run through the string, and output each + * 8-bit chunk linearly, regardless of platform. + * + * LOCKING: + * caller. + */ + +void ata_dev_id_string(struct ata_device *dev, unsigned char *s, + unsigned int ofs, unsigned int len) +{ + unsigned int c; + + while (len > 0) { + c = dev->id[ofs] >> 8; + *s = c; + s++; + + c = dev->id[ofs] & 0xff; + *s = c; + s++; + + ofs++; + len -= 2; + } +} + +/** + * __ata_dev_select - Select device 0/1 on ATA bus + * @ap: ATA channel to manipulate + * @device: ATA device (numbered from zero) to select + * + * Use the method defined in the ATA specification to + * make either device 0, or device 1, active on the + * ATA channel. + * + * LOCKING: + * caller. + */ + +static void __ata_dev_select (struct ata_port *ap, unsigned int device) +{ + u8 tmp; + + if (device == 0) + tmp = ATA_DEVICE_OBS; + else + tmp = ATA_DEVICE_OBS | ATA_DEV1; + + if (ap->flags & ATA_FLAG_MMIO) { + writeb(tmp, (void *) ap->ioaddr.device_addr); + } else { + outb(tmp, ap->ioaddr.device_addr); + } + ata_pause(ap); /* needed; also flushes, for mmio */ +} + +/** + * ata_dev_select - Select device 0/1 on ATA bus + * @ap: ATA channel to manipulate + * @device: ATA device (numbered from zero) to select + * @wait: non-zero to wait for Status register BSY bit to clear + * @can_sleep: non-zero if context allows sleeping + * + * Use the method defined in the ATA specification to + * make either device 0, or device 1, active on the + * ATA channel. + * + * This is a high-level version of __ata_dev_select(), + * which additionally provides the services of inserting + * the proper pauses and status polling, where needed. + * + * LOCKING: + * caller. + */ + +void ata_dev_select(struct ata_port *ap, unsigned int device, + unsigned int wait, unsigned int can_sleep) +{ + VPRINTK("ENTER, ata%u: device %u, wait %u\n", + ap->id, device, wait); + + if (wait) + ata_wait_idle(ap); + + __ata_dev_select(ap, device); + + if (wait) { + if (can_sleep && ap->device[device].class == ATA_DEV_ATAPI) + msleep(150); + ata_wait_idle(ap); + } +} + +/** + * ata_dump_id - IDENTIFY DEVICE info debugging output + * @dev: Device whose IDENTIFY DEVICE page we will dump + * + * Dump selected 16-bit words from a detected device's + * IDENTIFY PAGE page. + * + * LOCKING: + * caller. + */ + +static inline void ata_dump_id(struct ata_device *dev) +{ + DPRINTK("49==0x%04x " + "53==0x%04x " + "63==0x%04x " + "64==0x%04x " + "75==0x%04x \n", + dev->id[49], + dev->id[53], + dev->id[63], + dev->id[64], + dev->id[75]); + DPRINTK("80==0x%04x " + "81==0x%04x " + "82==0x%04x " + "83==0x%04x " + "84==0x%04x \n", + dev->id[80], + dev->id[81], + dev->id[82], + dev->id[83], + dev->id[84]); + DPRINTK("88==0x%04x " + "93==0x%04x\n", + dev->id[88], + dev->id[93]); +} + +/** + * ata_dev_identify - obtain IDENTIFY x DEVICE page + * @ap: port on which device we wish to probe resides + * @device: device bus address, starting at zero + * + * Following bus reset, we issue the IDENTIFY [PACKET] DEVICE + * command, and read back the 512-byte device information page. + * The device information page is fed to us via the standard + * PIO-IN protocol, but we hand-code it here. (TODO: investigate + * using standard PIO-IN paths) + * + * After reading the device information page, we use several + * bits of information from it to initialize data structures + * that will be used during the lifetime of the ata_device. + * Other data from the info page is used to disqualify certain + * older ATA devices we do not wish to support. + * + * LOCKING: + * Inherited from caller. Some functions called by this function + * obtain the host_set lock. + */ + +static void ata_dev_identify(struct ata_port *ap, unsigned int device) +{ + struct ata_device *dev = &ap->device[device]; + unsigned int i; + u16 tmp, udma_modes; + u8 status; + struct ata_taskfile tf; + unsigned int using_edd; + + if (!ata_dev_present(dev)) { + DPRINTK("ENTER/EXIT (host %u, dev %u) -- nodev\n", + ap->id, device); + return; + } + + if (ap->flags & (ATA_FLAG_SRST | ATA_FLAG_SATA_RESET)) + using_edd = 0; + else + using_edd = 1; + + DPRINTK("ENTER, host %u, dev %u\n", ap->id, device); + + assert (dev->class == ATA_DEV_ATA || dev->class == ATA_DEV_ATAPI || + dev->class == ATA_DEV_NONE); + + ata_dev_select(ap, device, 1, 1); /* select device 0/1 */ + +retry: + ata_tf_init(ap, &tf, device); + tf.ctl |= ATA_NIEN; + tf.protocol = ATA_PROT_PIO; + + if (dev->class == ATA_DEV_ATA) { + tf.command = ATA_CMD_ID_ATA; + DPRINTK("do ATA identify\n"); + } else { + tf.command = ATA_CMD_ID_ATAPI; + DPRINTK("do ATAPI identify\n"); + } + + ata_tf_to_host(ap, &tf); + + /* crazy ATAPI devices... */ + if (dev->class == ATA_DEV_ATAPI) + msleep(150); + + if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) + goto err_out; + + status = ata_chk_status(ap); + if (status & ATA_ERR) { + /* + * arg! EDD works for all test cases, but seems to return + * the ATA signature for some ATAPI devices. Until the + * reason for this is found and fixed, we fix up the mess + * here. If IDENTIFY DEVICE returns command aborted + * (as ATAPI devices do), then we issue an + * IDENTIFY PACKET DEVICE. + * + * ATA software reset (SRST, the default) does not appear + * to have this problem. + */ + if ((using_edd) && (tf.command == ATA_CMD_ID_ATA)) { + u8 err = ata_chk_err(ap); + if (err & ATA_ABORTED) { + dev->class = ATA_DEV_ATAPI; + goto retry; + } + } + goto err_out; + } + + /* make sure we have BSY=0, DRQ=1 */ + if ((status & ATA_DRQ) == 0) { + printk(KERN_WARNING "ata%u: dev %u (ATA%s?) not returning id page (0x%x)\n", + ap->id, device, + dev->class == ATA_DEV_ATA ? "" : "PI", + status); + goto err_out; + } + + /* read IDENTIFY [X] DEVICE page */ + if (ap->flags & ATA_FLAG_MMIO) { + for (i = 0; i < ATA_ID_WORDS; i++) + dev->id[i] = readw((void *)ap->ioaddr.data_addr); + } else + for (i = 0; i < ATA_ID_WORDS; i++) + dev->id[i] = inw(ap->ioaddr.data_addr); + + /* wait for host_idle */ + status = ata_wait_idle(ap); + if (status & (ATA_BUSY | ATA_DRQ)) { + printk(KERN_WARNING "ata%u: dev %u (ATA%s?) error after id page (0x%x)\n", + ap->id, device, + dev->class == ATA_DEV_ATA ? "" : "PI", + status); + goto err_out; + } + + ata_irq_on(ap); /* re-enable interrupts */ + + /* print device capabilities */ + printk(KERN_DEBUG "ata%u: dev %u cfg " + "49:%04x 82:%04x 83:%04x 84:%04x 85:%04x 86:%04x 87:%04x 88:%04x\n", + ap->id, device, dev->id[49], + dev->id[82], dev->id[83], dev->id[84], + dev->id[85], dev->id[86], dev->id[87], + dev->id[88]); + + /* + * common ATA, ATAPI feature tests + */ + + /* we require LBA and DMA support (bits 8 & 9 of word 49) */ + if (!ata_id_has_dma(dev) || !ata_id_has_lba(dev)) { + printk(KERN_DEBUG "ata%u: no dma/lba\n", ap->id); + goto err_out_nosup; + } + + /* we require UDMA support */ + udma_modes = + tmp = dev->id[ATA_ID_UDMA_MODES]; + if ((tmp & 0xff) == 0) { + printk(KERN_DEBUG "ata%u: no udma\n", ap->id); + goto err_out_nosup; + } + + ata_dump_id(dev); + + /* ATA-specific feature tests */ + if (dev->class == ATA_DEV_ATA) { + if (!ata_id_is_ata(dev)) /* sanity check */ + goto err_out_nosup; + + tmp = dev->id[ATA_ID_MAJOR_VER]; + for (i = 14; i >= 1; i--) + if (tmp & (1 << i)) + break; + + /* we require at least ATA-3 */ + if (i < 3) { + printk(KERN_DEBUG "ata%u: no ATA-3\n", ap->id); + goto err_out_nosup; + } + + if (ata_id_has_lba48(dev)) { + dev->flags |= ATA_DFLAG_LBA48; + dev->n_sectors = ata_id_u64(dev, 100); + } else { + dev->n_sectors = ata_id_u32(dev, 60); + } + + ap->host->max_cmd_len = 16; + + /* print device info to dmesg */ + printk(KERN_INFO "ata%u: dev %u ATA, max %s, %Lu sectors:%s\n", + ap->id, device, + ata_udma_string(udma_modes), + (unsigned long long)dev->n_sectors, + dev->flags & ATA_DFLAG_LBA48 ? " lba48" : ""); + } + + /* ATAPI-specific feature tests */ + else { + if (ata_id_is_ata(dev)) /* sanity check */ + goto err_out_nosup; + + /* see if 16-byte commands supported */ + tmp = dev->id[0] & 0x3; + if (tmp == 1) + ap->host->max_cmd_len = 16; + + /* print device info to dmesg */ + printk(KERN_INFO "ata%u: dev %u ATAPI, max %s\n", + ap->id, device, + ata_udma_string(udma_modes)); + } + + DPRINTK("EXIT, drv_stat = 0x%x\n", ata_chk_status(ap)); + return; + +err_out_nosup: + printk(KERN_WARNING "ata%u: dev %u not supported, ignoring\n", + ap->id, device); +err_out: + ata_irq_on(ap); /* re-enable interrupts */ + dev->class++; /* converts ATA_DEV_xxx into ATA_DEV_xxx_UNSUP */ + DPRINTK("EXIT, err\n"); +} + +/** + * ata_port_reset - + * @ap: + * + * LOCKING: + */ + +static void ata_port_reset(struct ata_port *ap) +{ + unsigned int i, found = 0; + + ap->ops->phy_reset(ap); + if (ap->flags & ATA_FLAG_PORT_DISABLED) + goto err_out; + + for (i = 0; i < ATA_MAX_DEVICES; i++) { + ata_dev_identify(ap, i); + if (ata_dev_present(&ap->device[i])) { + found = 1; + if (ap->ops->dev_config) + ap->ops->dev_config(ap, &ap->device[i]); + } + } + + if ((!found) || (ap->flags & ATA_FLAG_PORT_DISABLED)) + goto err_out_disable; + + ata_set_mode(ap); + if (ap->flags & ATA_FLAG_PORT_DISABLED) + goto err_out_disable; + + ap->thr_state = THR_PROBE_SUCCESS; + + return; + +err_out_disable: + ap->ops->port_disable(ap); +err_out: + ap->thr_state = THR_PROBE_FAILED; +} + +/** + * ata_port_probe - + * @ap: + * + * LOCKING: + */ + +void ata_port_probe(struct ata_port *ap) +{ + ap->flags &= ~ATA_FLAG_PORT_DISABLED; +} + +/** + * sata_phy_reset - + * @ap: + * + * LOCKING: + * + */ +void sata_phy_reset(struct ata_port *ap) +{ + u32 sstatus; + unsigned long timeout = jiffies + (HZ * 5); + + if (ap->flags & ATA_FLAG_SATA_RESET) { + scr_write(ap, SCR_CONTROL, 0x301); /* issue phy wake/reset */ + scr_read(ap, SCR_STATUS); /* dummy read; flush */ + udelay(400); /* FIXME: a guess */ + } + scr_write(ap, SCR_CONTROL, 0x300); /* issue phy wake/clear reset */ + + /* wait for phy to become ready, if necessary */ + do { + msleep(200); + sstatus = scr_read(ap, SCR_STATUS); + if ((sstatus & 0xf) != 1) + break; + } while (time_before(jiffies, timeout)); + + /* TODO: phy layer with polling, timeouts, etc. */ + if (sata_dev_present(ap)) + ata_port_probe(ap); + else { + sstatus = scr_read(ap, SCR_STATUS); + printk(KERN_INFO "ata%u: no device found (phy stat %08x)\n", + ap->id, sstatus); + ata_port_disable(ap); + } + + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + + if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { + ata_port_disable(ap); + return; + } + + ata_bus_reset(ap); +} + +/** + * ata_port_disable - + * @ap: + * + * LOCKING: + */ + +void ata_port_disable(struct ata_port *ap) +{ + ap->device[0].class = ATA_DEV_NONE; + ap->device[1].class = ATA_DEV_NONE; + ap->flags |= ATA_FLAG_PORT_DISABLED; +} + +/** + * ata_set_mode - Program timings and issue SET FEATURES - XFER + * @ap: port on which timings will be programmed + * + * LOCKING: + * + */ +static void ata_set_mode(struct ata_port *ap) +{ + unsigned int force_pio, i; + + ata_host_set_pio(ap); + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + + ata_host_set_udma(ap); + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + +#ifdef ATA_FORCE_PIO + force_pio = 1; +#else + force_pio = 0; +#endif + + if (force_pio) { + ata_dev_set_pio(ap, 0); + ata_dev_set_pio(ap, 1); + } else { + ata_dev_set_udma(ap, 0); + ata_dev_set_udma(ap, 1); + } + + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + + if (ap->ops->post_set_mode) + ap->ops->post_set_mode(ap); + + for (i = 0; i < 2; i++) { + struct ata_device *dev = &ap->device[i]; + ata_dev_set_protocol(dev); + } +} + +/** + * ata_busy_sleep - sleep until BSY clears, or timeout + * @ap: port containing status register to be polled + * @tmout_pat: impatience timeout + * @tmout: overall timeout + * + * LOCKING: + * + */ + +static unsigned int ata_busy_sleep (struct ata_port *ap, + unsigned long tmout_pat, + unsigned long tmout) +{ + unsigned long timer_start, timeout; + u8 status; + + status = ata_busy_wait(ap, ATA_BUSY, 300); + timer_start = jiffies; + timeout = timer_start + tmout_pat; + while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) { + msleep(50); + status = ata_busy_wait(ap, ATA_BUSY, 3); + } + + if (status & ATA_BUSY) + printk(KERN_WARNING "ata%u is slow to respond, " + "please be patient\n", ap->id); + + timeout = timer_start + tmout; + while ((status & ATA_BUSY) && (time_before(jiffies, timeout))) { + msleep(50); + status = ata_chk_status(ap); + } + + if (status & ATA_BUSY) { + printk(KERN_ERR "ata%u failed to respond (%lu secs)\n", + ap->id, tmout / HZ); + return 1; + } + + return 0; +} + +static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int dev0 = devmask & (1 << 0); + unsigned int dev1 = devmask & (1 << 1); + unsigned long timeout; + + /* if device 0 was found in ata_dev_devchk, wait for its + * BSY bit to clear + */ + if (dev0) + ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* if device 1 was found in ata_dev_devchk, wait for + * register access, then wait for BSY to clear + */ + timeout = jiffies + ATA_TMOUT_BOOT; + while (dev1) { + u8 nsect, lbal; + + __ata_dev_select(ap, 1); + if (ap->flags & ATA_FLAG_MMIO) { + nsect = readb((void *) ioaddr->nsect_addr); + lbal = readb((void *) ioaddr->lbal_addr); + } else { + nsect = inb(ioaddr->nsect_addr); + lbal = inb(ioaddr->lbal_addr); + } + if ((nsect == 1) && (lbal == 1)) + break; + if (time_after(jiffies, timeout)) { + dev1 = 0; + break; + } + msleep(50); /* give drive a breather */ + } + if (dev1) + ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* is all this really necessary? */ + __ata_dev_select(ap, 0); + if (dev1) + __ata_dev_select(ap, 1); + if (dev0) + __ata_dev_select(ap, 0); +} + +/** + * ata_bus_edd - + * @ap: + * + * LOCKING: + * + */ + +static unsigned int ata_bus_edd(struct ata_port *ap) +{ + struct ata_taskfile tf; + + /* set up execute-device-diag (bus reset) taskfile */ + /* also, take interrupts to a known state (disabled) */ + DPRINTK("execute-device-diag\n"); + ata_tf_init(ap, &tf, 0); + tf.ctl |= ATA_NIEN; + tf.command = ATA_CMD_EDD; + tf.protocol = ATA_PROT_NODATA; + + /* do bus reset */ + ata_tf_to_host(ap, &tf); + + /* spec says at least 2ms. but who knows with those + * crazy ATAPI devices... + */ + msleep(150); + + return ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); +} + +static unsigned int ata_bus_softreset(struct ata_port *ap, + unsigned int devmask) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + DPRINTK("ata%u: bus reset via SRST\n", ap->id); + + /* software reset. causes dev0 to be selected */ + if (ap->flags & ATA_FLAG_MMIO) { + writeb(ap->ctl, ioaddr->ctl_addr); + udelay(20); /* FIXME: flush */ + writeb(ap->ctl | ATA_SRST, ioaddr->ctl_addr); + udelay(20); /* FIXME: flush */ + writeb(ap->ctl, ioaddr->ctl_addr); + } else { + outb(ap->ctl, ioaddr->ctl_addr); + udelay(10); + outb(ap->ctl | ATA_SRST, ioaddr->ctl_addr); + udelay(10); + outb(ap->ctl, ioaddr->ctl_addr); + } + + /* spec mandates ">= 2ms" before checking status. + * We wait 150ms, because that was the magic delay used for + * ATAPI devices in Hale Landis's ATADRVR, for the period of time + * between when the ATA command register is written, and then + * status is checked. Because waiting for "a while" before + * checking status is fine, post SRST, we perform this magic + * delay here as well. + */ + msleep(150); + + ata_bus_post_reset(ap, devmask); + + return 0; +} + +/** + * ata_bus_reset - reset host port and associated ATA channel + * @ap: port to reset + * + * This is typically the first time we actually start issuing + * commands to the ATA channel. We wait for BSY to clear, then + * issue EXECUTE DEVICE DIAGNOSTIC command, polling for its + * result. Determine what devices, if any, are on the channel + * by looking at the device 0/1 error register. Look at the signature + * stored in each device's taskfile registers, to determine if + * the device is ATA or ATAPI. + * + * LOCKING: + * Inherited from caller. Some functions called by this function + * obtain the host_set lock. + * + * SIDE EFFECTS: + * Sets ATA_FLAG_PORT_DISABLED if bus reset fails. + */ + +void ata_bus_reset(struct ata_port *ap) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; + u8 err; + unsigned int dev0, dev1 = 0, rc = 0, devmask = 0; + + DPRINTK("ENTER, host %u, port %u\n", ap->id, ap->port_no); + + /* determine if device 0/1 are present */ + if (ap->flags & ATA_FLAG_SATA_RESET) + dev0 = 1; + else { + dev0 = ata_dev_devchk(ap, 0); + if (slave_possible) + dev1 = ata_dev_devchk(ap, 1); + } + + if (dev0) + devmask |= (1 << 0); + if (dev1) + devmask |= (1 << 1); + + /* select device 0 again */ + __ata_dev_select(ap, 0); + + /* issue bus reset */ + if (ap->flags & ATA_FLAG_SRST) + rc = ata_bus_softreset(ap, devmask); + else if ((ap->flags & ATA_FLAG_SATA_RESET) == 0) { + /* set up device control */ + if (ap->flags & ATA_FLAG_MMIO) + writeb(ap->ctl, ioaddr->ctl_addr); + else + outb(ap->ctl, ioaddr->ctl_addr); + rc = ata_bus_edd(ap); + } + + if (rc) + goto err_out; + + /* + * determine by signature whether we have ATA or ATAPI devices + */ + err = ata_dev_try_classify(ap, 0); + if ((slave_possible) && (err != 0x81)) + ata_dev_try_classify(ap, 1); + + /* re-enable interrupts */ + ata_irq_on(ap); + + /* is double-select really necessary? */ + if (ap->device[1].class != ATA_DEV_NONE) + __ata_dev_select(ap, 1); + if (ap->device[0].class != ATA_DEV_NONE) + __ata_dev_select(ap, 0); + + /* if no devices were detected, disable this port */ + if ((ap->device[0].class == ATA_DEV_NONE) && + (ap->device[1].class == ATA_DEV_NONE)) + goto err_out; + + if (ap->flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST)) { + /* set up device control for ATA_FLAG_SATA_RESET */ + if (ap->flags & ATA_FLAG_MMIO) + writeb(ap->ctl, ioaddr->ctl_addr); + else + outb(ap->ctl, ioaddr->ctl_addr); + } + + DPRINTK("EXIT\n"); + return; + +err_out: + printk(KERN_ERR "ata%u: disabling port\n", ap->id); + ap->ops->port_disable(ap); + + DPRINTK("EXIT\n"); +} + +/** + * ata_host_set_pio - + * @ap: + * + * LOCKING: + */ + +static void ata_host_set_pio(struct ata_port *ap) +{ + struct ata_device *master, *slave; + unsigned int pio, i; + u16 mask; + + master = &ap->device[0]; + slave = &ap->device[1]; + + assert (ata_dev_present(master) || ata_dev_present(slave)); + + mask = ap->pio_mask; + if (ata_dev_present(master)) + mask &= (master->id[ATA_ID_PIO_MODES] & 0x03); + if (ata_dev_present(slave)) + mask &= (slave->id[ATA_ID_PIO_MODES] & 0x03); + + /* require pio mode 3 or 4 support for host and all devices */ + if (mask == 0) { + printk(KERN_WARNING "ata%u: no PIO3/4 support, ignoring\n", + ap->id); + goto err_out; + } + + pio = (mask & ATA_ID_PIO4) ? 4 : 3; + for (i = 0; i < ATA_MAX_DEVICES; i++) + if (ata_dev_present(&ap->device[i])) { + ap->device[i].pio_mode = (pio == 3) ? + XFER_PIO_3 : XFER_PIO_4; + if (ap->ops->set_piomode) + ap->ops->set_piomode(ap, &ap->device[i], pio); + } + + return; + +err_out: + ap->ops->port_disable(ap); +} + +/** + * ata_host_set_udma - + * @ap: + * + * LOCKING: + */ + +static void ata_host_set_udma(struct ata_port *ap) +{ + struct ata_device *master, *slave; + u16 mask; + unsigned int i, j; + int udma_mode = -1; + + master = &ap->device[0]; + slave = &ap->device[1]; + + assert (ata_dev_present(master) || ata_dev_present(slave)); + assert ((ap->flags & ATA_FLAG_PORT_DISABLED) == 0); + + DPRINTK("udma masks: host 0x%X, master 0x%X, slave 0x%X\n", + ap->udma_mask, + (!ata_dev_present(master)) ? 0xff : + (master->id[ATA_ID_UDMA_MODES] & 0xff), + (!ata_dev_present(slave)) ? 0xff : + (slave->id[ATA_ID_UDMA_MODES] & 0xff)); + + mask = ap->udma_mask; + if (ata_dev_present(master)) + mask &= (master->id[ATA_ID_UDMA_MODES] & 0xff); + if (ata_dev_present(slave)) + mask &= (slave->id[ATA_ID_UDMA_MODES] & 0xff); + + i = XFER_UDMA_7; + while (i >= XFER_UDMA_0) { + j = i - XFER_UDMA_0; + DPRINTK("mask 0x%X i 0x%X j %u\n", mask, i, j); + if (mask & (1 << j)) { + udma_mode = i; + break; + } + + i--; + } + + /* require udma for host and all attached devices */ + if (udma_mode < 0) { + printk(KERN_WARNING "ata%u: no UltraDMA support, ignoring\n", + ap->id); + goto err_out; + } + + for (i = 0; i < ATA_MAX_DEVICES; i++) + if (ata_dev_present(&ap->device[i])) { + ap->device[i].udma_mode = udma_mode; + if (ap->ops->set_udmamode) + ap->ops->set_udmamode(ap, &ap->device[i], + udma_mode); + } + + return; + +err_out: + ap->ops->port_disable(ap); +} + +/** + * ata_dev_set_xfermode - Issue SET FEATURES - XFER MODE command + * @ap: Port associated with device @dev + * @dev: Device to which command will be sent + * + * LOCKING: + */ + +static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev) +{ + struct ata_taskfile tf; + + /* set up set-features taskfile */ + DPRINTK("set features - xfer mode\n"); + ata_tf_init(ap, &tf, dev->devno); + tf.ctl |= ATA_NIEN; + tf.command = ATA_CMD_SET_FEATURES; + tf.feature = SETFEATURES_XFER; + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf.protocol = ATA_PROT_NODATA; + if (dev->flags & ATA_DFLAG_PIO) + tf.nsect = dev->pio_mode; + else + tf.nsect = dev->udma_mode; + + /* do bus reset */ + ata_tf_to_host(ap, &tf); + + /* crazy ATAPI devices... */ + if (dev->class == ATA_DEV_ATAPI) + msleep(150); + + ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + ata_irq_on(ap); /* re-enable interrupts */ + + ata_wait_idle(ap); + + DPRINTK("EXIT\n"); +} + +/** + * ata_dev_set_udma - Set ATA device's transfer mode to Ultra DMA + * @ap: Port associated with device @dev + * @device: Device whose mode will be set + * + * LOCKING: + */ + +static void ata_dev_set_udma(struct ata_port *ap, unsigned int device) +{ + struct ata_device *dev = &ap->device[device]; + + if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED)) + return; + + ata_dev_set_xfermode(ap, dev); + + assert((dev->udma_mode >= XFER_UDMA_0) && + (dev->udma_mode <= XFER_UDMA_7)); + printk(KERN_INFO "ata%u: dev %u configured for %s\n", + ap->id, device, + udma_str[dev->udma_mode - XFER_UDMA_0]); +} + +/** + * ata_dev_set_pio - Set ATA device's transfer mode to PIO + * @ap: Port associated with device @dev + * @device: Device whose mode will be set + * + * LOCKING: + */ + +static void ata_dev_set_pio(struct ata_port *ap, unsigned int device) +{ + struct ata_device *dev = &ap->device[device]; + + if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED)) + return; + + /* force PIO mode */ + dev->flags |= ATA_DFLAG_PIO; + + ata_dev_set_xfermode(ap, dev); + + assert((dev->pio_mode >= XFER_PIO_3) && + (dev->pio_mode <= XFER_PIO_4)); + printk(KERN_INFO "ata%u: dev %u configured for PIO%c\n", + ap->id, device, + dev->pio_mode == 3 ? '3' : '4'); +} + +/** + * ata_sg_clean - + * @qc: + * + * LOCKING: + */ + +static void ata_sg_clean(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct scsi_cmnd *cmd = qc->scsicmd; + struct scatterlist *sg = qc->sg; + int dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + + assert(dir == SCSI_DATA_READ || dir == SCSI_DATA_WRITE); + assert(qc->flags & ATA_QCFLAG_SG); + assert(sg != NULL); + + if (!cmd->use_sg) + assert(qc->n_elem == 1); + + DPRINTK("unmapping %u sg elements\n", qc->n_elem); + + if (cmd->use_sg) + pci_unmap_sg(ap->host_set->pdev, sg, qc->n_elem, dir); + else + pci_unmap_single(ap->host_set->pdev, sg_dma_address(&sg[0]), + sg_dma_len(&sg[0]), dir); + + qc->flags &= ~ATA_QCFLAG_SG; + qc->sg = NULL; +} + +/** + * ata_fill_sg - + * @qc: + * + * LOCKING: + * + */ +void ata_fill_sg(struct ata_queued_cmd *qc) +{ + struct scatterlist *sg = qc->sg; + struct ata_port *ap = qc->ap; + unsigned int idx, nelem; + + assert(sg != NULL); + assert(qc->n_elem > 0); + + idx = 0; + for (nelem = qc->n_elem; nelem; nelem--,sg++) { + u32 addr, boundary; + u32 sg_len, len; + + /* determine if physical DMA addr spans 64K boundary. + * Note h/w doesn't support 64-bit, so we unconditionally + * truncate dma_addr_t to u32. + */ + addr = (u32) sg_dma_address(sg); + sg_len = sg_dma_len(sg); + + while (sg_len) { + boundary = (addr & ~0xffff) + (0xffff + 1); + len = sg_len; + if ((addr + sg_len) > boundary) + len = boundary - addr; + + ap->prd[idx].addr = cpu_to_le32(addr); + ap->prd[idx].flags_len = cpu_to_le32(len & 0xffff); + VPRINTK("PRD[%u] = (0x%X, 0x%X)\n", idx, addr, len); + + idx++; + sg_len -= len; + addr += len; + } + } + + if (idx) + ap->prd[idx - 1].flags_len |= cpu_to_le32(ATA_PRD_EOT); +} + +/** + * ata_sg_setup_one - + * @qc: + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * + */ + +static int ata_sg_setup_one(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct scsi_cmnd *cmd = qc->scsicmd; + int dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + struct scatterlist *sg = qc->sg; + unsigned int have_sg = (qc->flags & ATA_QCFLAG_SG); + dma_addr_t dma_address; + + assert(sg == &qc->sgent); + assert(qc->n_elem == 1); + + sg->address = cmd->request_buffer; + sg->page = virt_to_page(cmd->request_buffer); + sg->offset = (unsigned long) cmd->request_buffer & ~PAGE_MASK; + sg_dma_len(sg) = cmd->request_bufflen; + + if (!have_sg) + return 0; + + dma_address = pci_map_single(ap->host_set->pdev, cmd->request_buffer, + cmd->request_bufflen, dir); + + sg_dma_address(sg) = dma_address; + + DPRINTK("mapped buffer of %d bytes for %s\n", cmd->request_bufflen, + qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read"); + + return 0; +} + +/** + * ata_sg_setup - + * @qc: + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * + */ + +static int ata_sg_setup(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct scsi_cmnd *cmd = qc->scsicmd; + struct scatterlist *sg; + int n_elem; + unsigned int have_sg = (qc->flags & ATA_QCFLAG_SG); + + VPRINTK("ENTER, ata%u, use_sg %d\n", ap->id, cmd->use_sg); + assert(cmd->use_sg > 0); + + sg = (struct scatterlist *)cmd->request_buffer; + if (have_sg) { + int dir = scsi_to_pci_dma_dir(cmd->sc_data_direction); + n_elem = pci_map_sg(ap->host_set->pdev, sg, cmd->use_sg, dir); + if (n_elem < 1) + return -1; + DPRINTK("%d sg elements mapped\n", n_elem); + } else { + n_elem = cmd->use_sg; + } + qc->n_elem = n_elem; + + return 0; +} + +/** + * ata_pio_poll - + * @ap: + * + * LOCKING: + * + * RETURNS: + * + */ + +static unsigned long ata_pio_poll(struct ata_port *ap) +{ + u8 status; + unsigned int poll_state = PIO_ST_UNKNOWN; + unsigned int reg_state = PIO_ST_UNKNOWN; + const unsigned int tmout_state = PIO_ST_TMOUT; + + switch (ap->pio_task_state) { + case PIO_ST: + case PIO_ST_POLL: + poll_state = PIO_ST_POLL; + reg_state = PIO_ST; + break; + case PIO_ST_LAST: + case PIO_ST_LAST_POLL: + poll_state = PIO_ST_LAST_POLL; + reg_state = PIO_ST_LAST; + break; + default: + BUG(); + break; + } + + status = ata_chk_status(ap); + if (status & ATA_BUSY) { + if (time_after(jiffies, ap->pio_task_timeout)) { + ap->pio_task_state = tmout_state; + return 0; + } + ap->pio_task_state = poll_state; + return ATA_SHORT_PAUSE; + } + + ap->pio_task_state = reg_state; + return 0; +} + +/** + * ata_pio_complete - + * @ap: + * + * LOCKING: + */ + +static void ata_pio_complete (struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + u8 drv_stat; + + /* + * This is purely hueristic. This is a fast path. + * Sometimes when we enter, BSY will be cleared in + * a chk-status or two. If not, the drive is probably seeking + * or something. Snooze for a couple msecs, then + * chk-status again. If still busy, fall back to + * PIO_ST_POLL state. + */ + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10); + if (drv_stat & (ATA_BUSY | ATA_DRQ)) { + msleep(2); + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10); + if (drv_stat & (ATA_BUSY | ATA_DRQ)) { + ap->pio_task_state = PIO_ST_LAST_POLL; + ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO; + return; + } + } + + drv_stat = ata_wait_idle(ap); + if (drv_stat & (ATA_BUSY | ATA_DRQ)) { + ap->pio_task_state = PIO_ST_ERR; + return; + } + + qc = ata_qc_from_tag(ap, ap->active_tag); + assert(qc != NULL); + + ap->pio_task_state = PIO_ST_IDLE; + + ata_irq_on(ap); + + ata_qc_complete(qc, drv_stat); +} + +/** + * ata_pio_sector - + * @ap: + * + * LOCKING: + */ + +static void ata_pio_sector(struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + struct scatterlist *sg; + struct scsi_cmnd *cmd; + unsigned char *buf; + u8 status; + + /* + * This is purely hueristic. This is a fast path. + * Sometimes when we enter, BSY will be cleared in + * a chk-status or two. If not, the drive is probably seeking + * or something. Snooze for a couple msecs, then + * chk-status again. If still busy, fall back to + * PIO_ST_POLL state. + */ + status = ata_busy_wait(ap, ATA_BUSY, 5); + if (status & ATA_BUSY) { + msleep(2); + status = ata_busy_wait(ap, ATA_BUSY, 10); + if (status & ATA_BUSY) { + ap->pio_task_state = PIO_ST_POLL; + ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO; + return; + } + } + + /* handle BSY=0, DRQ=0 as error */ + if ((status & ATA_DRQ) == 0) { + ap->pio_task_state = PIO_ST_ERR; + return; + } + + qc = ata_qc_from_tag(ap, ap->active_tag); + assert(qc != NULL); + + cmd = qc->scsicmd; + sg = qc->sg; + + if (qc->cursect == (qc->nsect - 1)) + ap->pio_task_state = PIO_ST_LAST; + + buf = kmap(sg[qc->cursg].page) + + sg[qc->cursg].offset + (qc->cursg_ofs * ATA_SECT_SIZE); + + qc->cursect++; + qc->cursg_ofs++; + + if (cmd->use_sg) + if ((qc->cursg_ofs * ATA_SECT_SIZE) == sg_dma_len(&sg[qc->cursg])) { + qc->cursg++; + qc->cursg_ofs = 0; + } + + DPRINTK("data %s, drv_stat 0x%X\n", + qc->tf.flags & ATA_TFLAG_WRITE ? "write" : "read", + status); + + /* do the actual data transfer */ + /* FIXME: mmio-ize */ + if (qc->tf.flags & ATA_TFLAG_WRITE) + outsl(ap->ioaddr.data_addr, buf, ATA_SECT_DWORDS); + else + insl(ap->ioaddr.data_addr, buf, ATA_SECT_DWORDS); + + kunmap(sg[qc->cursg].page); +} + +static void ata_pio_task(void *_data) +{ + struct ata_port *ap = _data; + unsigned long timeout = 0; + + switch (ap->pio_task_state) { + case PIO_ST: + ata_pio_sector(ap); + break; + + case PIO_ST_LAST: + ata_pio_complete(ap); + break; + + case PIO_ST_POLL: + case PIO_ST_LAST_POLL: + timeout = ata_pio_poll(ap); + break; + + case PIO_ST_TMOUT: + printk(KERN_ERR "ata%d: FIXME: PIO_ST_TMOUT\n", /* FIXME */ + ap->id); + timeout = 11 * HZ; + break; + + case PIO_ST_ERR: + printk(KERN_ERR "ata%d: FIXME: PIO_ST_ERR\n", /* FIXME */ + ap->id); + timeout = 11 * HZ; + break; + } + + if (timeout) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + } + + if ((ap->pio_task_state != PIO_ST_IDLE) && + (ap->pio_task_state != PIO_ST_TMOUT) && + (ap->pio_task_state != PIO_ST_ERR)) + schedule_task(&ap->pio_task); +} + +/** + * ata_eng_timeout - Handle timeout of queued command + * @ap: Port on which timed-out command is active + * + * Some part of the kernel (currently, only the SCSI layer) + * has noticed that the active command on port @ap has not + * completed after a specified length of time. Handle this + * condition by disabling DMA (if necessary) and completing + * transactions, with error if necessary. + * + * This also handles the case of the "lost interrupt", where + * for some reason (possibly hardware bug, possibly driver bug) + * an interrupt was not delivered to the driver, even though the + * transaction completed successfully. + * + * LOCKING: + * Inherited from SCSI layer (none, can sleep) + */ + +void ata_eng_timeout(struct ata_port *ap) +{ + u8 host_stat, drv_stat; + struct ata_queued_cmd *qc; + + DPRINTK("ENTER\n"); + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + goto out; + } + + /* hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + qc->scsidone = scsi_finish_command; + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + if (ap->flags & ATA_FLAG_MMIO) { + void *mmio = (void *) ap->ioaddr.bmdma_addr; + host_stat = readb(mmio + ATA_DMA_STATUS); + } else + host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + + printk(KERN_ERR "ata%u: DMA timeout, stat 0x%x\n", + ap->id, host_stat); + + ata_dma_complete(qc, host_stat); + break; + + case ATA_PROT_NODATA: + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + + printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n", + ap->id, qc->tf.command, drv_stat); + + ata_qc_complete(qc, drv_stat); + break; + + default: + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + + printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n", + ap->id, qc->tf.command, drv_stat); + + ata_qc_complete(qc, drv_stat); + break; + } + +out: + DPRINTK("EXIT\n"); +} + +/** + * ata_qc_new - Request an available ATA command, for queueing + * @ap: Port associated with device @dev + * @dev: Device from whom we request an available command structure + * + * LOCKING: + */ + +static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) +{ + struct ata_queued_cmd *qc = NULL; + unsigned int i; + + for (i = 0; i < ATA_MAX_QUEUE; i++) + if (!test_and_set_bit(i, &ap->qactive)) { + qc = ata_qc_from_tag(ap, i); + break; + } + + if (qc) + qc->tag = i; + + return qc; +} + +/** + * ata_qc_new_init - Request an available ATA command, and initialize it + * @ap: Port associated with device @dev + * @dev: Device from whom we request an available command structure + * + * LOCKING: + */ + +struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, + struct ata_device *dev) +{ + struct ata_queued_cmd *qc; + + qc = ata_qc_new(ap); + if (qc) { + qc->sg = NULL; + qc->flags = 0; + qc->scsicmd = NULL; + qc->ap = ap; + qc->dev = dev; + qc->cursect = qc->cursg = qc->cursg_ofs = 0; + qc->nsect = 0; + + ata_tf_init(ap, &qc->tf, dev->devno); + + if (likely((dev->flags & ATA_DFLAG_PIO) == 0)) + qc->flags |= ATA_QCFLAG_DMA; + if (dev->flags & ATA_DFLAG_LBA48) + qc->tf.flags |= ATA_TFLAG_LBA48; + } + + return qc; +} + +/** + * ata_qc_complete - Complete an active ATA command + * @qc: Command to complete + * @drv_stat: ATA status register contents + * + * LOCKING: + * + */ + +void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) +{ + struct ata_port *ap = qc->ap; + struct scsi_cmnd *cmd = qc->scsicmd; + unsigned int tag, do_clear = 0; + + assert(qc != NULL); /* ata_qc_from_tag _might_ return NULL */ + assert(qc->flags & ATA_QCFLAG_ACTIVE); + + if (likely(qc->flags & ATA_QCFLAG_SG)) + ata_sg_clean(qc); + + if (cmd) { + if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) { + if (is_atapi_taskfile(&qc->tf)) + cmd->result = SAM_STAT_CHECK_CONDITION; + else + ata_to_sense_error(qc); + } else { + cmd->result = SAM_STAT_GOOD; + } + + qc->scsidone(cmd); + } + + qc->flags &= ~ATA_QCFLAG_ACTIVE; + tag = qc->tag; + if (likely(ata_tag_valid(tag))) { + if (tag == ap->active_tag) + ap->active_tag = ATA_TAG_POISON; + qc->tag = ATA_TAG_POISON; + do_clear = 1; + } + + if (qc->waiting) + complete(qc->waiting); + + if (likely(do_clear)) + clear_bit(tag, &ap->qactive); +} + +/** + * ata_qc_issue - issue taskfile to device + * @qc: command to issue to device + * + * Prepare an ATA command to submission to device. + * This includes mapping the data into a DMA-able + * area, filling in the S/G table, and finally + * writing the taskfile to hardware, starting the command. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, negative on error. + */ + +int ata_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct scsi_cmnd *cmd = qc->scsicmd; + + if (qc->flags & ATA_QCFLAG_SG) { + /* set up SG table */ + if (cmd->use_sg) { + if (ata_sg_setup(qc)) + goto err_out; + } else { + if (ata_sg_setup_one(qc)) + goto err_out; + } + + ap->ops->fill_sg(qc); + } + + qc->ap->active_tag = qc->tag; + qc->flags |= ATA_QCFLAG_ACTIVE; + + return ata_qc_issue_prot(qc); + +err_out: + return -1; +} + +/** + * ata_qc_issue_prot - issue taskfile to device in proto-dependent manner + * @qc: command to issue to device + * + * Using various libata functions and hooks, this function + * starts an ATA command. ATA commands are grouped into + * classes called "protocols", and issuing each type of protocol + * is slightly different. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, negative on error. + */ + +static int ata_qc_issue_prot(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + + ata_dev_select(ap, qc->dev->devno, 1, 0); + + switch (qc->tf.protocol) { + case ATA_PROT_NODATA: + ata_tf_to_host_nolock(ap, &qc->tf); + break; + + case ATA_PROT_DMA: + ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ + ap->ops->bmdma_setup(qc); /* set up bmdma */ + ap->ops->bmdma_start(qc); /* initiate bmdma */ + break; + + case ATA_PROT_PIO: /* load tf registers, initiate polling pio */ + ata_qc_set_polling(qc); + ata_tf_to_host_nolock(ap, &qc->tf); + ap->pio_task_state = PIO_ST; + schedule_task(&ap->pio_task); + break; + + case ATA_PROT_ATAPI: + ata_tf_to_host_nolock(ap, &qc->tf); + schedule_task(&ap->packet_task); + break; + + case ATA_PROT_ATAPI_DMA: + ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ + ap->ops->bmdma_setup(qc); /* set up bmdma */ + schedule_task(&ap->packet_task); + break; + + default: + return -1; + } + + return 0; +} + +/** + * ata_bmdma_setup_mmio - Set up PCI IDE BMDMA transaction (MMIO) + * @qc: Info associated with this ATA transaction. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_bmdma_setup_mmio (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); + u8 host_stat, dmactl; + void *mmio = (void *) ap->ioaddr.bmdma_addr; + + /* load PRD table addr. */ + mb(); /* make sure PRD table writes are visible to controller */ + writel(ap->prd_dma, mmio + ATA_DMA_TABLE_OFS); + + /* specify data direction, triple-check start bit is clear */ + dmactl = readb(mmio + ATA_DMA_CMD); + dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); + if (!rw) + dmactl |= ATA_DMA_WR; + writeb(dmactl, mmio + ATA_DMA_CMD); + + /* clear interrupt, error bits */ + host_stat = readb(mmio + ATA_DMA_STATUS); + writeb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR, mmio + ATA_DMA_STATUS); + + /* issue r/w command */ + ap->ops->exec_command(ap, &qc->tf); +} + +/** + * ata_bmdma_start_mmio - Start a PCI IDE BMDMA transaction (MMIO) + * @qc: Info associated with this ATA transaction. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_bmdma_start_mmio (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void *mmio = (void *) ap->ioaddr.bmdma_addr; + u8 dmactl; + + /* start host DMA transaction */ + dmactl = readb(mmio + ATA_DMA_CMD); + writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD); + + /* Strictly, one may wish to issue a readb() here, to + * flush the mmio write. However, control also passes + * to the hardware at this point, and it will interrupt + * us when we are to resume control. So, in effect, + * we don't care when the mmio write flushes. + * Further, a read of the DMA status register _immediately_ + * following the write may not be what certain flaky hardware + * is expected, so I think it is best to not add a readb() + * without first all the MMIO ATA cards/mobos. + * Or maybe I'm just being paranoid. + */ +} + +/** + * ata_bmdma_setup_pio - Set up PCI IDE BMDMA transaction (PIO) + * @qc: Info associated with this ATA transaction. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_bmdma_setup_pio (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); + u8 host_stat, dmactl; + + /* load PRD table addr. */ + outl(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); + + /* specify data direction, triple-check start bit is clear */ + dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); + if (!rw) + dmactl |= ATA_DMA_WR; + outb(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + /* clear interrupt, error bits */ + host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + outb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR, + ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + + /* issue r/w command */ + ap->ops->exec_command(ap, &qc->tf); +} + +/** + * ata_bmdma_start_pio - Start a PCI IDE BMDMA transaction (PIO) + * @qc: Info associated with this ATA transaction. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_bmdma_start_pio (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + u8 dmactl; + + /* start host DMA transaction */ + dmactl = inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + outb(dmactl | ATA_DMA_START, + ap->ioaddr.bmdma_addr + ATA_DMA_CMD); +} + +/** + * ata_dma_complete - Complete an active ATA BMDMA command + * @qc: Command to complete + * @host_stat: BMDMA status register contents + * + * LOCKING: + */ + +static void ata_dma_complete(struct ata_queued_cmd *qc, u8 host_stat) +{ + struct ata_port *ap = qc->ap; + VPRINTK("ENTER\n"); + + if (ap->flags & ATA_FLAG_MMIO) { + void *mmio = (void *) ap->ioaddr.bmdma_addr; + + /* clear start/stop bit */ + writeb(readb(mmio + ATA_DMA_CMD) & ~ATA_DMA_START, + mmio + ATA_DMA_CMD); + + /* ack intr, err bits */ + writeb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR, + mmio + ATA_DMA_STATUS); + } else { + /* clear start/stop bit */ + outb(inb(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START, + ap->ioaddr.bmdma_addr + ATA_DMA_CMD); + + /* ack intr, err bits */ + outb(host_stat | ATA_DMA_INTR | ATA_DMA_ERR, + ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + } + + + /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ + ata_altstatus(ap); /* dummy read */ + + DPRINTK("host %u, host_stat==0x%X, drv_stat==0x%X\n", + ap->id, (u32) host_stat, (u32) ata_chk_status(ap)); + + /* get drive status; clear intr; complete txn */ + ata_qc_complete(qc, ata_wait_idle(ap)); +} + +/** + * ata_host_intr - Handle host interrupt for given (port, task) + * @ap: Port on which interrupt arrived (possibly...) + * @qc: Taskfile currently active in engine + * + * Handle host interrupt for given queued command. Currently, + * only DMA interrupts are handled. All other commands are + * handled via polling with interrupts disabled (nIEN bit). + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * One if interrupt was handled, zero if not (shared irq). + */ + +inline unsigned int ata_host_intr (struct ata_port *ap, + struct ata_queued_cmd *qc) +{ + u8 status, host_stat; + unsigned int handled = 0; + + switch (qc->tf.protocol) { + + /* BMDMA completion */ + case ATA_PROT_DMA: + case ATA_PROT_ATAPI_DMA: + if (ap->flags & ATA_FLAG_MMIO) { + void *mmio = (void *) ap->ioaddr.bmdma_addr; + host_stat = readb(mmio + ATA_DMA_STATUS); + } else + host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + VPRINTK("BUS_DMA (host_stat 0x%X)\n", host_stat); + + if (!(host_stat & ATA_DMA_INTR)) { + ap->stats.idle_irq++; + break; + } + + ata_dma_complete(qc, host_stat); + handled = 1; + break; + + /* command completion, but no data xfer */ + /* FIXME: a shared interrupt _will_ cause a non-data command + * to be completed prematurely, with an error. + * + * This doesn't matter right now, since we aren't sending + * non-data commands down this pipe except in development + * situations. + */ + case ATA_PROT_ATAPI: + case ATA_PROT_NODATA: + status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status); + ata_qc_complete(qc, status); + handled = 1; + break; + + default: + ap->stats.idle_irq++; + +#ifdef ATA_IRQ_TRAP + if ((ap->stats.idle_irq % 1000) == 0) { + handled = 1; + ata_irq_ack(ap, 0); /* debug trap */ + printk(KERN_WARNING "ata%d: irq trap\n", ap->id); + } +#endif + break; + } + + return handled; +} + +/** + * ata_interrupt - Default ATA host interrupt handler + * @irq: irq line + * @dev_instance: pointer to our host information structure + * @regs: unused + * + * LOCKING: + * + * RETURNS: + * + */ + +irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + unsigned int i; + unsigned int handled = 0; + unsigned long flags; + + /* TODO: make _irqsave conditional on x86 PCI IDE legacy mode */ + spin_lock_irqsave(&host_set->lock, flags); + + for (i = 0; i < host_set->n_ports; i++) { + struct ata_port *ap; + + ap = host_set->ports[i]; + if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && (!(qc->tf.ctl & ATA_NIEN))) + handled += ata_host_intr(ap, qc); + } + } + + spin_unlock_irqrestore(&host_set->lock, flags); + + return IRQ_RETVAL(handled); +} + +/** + * ata_thread_iter - + * @ap: + * + * LOCKING: + * + * RETURNS: + * + */ + +static unsigned long ata_thread_iter(struct ata_port *ap) +{ + long timeout = 0; + + DPRINTK("ata%u: thr_state %s\n", + ap->id, ata_thr_state_name(ap->thr_state)); + + switch (ap->thr_state) { + case THR_UNKNOWN: + ap->thr_state = THR_PORT_RESET; + break; + + case THR_PROBE_START: + ap->thr_state = THR_PORT_RESET; + break; + + case THR_PORT_RESET: + ata_port_reset(ap); + break; + + case THR_PROBE_SUCCESS: + up(&ap->probe_sem); + ap->thr_state = THR_IDLE; + break; + + case THR_PROBE_FAILED: + up(&ap->probe_sem); + ap->thr_state = THR_AWAIT_DEATH; + break; + + case THR_AWAIT_DEATH: + case THR_IDLE: + timeout = -1; + break; + + default: + printk(KERN_DEBUG "ata%u: unknown thr state %s\n", + ap->id, ata_thr_state_name(ap->thr_state)); + break; + } + + DPRINTK("ata%u: new thr_state %s, returning %ld\n", + ap->id, ata_thr_state_name(ap->thr_state), timeout); + return timeout; +} + +/** + * atapi_packet_task - Write CDB bytes to hardware + * @_data: Port to which ATAPI device is attached. + * + * When device has indicated its readiness to accept + * a CDB, this function is called. Send the CDB. + * If DMA is to be performed, exit immediately. + * Otherwise, we are in polling mode, so poll + * status under operation succeeds or fails. + * + * LOCKING: + * Kernel thread context (may sleep) + */ + +static void atapi_packet_task(void *_data) +{ + struct ata_port *ap = _data; + struct ata_queued_cmd *qc; + u8 status; + + qc = ata_qc_from_tag(ap, ap->active_tag); + assert(qc != NULL); + assert(qc->flags & ATA_QCFLAG_ACTIVE); + + /* sleep-wait for BSY to clear */ + DPRINTK("busy wait\n"); + if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB)) + goto err_out; + + /* make sure DRQ is set */ + status = ata_chk_status(ap); + if ((status & ATA_DRQ) == 0) + goto err_out; + + /* send SCSI cdb */ + /* FIXME: mmio-ize */ + DPRINTK("send cdb\n"); + outsl(ap->ioaddr.data_addr, + qc->scsicmd->cmnd, ap->host->max_cmd_len / 4); + + /* if we are DMA'ing, irq handler takes over from here */ + if (qc->tf.protocol == ATA_PROT_ATAPI_DMA) + ap->ops->bmdma_start(qc); /* initiate bmdma */ + + /* non-data commands are also handled via irq */ + else if (qc->scsicmd->sc_data_direction == SCSI_DATA_NONE) { + /* do nothing */ + } + + /* PIO commands are handled by polling */ + else { + ap->pio_task_state = PIO_ST; + schedule_task(&ap->pio_task); + } + + return; + +err_out: + ata_qc_complete(qc, ATA_ERR); +} + +int ata_port_start (struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + + ap->prd = pci_alloc_consistent(pdev, ATA_PRD_TBL_SZ, &ap->prd_dma); + if (!ap->prd) + return -ENOMEM; + + DPRINTK("prd alloc, virt %p, dma %llx\n", ap->prd, (unsigned long long) ap->prd_dma); + + return 0; +} + +void ata_port_stop (struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + + pci_free_consistent(pdev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma); +} + +static void ata_probe_task(void *_data) +{ + struct ata_port *ap = _data; + long timeout; + + timeout = ata_thread_iter(ap); + if (timeout < 0) + return; + + if (timeout > 0) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(timeout); + } + + schedule_task(&ap->probe_task); +} + +/** + * ata_host_remove - Unregister SCSI host structure with upper layers + * @ap: Port to unregister + * @do_unregister: 1 if we fully unregister, 0 to just stop the port + * + * LOCKING: + */ + +static void ata_host_remove(struct ata_port *ap, unsigned int do_unregister) +{ + struct Scsi_Host *sh = ap->host; + + DPRINTK("ENTER\n"); + + if (do_unregister) + scsi_unregister(sh); + + ap->ops->port_stop(ap); +} + +/** + * ata_host_init - Initialize an ata_port structure + * @ap: Structure to initialize + * @host: associated SCSI mid-layer structure + * @host_set: Collection of hosts to which @ap belongs + * @ent: Probe information provided by low-level driver + * @port_no: Port number associated with this ata_port + * + * LOCKING: + * + */ + +static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, + struct ata_host_set *host_set, + struct ata_probe_ent *ent, unsigned int port_no) +{ + unsigned int i; + + host->max_id = 16; + host->max_lun = 1; + host->max_channel = 1; + host->unique_id = ata_unique_id++; + host->max_cmd_len = 12; + host->pci_dev = ent->pdev; + + ap->flags = ATA_FLAG_PORT_DISABLED; + ap->id = host->unique_id; + ap->host = host; + ap->ctl = ATA_DEVCTL_OBS; + ap->host_set = host_set; + ap->port_no = port_no; + ap->pio_mask = ent->pio_mask; + ap->udma_mask = ent->udma_mask; + ap->flags |= ent->host_flags; + ap->ops = ent->port_ops; + ap->thr_state = THR_PROBE_START; + ap->cbl = ATA_CBL_NONE; + ap->device[0].flags = ATA_DFLAG_MASTER; + ap->active_tag = ATA_TAG_POISON; + ap->last_ctl = 0xFF; + + INIT_TQUEUE(&ap->packet_task, atapi_packet_task, ap); + INIT_TQUEUE(&ap->pio_task, ata_pio_task, ap); + INIT_TQUEUE(&ap->probe_task, ata_probe_task, ap); + + for (i = 0; i < ATA_MAX_DEVICES; i++) + ap->device[i].devno = i; + + init_MUTEX_LOCKED(&ap->probe_sem); + +#ifdef ATA_IRQ_TRAP + ap->stats.unhandled_irq = 1; + ap->stats.idle_irq = 1; +#endif + + memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports)); +} + +/** + * ata_host_add - Attach low-level ATA driver to system + * @ent: Information provided by low-level driver + * @host_set: Collections of ports to which we add + * @port_no: Port number associated with this host + * + * LOCKING: + * + * RETURNS: + * + */ + +static struct ata_port * ata_host_add(struct ata_probe_ent *ent, + struct ata_host_set *host_set, + unsigned int port_no) +{ + struct Scsi_Host *host; + struct ata_port *ap; + int rc; + + DPRINTK("ENTER\n"); + host = scsi_register(ent->sht, sizeof(struct ata_port)); + if (!host) + return NULL; + + ap = (struct ata_port *) &host->hostdata[0]; + + ata_host_init(ap, host, host_set, ent, port_no); + + rc = ap->ops->port_start(ap); + if (rc) + goto err_out; + + return ap; + +err_out: + scsi_unregister(host); + return NULL; +} + +/** + * ata_device_add - + * @ent: + * + * LOCKING: + * + * RETURNS: + * + */ + +int ata_device_add(struct ata_probe_ent *ent) +{ + unsigned int count = 0, i; + struct pci_dev *pdev = ent->pdev; + struct ata_host_set *host_set; + + DPRINTK("ENTER\n"); + /* alloc a container for our list of ATA ports (buses) */ + host_set = kmalloc(sizeof(struct ata_host_set) + + (ent->n_ports * sizeof(void *)), GFP_KERNEL); + if (!host_set) + return 0; + memset(host_set, 0, sizeof(struct ata_host_set) + (ent->n_ports * sizeof(void *))); + spin_lock_init(&host_set->lock); + + host_set->pdev = pdev; + host_set->n_ports = ent->n_ports; + host_set->irq = ent->irq; + host_set->mmio_base = ent->mmio_base; + host_set->private_data = ent->private_data; + + /* register each port bound to this device */ + for (i = 0; i < ent->n_ports; i++) { + struct ata_port *ap; + + ap = ata_host_add(ent, host_set, i); + if (!ap) + goto err_out; + + host_set->ports[i] = ap; + + /* print per-port info to dmesg */ + printk(KERN_INFO "ata%u: %cATA max %s cmd 0x%lX ctl 0x%lX " + "bmdma 0x%lX irq %lu\n", + ap->id, + ap->flags & ATA_FLAG_SATA ? 'S' : 'P', + ata_udma_string(ent->udma_mask), + ap->ioaddr.cmd_addr, + ap->ioaddr.ctl_addr, + ap->ioaddr.bmdma_addr, + ent->irq); + + count++; + } + + if (!count) { + kfree(host_set); + return 0; + } + + /* obtain irq, that is shared between channels */ + if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, + DRV_NAME, host_set)) + goto err_out; + + /* perform each probe synchronously */ + DPRINTK("probe begin\n"); + for (i = 0; i < count; i++) { + struct ata_port *ap; + + ap = host_set->ports[i]; + + DPRINTK("ata%u: probe begin\n", ap->id); + schedule_task(&ap->probe_task); /* start probe */ + + DPRINTK("ata%u: probe-wait begin\n", ap->id); + down(&ap->probe_sem); /* wait for end */ + + DPRINTK("ata%u: probe-wait end\n", ap->id); + } + + pci_set_drvdata(pdev, host_set); + + VPRINTK("EXIT, returning %u\n", ent->n_ports); + return ent->n_ports; /* success */ + +err_out: + for (i = 0; i < count; i++) { + ata_host_remove(host_set->ports[i], 1); + } + kfree(host_set); + VPRINTK("EXIT, returning 0\n"); + return 0; +} + +/** + * ata_scsi_detect - + * @sht: + * + * LOCKING: + * + * RETURNS: + * + */ + +int ata_scsi_detect(Scsi_Host_Template *sht) +{ + struct list_head *node; + struct ata_probe_ent *ent; + int count = 0; + + VPRINTK("ENTER\n"); + + sht->use_new_eh_code = 1; /* IORL hack, part deux */ + + spin_lock(&ata_module_lock); + while (!list_empty(&ata_probe_list)) { + node = ata_probe_list.next; + ent = list_entry(node, struct ata_probe_ent, node); + list_del(node); + + spin_unlock(&ata_module_lock); + + count += ata_device_add(ent); + kfree(ent); + + spin_lock(&ata_module_lock); + } + spin_unlock(&ata_module_lock); + + VPRINTK("EXIT, returning %d\n", count); + return count; +} + +/** + * ata_scsi_release - SCSI layer callback hook for host unload + * @host: libata host to be unloaded + * + * Performs all duties necessary to shut down a libata port... + * Kill port kthread, disable port, and release resources. + * + * LOCKING: + * Inherited from SCSI layer. + * + * RETURNS: + * One. + */ + +int ata_scsi_release(struct Scsi_Host *host) +{ + struct ata_port *ap = (struct ata_port *) &host->hostdata[0]; + + DPRINTK("ENTER\n"); + + ap->ops->port_disable(ap); + ata_host_remove(ap, 0); + + DPRINTK("EXIT\n"); + return 1; +} + +/** + * ata_std_ports - initialize ioaddr with standard port offsets. + * @ioaddr: IO address structure to be initialized + */ +void ata_std_ports(struct ata_ioports *ioaddr) +{ + ioaddr->data_addr = ioaddr->cmd_addr + ATA_REG_DATA; + ioaddr->error_addr = ioaddr->cmd_addr + ATA_REG_ERR; + ioaddr->feature_addr = ioaddr->cmd_addr + ATA_REG_FEATURE; + ioaddr->nsect_addr = ioaddr->cmd_addr + ATA_REG_NSECT; + ioaddr->lbal_addr = ioaddr->cmd_addr + ATA_REG_LBAL; + ioaddr->lbam_addr = ioaddr->cmd_addr + ATA_REG_LBAM; + ioaddr->lbah_addr = ioaddr->cmd_addr + ATA_REG_LBAH; + ioaddr->device_addr = ioaddr->cmd_addr + ATA_REG_DEVICE; + ioaddr->status_addr = ioaddr->cmd_addr + ATA_REG_STATUS; + ioaddr->command_addr = ioaddr->cmd_addr + ATA_REG_CMD; +} + +/** + * ata_pci_init_one - Initialize/register PCI IDE host controller + * @pdev: Controller to be initialized + * @port_info: Information from low-level host driver + * @n_ports: Number of ports attached to host controller + * + * LOCKING: + * Inherited from PCI layer (may sleep). + * + * RETURNS: + * + */ + +int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, + unsigned int n_ports) +{ + struct ata_probe_ent *probe_ent, *probe_ent2 = NULL; + struct ata_port_info *port0, *port1; + u8 tmp8, mask; + unsigned int legacy_mode = 0; + int rc; + + DPRINTK("ENTER\n"); + + port0 = port_info[0]; + if (n_ports > 1) + port1 = port_info[1]; + else + port1 = port0; + + if ((port0->host_flags & ATA_FLAG_NO_LEGACY) == 0) { + /* TODO: support transitioning to native mode? */ + pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); + mask = (1 << 2) | (1 << 0); + if ((tmp8 & mask) != mask) + legacy_mode = (1 << 3); + } + + /* FIXME... */ + if ((!legacy_mode) && (n_ports > 1)) { + printk(KERN_ERR "ata: BUG: native mode, n_ports > 1\n"); + return -EINVAL; + } + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + if (legacy_mode) { + if (!request_region(0x1f0, 8, "libata")) + printk(KERN_WARNING "ata: 0x1f0 IDE port busy\n"); + else + legacy_mode |= (1 << 0); + + if (!request_region(0x170, 8, "libata")) + printk(KERN_WARNING "ata: 0x170 IDE port busy\n"); + else + legacy_mode |= (1 << 1); + } + + /* we have legacy mode, but all ports are unavailable */ + if (legacy_mode == (1 << 3)) { + rc = -EBUSY; + goto err_out_regions; + } + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + if (legacy_mode) { + probe_ent2 = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent2) { + rc = -ENOMEM; + goto err_out_free_ent; + } + + memset(probe_ent2, 0, sizeof(*probe_ent)); + probe_ent2->pdev = pdev; + INIT_LIST_HEAD(&probe_ent2->node); + } + + probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4); + probe_ent->sht = port0->sht; + probe_ent->host_flags = port0->host_flags; + probe_ent->pio_mask = port0->pio_mask; + probe_ent->udma_mask = port0->udma_mask; + probe_ent->port_ops = port0->port_ops; + + if (legacy_mode) { + probe_ent->port[0].cmd_addr = 0x1f0; + probe_ent->port[0].altstatus_addr = + probe_ent->port[0].ctl_addr = 0x3f6; + probe_ent->n_ports = 1; + probe_ent->irq = 14; + ata_std_ports(&probe_ent->port[0]); + + probe_ent2->port[0].cmd_addr = 0x170; + probe_ent2->port[0].altstatus_addr = + probe_ent2->port[0].ctl_addr = 0x376; + probe_ent2->port[0].bmdma_addr = pci_resource_start(pdev, 4)+8; + probe_ent2->n_ports = 1; + probe_ent2->irq = 15; + ata_std_ports(&probe_ent2->port[0]); + + probe_ent2->sht = port1->sht; + probe_ent2->host_flags = port1->host_flags; + probe_ent2->pio_mask = port1->pio_mask; + probe_ent2->udma_mask = port1->udma_mask; + probe_ent2->port_ops = port1->port_ops; + } else { + probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0); + ata_std_ports(&probe_ent->port[0]); + probe_ent->port[0].altstatus_addr = + probe_ent->port[0].ctl_addr = + pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; + + probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2); + ata_std_ports(&probe_ent->port[1]); + probe_ent->port[1].altstatus_addr = + probe_ent->port[1].ctl_addr = + pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; + probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8; + + probe_ent->n_ports = 2; + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + } + + pci_set_master(pdev); + + spin_lock(&ata_module_lock); + if (legacy_mode) { + if (legacy_mode & (1 << 0)) + list_add_tail(&probe_ent->node, &ata_probe_list); + else + kfree(probe_ent); + if (legacy_mode & (1 << 1)) + list_add_tail(&probe_ent2->node, &ata_probe_list); + else + kfree(probe_ent2); + } else { + list_add_tail(&probe_ent->node, &ata_probe_list); + } + spin_unlock(&ata_module_lock); + + return 0; + +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + if (legacy_mode & (1 << 0)) + release_region(0x1f0, 8); + if (legacy_mode & (1 << 1)) + release_region(0x170, 8); + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + +/** + * ata_pci_remove_one - PCI layer callback for device removal + * @pdev: PCI device that was removed + * + * PCI layer indicates to libata via this hook that + * hot-unplug or module unload event has occured. + * Handle this by unregistering all objects associated + * with this PCI device. Free those objects. Then finally + * release PCI resources and disable device. + * + * LOCKING: + * Inherited from PCI layer (may sleep). + */ + +void ata_pci_remove_one (struct pci_dev *pdev) +{ + struct ata_host_set *host_set = pci_get_drvdata(pdev); + struct ata_port *ap; + unsigned int i; + Scsi_Host_Template *sht; + int rc; + + /* FIXME: this unregisters all ports attached to the + * Scsi_Host_Template given. We _might_ have multiple + * templates (though we don't ATM), so this is ok... for now. + */ + ap = host_set->ports[0]; + sht = ap->host->hostt; + rc = scsi_unregister_module(MODULE_SCSI_HA, sht); + /* FIXME: handle 'rc' failure? */ + + free_irq(host_set->irq, host_set); + if (host_set->mmio_base) + iounmap(host_set->mmio_base); + if (host_set->ports[0]->ops->host_stop) + host_set->ports[0]->ops->host_stop(host_set); + + pci_release_regions(pdev); + + for (i = 0; i < host_set->n_ports; i++) { + struct ata_ioports *ioaddr; + + ap = host_set->ports[i]; + ioaddr = &ap->ioaddr; + + if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { + if (ioaddr->cmd_addr == 0x1f0) + release_region(0x1f0, 8); + else if (ioaddr->cmd_addr == 0x170) + release_region(0x170, 8); + } + } + + kfree(host_set); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +} + +/** + * ata_add_to_probe_list - add an entry to the list of things + * to be probed. + * @probe_ent: describes device to be probed. + * + * LOCKING: + */ + +void ata_add_to_probe_list(struct ata_probe_ent *probe_ent) +{ + spin_lock(&ata_module_lock); + list_add_tail(&probe_ent->node, &ata_probe_list); + spin_unlock(&ata_module_lock); +} + +/* move to PCI subsystem */ +int pci_test_config_bits(struct pci_dev *pdev, struct pci_bits *bits) +{ + unsigned long tmp = 0; + + switch (bits->width) { + case 1: { + u8 tmp8 = 0; + pci_read_config_byte(pdev, bits->reg, &tmp8); + tmp = tmp8; + break; + } + case 2: { + u16 tmp16 = 0; + pci_read_config_word(pdev, bits->reg, &tmp16); + tmp = tmp16; + break; + } + case 4: { + u32 tmp32 = 0; + pci_read_config_dword(pdev, bits->reg, &tmp32); + tmp = tmp32; + break; + } + + default: + return -EINVAL; + } + + tmp &= bits->mask; + + return (tmp == bits->val) ? 1 : 0; +} + + +/** + * ata_init - + * + * LOCKING: + * + * RETURNS: + * + */ + +static int __init ata_init(void) +{ + printk(KERN_DEBUG "libata version " DRV_VERSION " loaded.\n"); + return 0; +} + +static void __exit ata_exit(void) +{ +} + +module_init(ata_init); +module_exit(ata_exit); + +/* + * libata is essentially a library of internal helper functions for + * low-level ATA host controller drivers. As such, the API/ABI is + * likely to change as new drivers are added and updated. + * Do not depend on ABI/API stability. + */ + +EXPORT_SYMBOL_GPL(pci_test_config_bits); +EXPORT_SYMBOL_GPL(ata_std_bios_param); +EXPORT_SYMBOL_GPL(ata_std_ports); +EXPORT_SYMBOL_GPL(ata_device_add); +EXPORT_SYMBOL_GPL(ata_qc_complete); +EXPORT_SYMBOL_GPL(ata_eng_timeout); +EXPORT_SYMBOL_GPL(ata_tf_load_pio); +EXPORT_SYMBOL_GPL(ata_tf_load_mmio); +EXPORT_SYMBOL_GPL(ata_tf_read_pio); +EXPORT_SYMBOL_GPL(ata_tf_read_mmio); +EXPORT_SYMBOL_GPL(ata_tf_to_fis); +EXPORT_SYMBOL_GPL(ata_tf_from_fis); +EXPORT_SYMBOL_GPL(ata_check_status_pio); +EXPORT_SYMBOL_GPL(ata_check_status_mmio); +EXPORT_SYMBOL_GPL(ata_exec_command_pio); +EXPORT_SYMBOL_GPL(ata_exec_command_mmio); +EXPORT_SYMBOL_GPL(ata_port_start); +EXPORT_SYMBOL_GPL(ata_port_stop); +EXPORT_SYMBOL_GPL(ata_interrupt); +EXPORT_SYMBOL_GPL(ata_fill_sg); +EXPORT_SYMBOL_GPL(ata_bmdma_setup_pio); +EXPORT_SYMBOL_GPL(ata_bmdma_start_pio); +EXPORT_SYMBOL_GPL(ata_bmdma_setup_mmio); +EXPORT_SYMBOL_GPL(ata_bmdma_start_mmio); +EXPORT_SYMBOL_GPL(ata_port_probe); +EXPORT_SYMBOL_GPL(sata_phy_reset); +EXPORT_SYMBOL_GPL(ata_bus_reset); +EXPORT_SYMBOL_GPL(ata_port_disable); +EXPORT_SYMBOL_GPL(ata_pci_init_one); +EXPORT_SYMBOL_GPL(ata_pci_remove_one); +EXPORT_SYMBOL_GPL(ata_scsi_queuecmd); +EXPORT_SYMBOL_GPL(ata_scsi_error); +EXPORT_SYMBOL_GPL(ata_scsi_detect); +EXPORT_SYMBOL_GPL(ata_add_to_probe_list); +EXPORT_SYMBOL_GPL(ata_scsi_release); +EXPORT_SYMBOL_GPL(ata_host_intr); +EXPORT_SYMBOL_GPL(ata_dev_id_string); diff -urN linux-2.4.26/drivers/scsi/libata-scsi.c linux-2.4.27/drivers/scsi/libata-scsi.c --- linux-2.4.26/drivers/scsi/libata-scsi.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/libata-scsi.c 2004-08-07 16:26:05.511381992 -0700 @@ -0,0 +1,1186 @@ +/* + libata-scsi.c - helper library for ATA + + Copyright 2003-2004 Red Hat, Inc. All rights reserved. + Copyright 2003-2004 Jeff Garzik + + The contents of this file are subject to the Open + Software License version 1.1 that can be found at + http://www.opensource.org/licenses/osl-1.1.txt and is included herein + by reference. + + Alternatively, the contents of this file may be used under the terms + of the GNU General Public License version 2 (the "GPL") as distributed + in the kernel source COPYING file, in which case the provisions of + the GPL are applicable instead of the above. If you wish to allow + the use of your version of this file only under the terms of the + GPL and not to allow others to use your version of this file under + the OSL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the GPL. + If you do not delete the provisions above, a recipient may use your + version of this file under either the OSL or the GPL. + + */ + +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include "sd.h" +#include + +#include "libata.h" + +typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, u8 *scsicmd); +static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)); + + +/** + * ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd. + * @disk: SCSI device for which BIOS geometry is to be determined + * @dev: device major/minor + * @ip: location to which geometry will be output + * + * Generic bios head/sector/cylinder calculator + * used by sd. Most BIOSes nowadays expect a XXX/255/16 (CHS) + * mapping. Some situations may arise where the disk is not + * bootable if this is not used. + * + * LOCKING: + * Defined by the SCSI layer. We don't really care. + * + * RETURNS: + * Zero. + */ +int ata_std_bios_param(Disk * disk, /* SCSI disk */ + kdev_t dev, /* Device major, minor */ + int *ip /* Heads, sectors, cylinders in that order */ ) +{ + ip[0] = 255; + ip[1] = 63; + ip[2] = disk->capacity / (ip[0] * ip[1]); + + return 0; +} + + +/** + * ata_scsi_qc_new - acquire new ata_queued_cmd reference + * @ap: ATA port to which the new command is attached + * @dev: ATA device to which the new command is attached + * @cmd: SCSI command that originated this ATA command + * @done: SCSI command completion function + * + * Obtain a reference to an unused ata_queued_cmd structure, + * which is the basic libata structure representing a single + * ATA command sent to the hardware. + * + * If a command was available, fill in the SCSI-specific + * portions of the structure with information on the + * current command. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Command allocated, or %NULL if none available. + */ +struct ata_queued_cmd *ata_scsi_qc_new(struct ata_port *ap, + struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct ata_queued_cmd *qc; + + qc = ata_qc_new_init(ap, dev); + if (qc) { + qc->scsicmd = cmd; + qc->scsidone = done; + + if (cmd->use_sg) { + qc->sg = (struct scatterlist *) cmd->request_buffer; + qc->n_elem = cmd->use_sg; + } else { + qc->sg = &qc->sgent; + qc->n_elem = 1; + } + } else { + cmd->result = (DID_OK << 16) | (QUEUE_FULL << 1); + done(cmd); + } + + return qc; +} + +/** + * ata_to_sense_error - convert ATA error to SCSI error + * @qc: Command that we are erroring out + * + * Converts an ATA error into a SCSI error. + * + * Right now, this routine is laughably primitive. We + * don't even examine what ATA told us, we just look at + * the command data direction, and return a fatal SCSI + * sense error based on that. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_to_sense_error(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + + cmd->result = SAM_STAT_CHECK_CONDITION; + + cmd->sense_buffer[0] = 0x70; + cmd->sense_buffer[2] = MEDIUM_ERROR; + cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */ + + /* additional-sense-code[-qualifier] */ + if (cmd->sc_data_direction == SCSI_DATA_READ) { + cmd->sense_buffer[12] = 0x11; /* "unrecovered read error" */ + cmd->sense_buffer[13] = 0x04; + } else { + cmd->sense_buffer[12] = 0x0C; /* "write error - */ + cmd->sense_buffer[13] = 0x02; /* auto-reallocation failed" */ + } +} + +/** + * ata_scsi_error - SCSI layer error handler callback + * @host: SCSI host on which error occurred + * + * Handles SCSI-layer-thrown error events. + * + * LOCKING: + * Inherited from SCSI layer (none, can sleep) + * + * RETURNS: + * Zero. + */ + +int ata_scsi_error(struct Scsi_Host *host) +{ + struct ata_port *ap; + + DPRINTK("ENTER\n"); + + ap = (struct ata_port *) &host->hostdata[0]; + ap->ops->eng_timeout(ap); + + DPRINTK("EXIT\n"); + return 0; +} + +/** + * ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one + * @qc: Storage for translated ATA taskfile + * @scsicmd: SCSI command to translate + * + * Converts any of six SCSI read/write commands into the + * ATA counterpart, including starting sector (LBA), + * sector count, and taking into account the device's LBA48 + * support. + * + * Commands %READ_6, %READ_10, %READ_16, %WRITE_6, %WRITE_10, and + * %WRITE_16 are currently supported. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on error. + */ + +static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) +{ + struct ata_taskfile *tf = &qc->tf; + unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; + + tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + tf->hob_nsect = 0; + tf->hob_lbal = 0; + tf->hob_lbam = 0; + tf->hob_lbah = 0; + tf->protocol = qc->dev->xfer_protocol; + tf->device |= ATA_LBA; + + if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 || + scsicmd[0] == READ_16) { + tf->command = qc->dev->read_cmd; + } else { + tf->command = qc->dev->write_cmd; + tf->flags |= ATA_TFLAG_WRITE; + } + + if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) { + if (lba48) { + tf->hob_nsect = scsicmd[7]; + tf->hob_lbal = scsicmd[2]; + + qc->nsect = ((unsigned int)scsicmd[7] << 8) | + scsicmd[8]; + } else { + /* if we don't support LBA48 addressing, the request + * -may- be too large. */ + if ((scsicmd[2] & 0xf0) || scsicmd[7]) + return 1; + + /* stores LBA27:24 in lower 4 bits of device reg */ + tf->device |= scsicmd[2]; + + qc->nsect = scsicmd[8]; + } + + tf->nsect = scsicmd[8]; + tf->lbal = scsicmd[5]; + tf->lbam = scsicmd[4]; + tf->lbah = scsicmd[3]; + + VPRINTK("ten-byte command\n"); + return 0; + } + + if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) { + qc->nsect = tf->nsect = scsicmd[4]; + tf->lbal = scsicmd[3]; + tf->lbam = scsicmd[2]; + tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */ + + VPRINTK("six-byte command\n"); + return 0; + } + + if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) { + /* rule out impossible LBAs and sector counts */ + if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11]) + return 1; + + if (lba48) { + tf->hob_nsect = scsicmd[12]; + tf->hob_lbal = scsicmd[6]; + tf->hob_lbam = scsicmd[5]; + tf->hob_lbah = scsicmd[4]; + + qc->nsect = ((unsigned int)scsicmd[12] << 8) | + scsicmd[13]; + } else { + /* once again, filter out impossible non-zero values */ + if (scsicmd[4] || scsicmd[5] || scsicmd[12] || + (scsicmd[6] & 0xf0)) + return 1; + + /* stores LBA27:24 in lower 4 bits of device reg */ + tf->device |= scsicmd[2]; + + qc->nsect = scsicmd[13]; + } + + tf->nsect = scsicmd[13]; + tf->lbal = scsicmd[9]; + tf->lbam = scsicmd[8]; + tf->lbah = scsicmd[7]; + + VPRINTK("sixteen-byte command\n"); + return 0; + } + + DPRINTK("no-byte command\n"); + return 1; +} + +/** + * ata_scsi_translate - Translate then issue SCSI command to ATA device + * @ap: ATA port to which the command is addressed + * @dev: ATA device to which the command is addressed + * @cmd: SCSI command to execute + * @done: SCSI command completion function + * @xlat_func: Actor which translates @cmd to an ATA taskfile + * + * Our ->queuecommand() function has decided that the SCSI + * command issued can be directly translated into an ATA + * command, rather than handled internally. + * + * This function sets up an ata_queued_cmd structure for the + * SCSI command, and sends that ata_queued_cmd to the hardware. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *), + ata_xlat_func_t xlat_func) +{ + struct ata_queued_cmd *qc; + u8 *scsicmd = cmd->cmnd; + + VPRINTK("ENTER\n"); + + qc = ata_scsi_qc_new(ap, dev, cmd, done); + if (!qc) + return; + + if (cmd->sc_data_direction == SCSI_DATA_READ || + cmd->sc_data_direction == SCSI_DATA_WRITE) { + if (unlikely(cmd->request_bufflen < 1)) { + printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n", + ap->id, dev->devno); + goto err_out; + } + + qc->flags |= ATA_QCFLAG_SG; /* data is present; dma-map it */ + } + + if (xlat_func(qc, scsicmd)) + goto err_out; + + /* select device, send command to hardware */ + if (ata_qc_issue(qc)) + goto err_out; + + VPRINTK("EXIT\n"); + return; + +err_out: + ata_bad_cdb(cmd, done); + DPRINTK("EXIT - badcmd\n"); +} + +/** + * ata_scsi_rbuf_get - Map response buffer. + * @cmd: SCSI command containing buffer to be mapped. + * @buf_out: Pointer to mapped area. + * + * Maps buffer contained within SCSI command @cmd. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Length of response buffer. + */ + +static unsigned int ata_scsi_rbuf_get(struct scsi_cmnd *cmd, u8 **buf_out) +{ + u8 *buf; + unsigned int buflen; + + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + buf = kmap_atomic(sg->page, KM_USER0) + sg->offset; + buflen = sg->length; + } else { + buf = cmd->request_buffer; + buflen = cmd->request_bufflen; + } + + memset(buf, 0, buflen); + *buf_out = buf; + return buflen; +} + +/** + * ata_scsi_rbuf_put - Unmap response buffer. + * @cmd: SCSI command containing buffer to be unmapped. + * + * Unmaps response buffer contained within @cmd. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd) +{ + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + kunmap_atomic(sg->page, KM_USER0); + } +} + +/** + * ata_scsi_rbuf_fill - wrapper for SCSI command simulators + * @args: Port / device / SCSI command of interest. + * @actor: Callback hook for desired SCSI command simulator + * + * Takes care of the hard work of simulating a SCSI command... + * Mapping the response buffer, calling the command's handler, + * and handling the handler's return value. This return value + * indicates whether the handler wishes the SCSI command to be + * completed successfully, or not. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_scsi_rbuf_fill(struct ata_scsi_args *args, + unsigned int (*actor) (struct ata_scsi_args *args, + u8 *rbuf, unsigned int buflen)) +{ + u8 *rbuf; + unsigned int buflen, rc; + struct scsi_cmnd *cmd = args->cmd; + + buflen = ata_scsi_rbuf_get(cmd, &rbuf); + rc = actor(args, rbuf, buflen); + ata_scsi_rbuf_put(cmd); + + if (rc) + ata_bad_cdb(cmd, args->done); + else { + cmd->result = SAM_STAT_GOOD; + args->done(cmd); + } +} + +/** + * ata_scsiop_inq_std - Simulate INQUIRY command + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Returns standard device identification data associated + * with non-EVPD INQUIRY command output. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + struct ata_device *dev = args->dev; + + u8 hdr[] = { + TYPE_DISK, + 0, + 0x5, /* claim SPC-3 version compatibility */ + 2, + 96 - 4 + }; + + /* set scsi removeable (RMB) bit per ata bit */ + if (ata_id_removeable(dev)) + hdr[1] |= (1 << 7); + + VPRINTK("ENTER\n"); + + memcpy(rbuf, hdr, sizeof(hdr)); + + if (buflen > 36) { + memcpy(&rbuf[8], "ATA ", 8); + ata_dev_id_string(dev, &rbuf[16], ATA_ID_PROD_OFS, 16); + ata_dev_id_string(dev, &rbuf[32], ATA_ID_FW_REV_OFS, 4); + if (rbuf[32] == 0 || rbuf[32] == ' ') + memcpy(&rbuf[32], "n/a ", 4); + } + + if (buflen > 63) { + const u8 versions[] = { + 0x60, /* SAM-3 (no version claimed) */ + + 0x03, + 0x20, /* SBC-2 (no version claimed) */ + + 0x02, + 0x60 /* SPC-3 (no version claimed) */ + }; + + memcpy(rbuf + 59, versions, sizeof(versions)); + } + + return 0; +} + +/** + * ata_scsiop_inq_00 - Simulate INQUIRY EVPD page 0, list of pages + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Returns list of inquiry EVPD pages available. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + const u8 pages[] = { + 0x00, /* page 0x00, this page */ + 0x80, /* page 0x80, unit serial no page */ + 0x83 /* page 0x83, device ident page */ + }; + rbuf[3] = sizeof(pages); /* number of supported EVPD pages */ + + if (buflen > 6) + memcpy(rbuf + 4, pages, sizeof(pages)); + + return 0; +} + +/** + * ata_scsiop_inq_80 - Simulate INQUIRY EVPD page 80, device serial number + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Returns ATA device serial number. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + const u8 hdr[] = { + 0, + 0x80, /* this page code */ + 0, + ATA_SERNO_LEN, /* page len */ + }; + memcpy(rbuf, hdr, sizeof(hdr)); + + if (buflen > (ATA_SERNO_LEN + 4)) + ata_dev_id_string(args->dev, (unsigned char *) &rbuf[4], + ATA_ID_SERNO_OFS, ATA_SERNO_LEN); + + return 0; +} + +static const char *inq_83_str = "Linux ATA-SCSI simulator"; + +/** + * ata_scsiop_inq_83 - Simulate INQUIRY EVPD page 83, device identity + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Returns device identification. Currently hardcoded to + * return "Linux ATA-SCSI simulator". + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + rbuf[1] = 0x83; /* this page code */ + rbuf[3] = 4 + strlen(inq_83_str); /* page len */ + + /* our one and only identification descriptor (vendor-specific) */ + if (buflen > (strlen(inq_83_str) + 4 + 4)) { + rbuf[4 + 0] = 2; /* code set: ASCII */ + rbuf[4 + 3] = strlen(inq_83_str); + memcpy(rbuf + 4 + 4, inq_83_str, strlen(inq_83_str)); + } + + return 0; +} + +/** + * ata_scsiop_noop - + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * No operation. Simply returns success to caller, to indicate + * that the caller should successfully complete this SCSI command. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + VPRINTK("ENTER\n"); + return 0; +} + +/** + * ata_msense_push - Push data onto MODE SENSE data output buffer + * @ptr_io: (input/output) Location to store more output data + * @last: End of output data buffer + * @buf: Pointer to BLOB being added to output buffer + * @buflen: Length of BLOB + * + * Store MODE SENSE data on an output buffer. + * + * LOCKING: + * None. + */ + +static void ata_msense_push(u8 **ptr_io, const u8 *last, + const u8 *buf, unsigned int buflen) +{ + u8 *ptr = *ptr_io; + + if ((ptr + buflen - 1) > last) + return; + + memcpy(ptr, buf, buflen); + + ptr += buflen; + + *ptr_io = ptr; +} + +/** + * ata_msense_caching - Simulate MODE SENSE caching info page + * @dev: Device associated with this MODE SENSE command + * @ptr_io: (input/output) Location to store more output data + * @last: End of output data buffer + * + * Generate a caching info page, which conditionally indicates + * write caching to the SCSI layer, depending on device + * capabilities. + * + * LOCKING: + * None. + */ + +static unsigned int ata_msense_caching(struct ata_device *dev, u8 **ptr_io, + const u8 *last) +{ + u8 page[] = { + 0x8, /* page code */ + 0x12, /* page length */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 zeroes */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 8 zeroes */ + }; + + if (ata_id_wcache_enabled(dev)) + page[2] |= (1 << 2); /* write cache enable */ + if (!ata_id_rahead_enabled(dev)) + page[12] |= (1 << 5); /* disable read ahead */ + + ata_msense_push(ptr_io, last, page, sizeof(page)); + return sizeof(page); +} + +/** + * ata_msense_ctl_mode - Simulate MODE SENSE control mode page + * @dev: Device associated with this MODE SENSE command + * @ptr_io: (input/output) Location to store more output data + * @last: End of output data buffer + * + * Generate a generic MODE SENSE control mode page. + * + * LOCKING: + * None. + */ + +static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last) +{ + const u8 page[] = {0xa, 0xa, 2, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30}; + + ata_msense_push(ptr_io, last, page, sizeof(page)); + return sizeof(page); +} + +/** + * ata_msense_rw_recovery - Simulate MODE SENSE r/w error recovery page + * @dev: Device associated with this MODE SENSE command + * @ptr_io: (input/output) Location to store more output data + * @last: End of output data buffer + * + * Generate a generic MODE SENSE r/w error recovery page. + * + * LOCKING: + * None. + */ + +static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last) +{ + const u8 page[] = { + 0x1, /* page code */ + 0xa, /* page length */ + (1 << 7) | (1 << 6), /* note auto r/w reallocation */ + 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */ + }; + + ata_msense_push(ptr_io, last, page, sizeof(page)); + return sizeof(page); +} + +/** + * ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Simulate MODE SENSE commands. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + u8 *scsicmd = args->cmd->cmnd, *p, *last; + struct ata_device *dev = args->dev; + unsigned int page_control, six_byte, output_len; + + VPRINTK("ENTER\n"); + + six_byte = (scsicmd[0] == MODE_SENSE); + + /* we only support saved and current values (which we treat + * in the same manner) + */ + page_control = scsicmd[2] >> 6; + if ((page_control != 0) && (page_control != 3)) + return 1; + + if (six_byte) + output_len = 4; + else + output_len = 8; + + p = rbuf + output_len; + last = rbuf + buflen - 1; + + switch(scsicmd[2] & 0x3f) { + case 0x01: /* r/w error recovery */ + output_len += ata_msense_rw_recovery(&p, last); + break; + + case 0x08: /* caching */ + output_len += ata_msense_caching(dev, &p, last); + break; + + case 0x0a: { /* control mode */ + output_len += ata_msense_ctl_mode(&p, last); + break; + } + + case 0x3f: /* all pages */ + output_len += ata_msense_rw_recovery(&p, last); + output_len += ata_msense_caching(dev, &p, last); + output_len += ata_msense_ctl_mode(&p, last); + break; + + default: /* invalid page code */ + return 1; + } + + if (six_byte) { + output_len--; + rbuf[0] = output_len; + } else { + output_len -= 2; + rbuf[0] = output_len >> 8; + rbuf[1] = output_len; + } + + return 0; +} + +/** + * ata_scsiop_read_cap - Simulate READ CAPACITY[ 16] commands + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Simulate READ CAPACITY commands. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + u64 n_sectors = args->dev->n_sectors; + u32 tmp; + + VPRINTK("ENTER\n"); + + n_sectors--; /* ATA TotalUserSectors - 1 */ + + tmp = n_sectors; /* note: truncates, if lba48 */ + if (args->cmd->cmnd[0] == READ_CAPACITY) { + /* sector count, 32-bit */ + rbuf[0] = tmp >> (8 * 3); + rbuf[1] = tmp >> (8 * 2); + rbuf[2] = tmp >> (8 * 1); + rbuf[3] = tmp; + + /* sector size */ + tmp = ATA_SECT_SIZE; + rbuf[6] = tmp >> 8; + rbuf[7] = tmp; + + } else { + /* sector count, 64-bit */ + rbuf[2] = n_sectors >> (8 * 7); + rbuf[3] = n_sectors >> (8 * 6); + rbuf[4] = n_sectors >> (8 * 5); + rbuf[5] = n_sectors >> (8 * 4); + rbuf[6] = tmp >> (8 * 3); + rbuf[7] = tmp >> (8 * 2); + rbuf[8] = tmp >> (8 * 1); + rbuf[9] = tmp; + + /* sector size */ + tmp = ATA_SECT_SIZE; + rbuf[12] = tmp >> 8; + rbuf[13] = tmp; + } + + return 0; +} + +/** + * ata_scsiop_report_luns - Simulate REPORT LUNS command + * @args: Port / device / SCSI command of interest. + * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. + * @buflen: Response buffer length. + * + * Simulate REPORT LUNS command. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen) +{ + VPRINTK("ENTER\n"); + rbuf[3] = 8; /* just one lun, LUN 0, size 8 bytes */ + + return 0; +} + +/** + * ata_scsi_badcmd - End a SCSI request with an error + * @cmd: SCSI request to be handled + * @done: SCSI command completion function + * @asc: SCSI-defined additional sense code + * @ascq: SCSI-defined additional sense code qualifier + * + * Helper function that completes a SCSI command with + * %SAM_STAT_CHECK_CONDITION, with a sense key %ILLEGAL_REQUEST + * and the specified additional sense codes. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq) +{ + DPRINTK("ENTER\n"); + cmd->result = SAM_STAT_CHECK_CONDITION; + + cmd->sense_buffer[0] = 0x70; + cmd->sense_buffer[2] = ILLEGAL_REQUEST; + cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */ + cmd->sense_buffer[12] = asc; + cmd->sense_buffer[13] = ascq; + + done(cmd); +} + +/** + * atapi_xlat - Initialize PACKET taskfile + * @qc: command structure to be initialized + * @scsicmd: SCSI CDB associated with this PACKET command + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on failure. + */ + +static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + + qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + if (cmd->sc_data_direction == SCSI_DATA_WRITE) { + qc->tf.flags |= ATA_TFLAG_WRITE; + DPRINTK("direction: write\n"); + } + + qc->tf.command = ATA_CMD_PACKET; + + /* no data - interrupt-driven */ + if (cmd->sc_data_direction == SCSI_DATA_NONE) + qc->tf.protocol = ATA_PROT_ATAPI; + + /* PIO data xfer - polling */ + else if ((qc->flags & ATA_QCFLAG_DMA) == 0) { + ata_qc_set_polling(qc); + qc->tf.protocol = ATA_PROT_ATAPI; + qc->tf.lbam = (8 * 1024) & 0xff; + qc->tf.lbah = (8 * 1024) >> 8; + + /* DMA data xfer - interrupt-driven */ + } else { + qc->tf.protocol = ATA_PROT_ATAPI_DMA; + qc->tf.feature |= ATAPI_PKT_DMA; + +#ifdef ATAPI_ENABLE_DMADIR + /* some SATA bridges need us to indicate data xfer direction */ + if (cmd->sc_data_direction != SCSI_DATA_WRITE) + qc->tf.feature |= ATAPI_DMADIR; +#endif + } + + return 0; +} + +/** + * ata_scsi_find_dev - lookup ata_device from scsi_cmnd + * @ap: ATA port to which the device is attached + * @cmd: SCSI command to be sent to the device + * + * Given various information provided in struct scsi_cmnd, + * map that onto an ATA bus, and using that mapping + * determine which ata_device is associated with the + * SCSI command to be sent. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Associated ATA device, or %NULL if not found. + */ + +static inline struct ata_device * +ata_scsi_find_dev(struct ata_port *ap, struct scsi_cmnd *cmd) +{ + struct ata_device *dev; + + /* skip commands not addressed to targets we simulate */ + if (likely(cmd->target < ATA_MAX_DEVICES)) + dev = &ap->device[cmd->target]; + else + return NULL; + + if (unlikely((cmd->channel != 0) || + (cmd->lun != 0))) + return NULL; + + if (unlikely(!ata_dev_present(dev))) + return NULL; + +#ifndef ATA_ENABLE_ATAPI + if (unlikely(dev->class == ATA_DEV_ATAPI)) + return NULL; +#endif + + return dev; +} + +/** + * ata_get_xlat_func - check if SCSI to ATA translation is possible + * @cmd: SCSI command opcode to consider + * + * Look up the SCSI command given, and determine whether the + * SCSI command is to be translated or simulated. + * + * RETURNS: + * Pointer to translation function if possible, %NULL if not. + */ + +static inline ata_xlat_func_t ata_get_xlat_func(u8 cmd) +{ + switch (cmd) { + case READ_6: + case READ_10: + case READ_16: + + case WRITE_6: + case WRITE_10: + case WRITE_16: + return ata_scsi_rw_xlat; + } + + return NULL; +} + +/** + * ata_scsi_dump_cdb - dump SCSI command contents to dmesg + * @ap: ATA port to which the command was being sent + * @cmd: SCSI command to dump + * + * Prints the contents of a SCSI command via printk(). + */ + +static inline void ata_scsi_dump_cdb(struct ata_port *ap, + struct scsi_cmnd *cmd) +{ +#ifdef ATA_DEBUG + u8 *scsicmd = cmd->cmnd; + + DPRINTK("CDB (%u:%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + ap->id, + cmd->channel, cmd->target, cmd->lun, + scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3], + scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7], + scsicmd[8]); +#endif +} + +/** + * ata_scsi_queuecmd - Issue SCSI cdb to libata-managed device + * @cmd: SCSI command to be sent + * @done: Completion function, called when command is complete + * + * In some cases, this function translates SCSI commands into + * ATA taskfiles, and queues the taskfiles to be sent to + * hardware. In other cases, this function simulates a + * SCSI device by evaluating and responding to certain + * SCSI commands. This creates the overall effect of + * ATA and ATAPI devices appearing as SCSI devices. + * + * LOCKING: + * Releases scsi-layer-held lock, and obtains host_set lock. + * + * RETURNS: + * Zero. + */ + +int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + struct ata_port *ap; + struct ata_device *dev; + + /* Note: spin_lock_irqsave is held by caller... */ + spin_unlock(&io_request_lock); + + ap = (struct ata_port *) &cmd->host->hostdata[0]; + + spin_lock(&ap->host_set->lock); + + ata_scsi_dump_cdb(ap, cmd); + + dev = ata_scsi_find_dev(ap, cmd); + if (unlikely(!dev)) { + cmd->result = (DID_BAD_TARGET << 16); + done(cmd); + goto out_unlock; + } + + if (dev->class == ATA_DEV_ATA) { + ata_xlat_func_t xlat_func = ata_get_xlat_func(cmd->cmnd[0]); + + if (xlat_func) + ata_scsi_translate(ap, dev, cmd, done, xlat_func); + else + ata_scsi_simulate(ap, dev, cmd, done); + } else + ata_scsi_translate(ap, dev, cmd, done, atapi_xlat); + +out_unlock: + spin_unlock(&ap->host_set->lock); + spin_lock(&io_request_lock); + return 0; +} + +/** + * ata_scsi_simulate - simulate SCSI command on ATA device + * @ap: Port to which ATA device is attached. + * @dev: Target device for CDB. + * @cmd: SCSI command being sent to device. + * @done: SCSI command completion function. + * + * Interprets and directly executes a select list of SCSI commands + * that can be handled internally. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ + +static void ata_scsi_simulate(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct ata_scsi_args args; + u8 *scsicmd = cmd->cmnd; + + args.ap = ap; + args.dev = dev; + args.cmd = cmd; + args.done = done; + + switch(scsicmd[0]) { + /* no-op's, complete with success */ + case SYNCHRONIZE_CACHE: /* FIXME: temporary */ + case REZERO_UNIT: + case SEEK_6: + case SEEK_10: + case TEST_UNIT_READY: + case FORMAT_UNIT: /* FIXME: correct? */ + case SEND_DIAGNOSTIC: /* FIXME: correct? */ + ata_scsi_rbuf_fill(&args, ata_scsiop_noop); + break; + + case INQUIRY: + if (scsicmd[1] & 2) /* is CmdDt set? */ + ata_bad_cdb(cmd, done); + else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); + else if (scsicmd[2] == 0x00) + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_00); + else if (scsicmd[2] == 0x80) + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_80); + else if (scsicmd[2] == 0x83) + ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83); + else + ata_bad_cdb(cmd, done); + break; + + case MODE_SENSE: + case MODE_SENSE_10: + ata_scsi_rbuf_fill(&args, ata_scsiop_mode_sense); + break; + + case MODE_SELECT: /* unconditionally return */ + case MODE_SELECT_10: /* bad-field-in-cdb */ + ata_bad_cdb(cmd, done); + break; + + case READ_CAPACITY: + ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); + break; + + case SERVICE_ACTION_IN: + if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) + ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); + else + ata_bad_cdb(cmd, done); + break; + + case REPORT_LUNS: + ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns); + break; + + /* mandantory commands we haven't implemented yet */ + case REQUEST_SENSE: + + /* all other commands */ + default: + ata_bad_scsiop(cmd, done); + break; + } +} + diff -urN linux-2.4.26/drivers/scsi/libata.h linux-2.4.27/drivers/scsi/libata.h --- linux-2.4.26/drivers/scsi/libata.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/libata.h 2004-08-07 16:26:05.512382033 -0700 @@ -0,0 +1,88 @@ +/* + libata.h - helper library for ATA + + Copyright 2003-2004 Red Hat, Inc. All rights reserved. + Copyright 2003-2004 Jeff Garzik + + The contents of this file are subject to the Open + Software License version 1.1 that can be found at + http://www.opensource.org/licenses/osl-1.1.txt and is included herein + by reference. + + Alternatively, the contents of this file may be used under the terms + of the GNU General Public License version 2 (the "GPL") as distributed + in the kernel source COPYING file, in which case the provisions of + the GPL are applicable instead of the above. If you wish to allow + the use of your version of this file only under the terms of the + GPL and not to allow others to use your version of this file under + the OSL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the GPL. + If you do not delete the provisions above, a recipient may use your + version of this file under either the OSL or the GPL. + + */ + +#ifndef __LIBATA_H__ +#define __LIBATA_H__ + +#define DRV_NAME "libata" +#define DRV_VERSION "1.02" /* must be exactly four chars */ + +struct ata_scsi_args { + struct ata_port *ap; + struct ata_device *dev; + struct scsi_cmnd *cmd; + void (*done)(struct scsi_cmnd *); +}; + + +/* libata-core.c */ +extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, + struct ata_device *dev); +extern int ata_qc_issue(struct ata_queued_cmd *qc); +extern void ata_dev_select(struct ata_port *ap, unsigned int device, + unsigned int wait, unsigned int can_sleep); +extern void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf); + + +/* libata-scsi.c */ +extern void ata_to_sense_error(struct ata_queued_cmd *qc); +extern int ata_scsi_error(struct Scsi_Host *host); +extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); + +extern unsigned int ata_scsiop_inq_00(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); + +extern unsigned int ata_scsiop_inq_80(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern unsigned int ata_scsiop_inq_83(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern unsigned int ata_scsiop_noop(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern unsigned int ata_scsiop_sync_cache(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, + unsigned int buflen); +extern void ata_scsi_badcmd(struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *), + u8 asc, u8 ascq); +extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, + unsigned int (*actor) (struct ata_scsi_args *args, + u8 *rbuf, unsigned int buflen)); + +static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + ata_scsi_badcmd(cmd, done, 0x20, 0x00); +} + +static inline void ata_bad_cdb(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + ata_scsi_badcmd(cmd, done, 0x24, 0x00); +} + +#endif /* __LIBATA_H__ */ diff -urN linux-2.4.26/drivers/scsi/megaraid2.c linux-2.4.27/drivers/scsi/megaraid2.c --- linux-2.4.26/drivers/scsi/megaraid2.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/megaraid2.c 2004-08-07 16:26:05.516382197 -0700 @@ -14,7 +14,10 @@ * - speed-ups (list handling fixes, issued_list, optimizations.) * - lots of cleanups. * - * Version : v2.10.1 (Dec 03, 2003) - Atul Mukker + * Version : v2.10.3 (Apr 08, 2004) + * + * Authors: Atul Mukker + * Sreenivas Bagalkote * * Description: Linux device driver for LSI Logic MegaRAID controller * @@ -23,8 +26,6 @@ * * This driver is supported by LSI Logic, with assistance from Red Hat, Dell, * and others. Please send updates to the public mailing list - * linux-megaraid-devel@dell.com, and subscribe to and read archives of this - * list at http://lists.us.dell.com/. * * For history of changes, see ChangeLog.megaraid. * @@ -45,6 +46,10 @@ #include "megaraid2.h" +#ifdef LSI_CONFIG_COMPAT +#include +#endif + MODULE_AUTHOR ("LSI Logic Corporation"); MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); MODULE_LICENSE ("GPL"); @@ -206,6 +211,10 @@ */ major = register_chrdev(0, "megadev", &megadev_fops); + if (major < 0) { + printk(KERN_WARNING + "megaraid: failed to register char device.\n"); + } /* * Register the Shutdown Notification hook in kernel */ @@ -214,6 +223,13 @@ "MegaRAID Shutdown routine not registered!!\n"); } +#ifdef LSI_CONFIG_COMPAT + /* + * Register the 32-bit ioctl conversion + */ + register_ioctl32_conversion(MEGAIOCCMD, megadev_compat_ioctl); +#endif + } return hba_count; @@ -311,6 +327,7 @@ (subsysvid != DELL_SUBSYS_VID) && (subsysvid != HP_SUBSYS_VID) && (subsysvid != INTEL_SUBSYS_VID) && + (subsysvid != FSC_SUBSYS_VID) && (subsysvid != LSI_SUBSYS_VID) ) continue; @@ -403,7 +420,8 @@ scsi_set_host_lock(&adapter->lock); # endif #else - /* And this is the remainder of the 2.4 kernel series */ + /* And this is the remainder of the 2.4 kernel + series */ adapter->host_lock = &io_request_lock; #endif @@ -620,12 +638,15 @@ /* Set the Mode of addressing to 64 bit if we can */ if((adapter->flag & BOARD_64BIT)&&(sizeof(dma_addr_t) == 8)) { - pci_set_dma_mask(pdev, 0xffffffffffffffffULL); - adapter->has_64bit_addr = 1; + if (pci_set_dma_mask(pdev, 0xffffffffffffffffULL) == 0) + adapter->has_64bit_addr = 1; } - else { - pci_set_dma_mask(pdev, 0xffffffff); - adapter->has_64bit_addr = 0; + if (!adapter->has_64bit_addr) { + if (pci_set_dma_mask(pdev, 0xffffffffULL) != 0) { + printk("megaraid%d: DMA not available.\n", + host->host_no); + goto fail_attach; + } } init_MUTEX(&adapter->int_mtx); @@ -2249,26 +2270,28 @@ break; case MEGA_BULK_DATA: - pci_unmap_page(adapter->dev, scb->dma_h_bulkdata, - scb->cmd->request_bufflen, scb->dma_direction); - if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { pci_dma_sync_single(adapter->dev, scb->dma_h_bulkdata, scb->cmd->request_bufflen, PCI_DMA_FROMDEVICE); } + pci_unmap_page(adapter->dev, scb->dma_h_bulkdata, + scb->cmd->request_bufflen, scb->dma_direction); + break; case MEGA_SGLIST: - pci_unmap_sg(adapter->dev, scb->cmd->request_buffer, - scb->cmd->use_sg, scb->dma_direction); - if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { - pci_dma_sync_sg(adapter->dev, scb->cmd->request_buffer, - scb->cmd->use_sg, PCI_DMA_FROMDEVICE); + pci_dma_sync_sg(adapter->dev, + (struct scatterlist *)scb->cmd->request_buffer, + scb->cmd->use_sg, PCI_DMA_FROMDEVICE); } + pci_unmap_sg(adapter->dev, + (struct scatterlist *)scb->cmd->request_buffer, + scb->cmd->use_sg, scb->dma_direction); + break; default: @@ -2402,8 +2425,9 @@ *len = (u32)cmd->request_bufflen; if( scb->dma_direction == PCI_DMA_TODEVICE ) { - pci_dma_sync_sg(adapter->dev, cmd->request_buffer, - cmd->use_sg, PCI_DMA_TODEVICE); + pci_dma_sync_sg(adapter->dev, + (struct scatterlist *)cmd->request_buffer, + cmd->use_sg, PCI_DMA_TODEVICE); } /* Return count of SG requests */ @@ -2474,7 +2498,9 @@ memset(raw_mbox, 0, sizeof(raw_mbox)); raw_mbox[0] = FLUSH_ADAPTER; - irq_disable(adapter); + if (adapter->flag & BOARD_IOMAP) + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); /* Issue a blocking (interrupts disabled) command to the card */ @@ -2549,7 +2575,9 @@ /* * Unregister the character device interface to the driver. */ - unregister_chrdev(major, "megadev"); + if (major >= 0) { + unregister_chrdev(major, "megadev"); + } unregister_reboot_notifier(&mega_notifier); @@ -2568,6 +2596,9 @@ */ scsi_unregister(host); +#ifdef LSI_CONFIG_COMPAT + unregister_ioctl32_conversion(MEGAIOCCMD); +#endif printk("ok.\n"); @@ -3392,7 +3423,11 @@ max_channels = adapter->product_info.nchannels; - if( channel >= max_channels ) return 0; + if (channel >= max_channels) { + pci_free_consistent(pdev, 256, scsi_inq, scsi_inq_dma_handle); + mega_free_inquiry(inquiry, dma_handle, pdev); + return 0; + } for( tgt = 0; tgt <= MAX_TARGET; tgt++ ) { @@ -3860,153 +3895,6 @@ /** - * megaraid_biosparam() - * @disk - * @dev - * @geom - * - * Return the disk geometry for a particular disk - * Input: - * Disk *disk - Disk geometry - * kdev_t dev - Device node - * int *geom - Returns geometry fields - * geom[0] = heads - * geom[1] = sectors - * geom[2] = cylinders - */ -static int -megaraid_biosparam(Disk *disk, kdev_t dev, int *geom) -{ - int heads, sectors, cylinders; - adapter_t *adapter; - - /* Get pointer to host config structure */ - adapter = (adapter_t *)disk->device->host->hostdata; - - if (IS_RAID_CH(adapter, disk->device->channel)) { - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); - - /* - * Handle extended translation size for logical drives - * > 1Gb - */ - if (disk->capacity >= 0x200000) { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (heads * sectors); - } - - /* return result */ - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } - else { - if( !mega_partsize(disk, dev, geom) ) - return 0; - - printk(KERN_WARNING - "megaraid: invalid partition on this disk on channel %d\n", - disk->device->channel); - - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); - - /* Handle extended translation size for logical drives > 1Gb */ - if (disk->capacity >= 0x200000) { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (heads * sectors); - } - - /* return result */ - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } - - return 0; -} - -/* - * mega_partsize() - * @disk - * @geom - * - * Purpose : to determine the BIOS mapping used to create the partition - * table, storing the results (cyls, hds, and secs) in geom - * - * Note: Code is picked from scsicam.h - * - * Returns : -1 on failure, 0 on success. - */ -static int -mega_partsize(Disk *disk, kdev_t dev, int *geom) -{ - struct buffer_head *bh; - struct partition *p, *largest = NULL; - int i, largest_cyl; - int heads, cyls, sectors; - int capacity = disk->capacity; - - int ma = MAJOR(dev); - int mi = (MINOR(dev) & ~0xf); - - int block = 1024; - - if (blksize_size[ma]) - block = blksize_size[ma][mi]; - - if (!(bh = bread(MKDEV(ma,mi), 0, block))) - return -1; - - if (*(unsigned short *)(bh->b_data + 510) == 0xAA55 ) { - - for (largest_cyl = -1, - p = (struct partition *)(0x1BE + bh->b_data), i = 0; - i < 4; ++i, ++p) { - - if (!p->sys_ind) continue; - - cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2); - - if (cyls >= largest_cyl) { - largest_cyl = cyls; - largest = p; - } - } - } - - if (largest) { - heads = largest->end_head + 1; - sectors = largest->end_sector & 0x3f; - - if (!heads || !sectors) { - brelse(bh); - return -1; - } - - cyls = capacity/(heads * sectors); - - geom[0] = heads; - geom[1] = sectors; - geom[2] = cyls; - - brelse(bh); - return 0; - } - - brelse(bh); - return -1; -} - - -/** * megaraid_reboot_notify() * @this - unused * @code - shutdown code @@ -4040,7 +3928,9 @@ memset(raw_mbox, 0, sizeof(raw_mbox)); raw_mbox[0] = FLUSH_ADAPTER; - irq_disable(adapter); + if (adapter->flag & BOARD_IOMAP) + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); /* @@ -4179,6 +4069,18 @@ } +#ifdef LSI_CONFIG_COMPAT +static int +megadev_compat_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg, + struct file *filep) +{ + struct inode *inode = filep->f_dentry->d_inode; + + return megadev_ioctl(inode, filep, cmd, arg); +} +#endif + + /** * megadev_ioctl() * @inode - Our device inode @@ -4386,8 +4288,8 @@ /* * The user passthru structure */ - upthru = (mega_passthru *)MBOX(uioc)->xferaddr; - + upthru = (mega_passthru *) + ((ulong)(MBOX(uioc)->xferaddr)); /* * Copy in the user passthru here. */ @@ -4434,8 +4336,9 @@ /* * Get the user data */ - if( copy_from_user(data, (char *)uxferaddr, - pthru->dataxferlen) ) { + if( copy_from_user(data, + (char *)((ulong)uxferaddr), + pthru->dataxferlen) ) { rval = (-EFAULT); goto freemem_and_return; } @@ -4460,8 +4363,8 @@ * Is data going up-stream */ if( pthru->dataxferlen && (uioc.flags & UIOC_RD) ) { - if( copy_to_user((char *)uxferaddr, data, - pthru->dataxferlen) ) { + if( copy_to_user((char *)((ulong)uxferaddr), + data, pthru->dataxferlen) ) { rval = (-EFAULT); } } @@ -4509,12 +4412,13 @@ /* * Get the user data */ - if( copy_from_user(data, (char *)uxferaddr, - uioc.xferlen) ) { + if( copy_from_user(data, + (char *)((ulong)uxferaddr), + uioc.xferlen) ) { pci_free_consistent(pdev, - uioc.xferlen, - data, data_dma_hndl); + uioc.xferlen, data, + data_dma_hndl); return (-EFAULT); } @@ -4545,8 +4449,8 @@ * Is data going up-stream */ if( uioc.xferlen && (uioc.flags & UIOC_RD) ) { - if( copy_to_user((char *)uxferaddr, data, - uioc.xferlen) ) { + if( copy_to_user((char *)((ulong)uxferaddr), + data, uioc.xferlen) ) { rval = (-EFAULT); } @@ -4731,7 +4635,7 @@ umc = MBOX_P(uiocp); - upthru = (mega_passthru *)umc->xferaddr; + upthru = (mega_passthru *)((ulong)(umc->xferaddr)); if( put_user(mc->status, (u8 *)&upthru->scsistatus) ) return (-EFAULT); @@ -4749,7 +4653,7 @@ if (copy_from_user(&kmc, umc, sizeof(megacmd_t))) return -EFAULT; - upthru = (mega_passthru *)kmc.xferaddr; + upthru = (mega_passthru *)((ulong)kmc.xferaddr); if( put_user(mc->status, (u8 *)&upthru->scsistatus) ) return (-EFAULT); diff -urN linux-2.4.26/drivers/scsi/megaraid2.h linux-2.4.27/drivers/scsi/megaraid2.h --- linux-2.4.26/drivers/scsi/megaraid2.h 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/megaraid2.h 2004-08-07 16:26:05.517382238 -0700 @@ -6,7 +6,7 @@ #define MEGARAID_VERSION \ - "v2.10.1 (Release Date: Wed Dec 3 15:34:42 EST 2003)\n" + "v2.10.3 (Release Date: Thu Apr 8 16:16:05 EDT 2004)\n" /* * Driver features - change the values to enable or disable features in the @@ -44,12 +44,6 @@ */ #define MEGA_HAVE_ENH_PROC 1 -#define MAX_DEV_TYPE 32 - -#ifndef PCI_VENDOR_ID_LSI_LOGIC -#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 -#endif - #ifndef PCI_VENDOR_ID_AMI #define PCI_VENDOR_ID_AMI 0x101E #endif @@ -87,6 +81,7 @@ #define HP_SUBSYS_VID 0x103C #define LSI_SUBSYS_VID 0x1000 #define INTEL_SUBSYS_VID 0x8086 +#define FSC_SUBSYS_VID 0x1734 #define HBA_SIGNATURE 0x3344 #define HBA_SIGNATURE_471 0xCCCC @@ -134,7 +129,6 @@ .info = megaraid_info, \ .command = megaraid_command, \ .queuecommand = megaraid_queue, \ - .bios_param = megaraid_biosparam, \ .max_sectors = MAX_SECTORS_PER_IO, \ .can_queue = MAX_COMMANDS, \ .this_id = DEFAULT_INITIATOR_ID, \ @@ -662,6 +656,9 @@ */ #define MEGAIOC_MAGIC 'm' +/* Mega IOCTL command */ +#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t) + #define MEGAIOC_QNADAP 'm' /* Query # of adapters */ #define MEGAIOC_QDRVRVER 'e' /* Query driver version */ #define MEGAIOC_QADAPINFO 'g' /* Query adapter information */ @@ -1115,7 +1112,6 @@ static int megaraid_command (Scsi_Cmnd *); static int megaraid_abort(Scsi_Cmnd *); static int megaraid_reset(Scsi_Cmnd *); -static int megaraid_biosparam (Disk *, kdev_t, int *); static int mega_build_sglist (adapter_t *adapter, scb_t *scb, u32 *buffer, u32 *length); @@ -1129,6 +1125,16 @@ static int megaraid_reboot_notify (struct notifier_block *, unsigned long, void *); static int megadev_open (struct inode *, struct file *); + +#if defined(CONFIG_COMPAT) || defined( __x86_64__) || defined(IA32_EMULATION) +#define LSI_CONFIG_COMPAT +#endif + +#ifdef LSI_CONFIG_COMPAT +static int megadev_compat_ioctl(unsigned int, unsigned int, unsigned long, + struct file *); +#endif + static int megadev_ioctl (struct inode *, struct file *, unsigned int, unsigned long); static int mega_m_to_n(void *, nitioctl_t *); @@ -1172,7 +1178,6 @@ static mega_ext_passthru* mega_prepare_extpassthru(adapter_t *, scb_t *, Scsi_Cmnd *, int, int); static void mega_enum_raid_scsi(adapter_t *); -static int mega_partsize(Disk *, kdev_t, int *); static void mega_get_boot_drv(adapter_t *); static inline int mega_get_ldrv_num(adapter_t *, Scsi_Cmnd *, int); static int mega_support_random_del(adapter_t *); diff -urN linux-2.4.26/drivers/scsi/oktagon_esp.c linux-2.4.27/drivers/scsi/oktagon_esp.c --- linux-2.4.26/drivers/scsi/oktagon_esp.c 2002-08-02 17:39:44.000000000 -0700 +++ linux-2.4.27/drivers/scsi/oktagon_esp.c 2004-08-07 16:26:05.518382279 -0700 @@ -548,7 +548,7 @@ void dma_mmu_get_scsi_one(struct NCR_ESP *esp, Scsi_Cmnd *sp) { - sp->SCp.have_data_in = (int) sp->SCp.ptr = + sp->SCp.ptr = sp->request_buffer; } diff -urN linux-2.4.26/drivers/scsi/osst.c linux-2.4.27/drivers/scsi/osst.c --- linux-2.4.26/drivers/scsi/osst.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/osst.c 2004-08-07 16:26:05.522382444 -0700 @@ -3148,6 +3148,7 @@ ST_mode * STm; ST_partstat * STps; int dev = TAPE_NR(inode->i_rdev); + loff_t pos = *ppos; STp = os_scsi_tapes[dev]; @@ -3369,7 +3370,7 @@ if (i == (-ENOSPC)) { transfer = STp->buffer->writing; /* FIXME -- check this logic */ if (transfer <= do_count) { - filp->f_pos += do_count - transfer; + pos += do_count - transfer; count -= do_count - transfer; if (STps->drv_block >= 0) { STps->drv_block += (do_count - transfer) / STp->block_size; @@ -3407,7 +3408,7 @@ goto out; } - filp->f_pos += do_count; + pos += do_count; b_point += do_count; count -= do_count; if (STps->drv_block >= 0) { @@ -3429,7 +3430,7 @@ if (STps->drv_block >= 0) { STps->drv_block += blks; } - filp->f_pos += count; + pos += count; count = 0; } @@ -3459,6 +3460,7 @@ retval = total; out: + *ppos = pos; if (SRpnt != NULL) scsi_release_request(SRpnt); up(&STp->lock); @@ -3479,6 +3481,7 @@ ST_partstat * STps; Scsi_Request *SRpnt = NULL; int dev = TAPE_NR(inode->i_rdev); + loff_t pos = *ppos; STp = os_scsi_tapes[dev]; @@ -3614,7 +3617,7 @@ } STp->logical_blk_num += transfer / STp->block_size; STps->drv_block += transfer / STp->block_size; - filp->f_pos += transfer; + pos += transfer; buf += transfer; total += transfer; } @@ -3653,6 +3656,7 @@ retval = total; out: + *ppos = pos; if (SRpnt != NULL) scsi_release_request(SRpnt); up(&STp->lock); @@ -5501,6 +5505,7 @@ read: osst_read, write: osst_write, ioctl: osst_ioctl, + llseek: no_llseek, open: os_scsi_tape_open, flush: os_scsi_tape_flush, release: os_scsi_tape_close, diff -urN linux-2.4.26/drivers/scsi/sata_promise.c linux-2.4.27/drivers/scsi/sata_promise.c --- linux-2.4.26/drivers/scsi/sata_promise.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_promise.c 2004-08-07 16:26:05.524382526 -0700 @@ -0,0 +1,667 @@ +/* + * sata_promise.c - Promise SATA + * + * Maintained by: Jeff Garzik + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2003-2004 Red Hat, Inc. + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include +#include +#include "sata_promise.h" + +#define DRV_NAME "sata_promise" +#define DRV_VERSION "1.00" + + +enum { + PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */ + PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */ + PDC_TBG_MODE = 0x41, /* TBG mode */ + PDC_FLASH_CTL = 0x44, /* Flash control register */ + PDC_PCI_CTL = 0x48, /* PCI control and status register */ + PDC_GLOBAL_CTL = 0x48, /* Global control/status (per port) */ + PDC_CTLSTAT = 0x60, /* IDE control and status (per port) */ + PDC_SATA_PLUG_CSR = 0x6C, /* SATA Plug control/status reg */ + PDC_SLEW_CTL = 0x470, /* slew rate control reg */ + + PDC_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) | + (1<<8) | (1<<9) | (1<<10), + + board_2037x = 0, /* FastTrak S150 TX2plus */ + board_20319 = 1, /* FastTrak S150 TX4 */ + + PDC_HAS_PATA = (1 << 1), /* PDC20375 has PATA */ + + PDC_RESET = (1 << 11), /* HDMA reset */ +}; + + +struct pdc_port_priv { + u8 *pkt; + dma_addr_t pkt_dma; +}; + +static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static void pdc_dma_setup(struct ata_queued_cmd *qc); +static void pdc_dma_start(struct ata_queued_cmd *qc); +static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs); +static void pdc_eng_timeout(struct ata_port *ap); +static int pdc_port_start(struct ata_port *ap); +static void pdc_port_stop(struct ata_port *ap); +static void pdc_phy_reset(struct ata_port *ap); +static void pdc_fill_sg(struct ata_queued_cmd *qc); +static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf); +static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf); +static inline void pdc_dma_complete (struct ata_port *ap, + struct ata_queued_cmd *qc, int have_err); + +static Scsi_Host_Template pdc_sata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations pdc_sata_ops = { + .port_disable = ata_port_disable, + .tf_load = pdc_tf_load_mmio, + .tf_read = ata_tf_read_mmio, + .check_status = ata_check_status_mmio, + .exec_command = pdc_exec_command_mmio, + .phy_reset = pdc_phy_reset, + .bmdma_setup = pdc_dma_setup, + .bmdma_start = pdc_dma_start, + .fill_sg = pdc_fill_sg, + .eng_timeout = pdc_eng_timeout, + .irq_handler = pdc_interrupt, + .scr_read = pdc_sata_scr_read, + .scr_write = pdc_sata_scr_write, + .port_start = pdc_port_start, + .port_stop = pdc_port_stop, +}; + +static struct ata_port_info pdc_port_info[] = { + /* board_2037x */ + { + .sht = &pdc_sata_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SRST | ATA_FLAG_MMIO, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_sata_ops, + }, + + /* board_20319 */ + { + .sht = &pdc_sata_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SRST | ATA_FLAG_MMIO, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_sata_ops, + }, +}; + +static struct pci_device_id pdc_sata_pci_tbl[] = { + { PCI_VENDOR_ID_PROMISE, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3373, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3375, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3376, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { } /* terminate list */ +}; + + +static struct pci_driver pdc_sata_pci_driver = { + .name = DRV_NAME, + .id_table = pdc_sata_pci_tbl, + .probe = pdc_sata_init_one, + .remove = ata_pci_remove_one, +}; + + +static int pdc_port_start(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct pdc_port_priv *pp; + int rc; + + rc = ata_port_start(ap); + if (rc) + return rc; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + rc = -ENOMEM; + goto err_out; + } + memset(pp, 0, sizeof(*pp)); + + pp->pkt = pci_alloc_consistent(pdev, 128, &pp->pkt_dma); + if (!pp->pkt) { + rc = -ENOMEM; + goto err_out_kfree; + } + + ap->private_data = pp; + + return 0; + +err_out_kfree: + kfree(pp); +err_out: + ata_port_stop(ap); + return rc; +} + + +static void pdc_port_stop(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct pdc_port_priv *pp = ap->private_data; + + ap->private_data = NULL; + pci_free_consistent(pdev, 128, pp->pkt, pp->pkt_dma); + kfree(pp); + ata_port_stop(ap); +} + + +static void pdc_reset_port(struct ata_port *ap) +{ + void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_CTLSTAT; + unsigned int i; + u32 tmp; + + for (i = 11; i > 0; i--) { + tmp = readl(mmio); + if (tmp & PDC_RESET) + break; + + udelay(100); + + tmp |= PDC_RESET; + writel(tmp, mmio); + } + + tmp &= ~PDC_RESET; + writel(tmp, mmio); + readl(mmio); /* flush */ +} + +static void pdc_phy_reset(struct ata_port *ap) +{ + pdc_reset_port(ap); + sata_phy_reset(ap); +} + +static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, + u32 val) +{ + if (sc_reg > SCR_CONTROL) + return; + writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + +static void pdc_fill_sg(struct ata_queued_cmd *qc) +{ + struct pdc_port_priv *pp = qc->ap->private_data; + unsigned int i; + + VPRINTK("ENTER\n"); + + ata_fill_sg(qc); + + i = pdc_pkt_header(&qc->tf, qc->ap->prd_dma, qc->dev->devno, pp->pkt); + + if (qc->tf.flags & ATA_TFLAG_LBA48) + i = pdc_prep_lba48(&qc->tf, pp->pkt, i); + else + i = pdc_prep_lba28(&qc->tf, pp->pkt, i); + + pdc_pkt_footer(&qc->tf, pp->pkt, i); +} + +static inline void pdc_dma_complete (struct ata_port *ap, + struct ata_queued_cmd *qc, + int have_err) +{ + u8 err_bit = have_err ? ATA_ERR : 0; + + /* get drive status; clear intr; complete txn */ + ata_qc_complete(qc, ata_wait_idle(ap) | err_bit); +} + +static void pdc_eng_timeout(struct ata_port *ap) +{ + u8 drv_stat; + struct ata_queued_cmd *qc; + + DPRINTK("ENTER\n"); + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + goto out; + } + + /* hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + qc->scsidone = scsi_finish_command; + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + printk(KERN_ERR "ata%u: DMA timeout\n", ap->id); + ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR); + break; + + case ATA_PROT_NODATA: + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + + printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n", + ap->id, qc->tf.command, drv_stat); + + ata_qc_complete(qc, drv_stat); + break; + + default: + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + + printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n", + ap->id, qc->tf.command, drv_stat); + + ata_qc_complete(qc, drv_stat); + break; + } + +out: + DPRINTK("EXIT\n"); +} + +static inline unsigned int pdc_host_intr( struct ata_port *ap, + struct ata_queued_cmd *qc) +{ + u8 status; + unsigned int handled = 0, have_err = 0; + u32 tmp; + void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL; + + tmp = readl(mmio); + if (tmp & PDC_ERR_MASK) { + have_err = 1; + pdc_reset_port(ap); + } + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + pdc_dma_complete(ap, qc, have_err); + handled = 1; + break; + + case ATA_PROT_NODATA: /* command completion, but no data xfer */ + status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status); + if (have_err) + status |= ATA_ERR; + ata_qc_complete(qc, status); + handled = 1; + break; + + default: + ap->stats.idle_irq++; + break; + } + + return handled; +} + +static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + struct ata_port *ap; + u32 mask = 0; + unsigned int i, tmp; + unsigned int handled = 0; + void *mmio_base; + + VPRINTK("ENTER\n"); + + if (!host_set || !host_set->mmio_base) { + VPRINTK("QUICK EXIT\n"); + return IRQ_NONE; + } + + mmio_base = host_set->mmio_base; + + /* reading should also clear interrupts */ + mask = readl(mmio_base + PDC_INT_SEQMASK); + + if (mask == 0xffffffff) { + VPRINTK("QUICK EXIT 2\n"); + return IRQ_NONE; + } + mask &= 0xffff; /* only 16 tags possible */ + if (!mask) { + VPRINTK("QUICK EXIT 3\n"); + return IRQ_NONE; + } + + spin_lock(&host_set->lock); + + for (i = 0; i < host_set->n_ports; i++) { + VPRINTK("port %u\n", i); + ap = host_set->ports[i]; + tmp = mask & (1 << (i + 1)); + if (tmp && ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && (!(qc->tf.ctl & ATA_NIEN))) + handled += pdc_host_intr(ap, qc); + } + } + + spin_unlock(&host_set->lock); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} + +static void pdc_dma_setup(struct ata_queued_cmd *qc) +{ + /* nothing for now. later, we will call standard + * code in libata-core for ATAPI here */ +} + +static void pdc_dma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct pdc_port_priv *pp = ap->private_data; + unsigned int port_no = ap->port_no; + u8 seq = (u8) (port_no + 1); + + VPRINTK("ENTER, ap %p\n", ap); + + writel(0x00000001, ap->host_set->mmio_base + (seq * 4)); + readl(ap->host_set->mmio_base + (seq * 4)); /* flush */ + + pp->pkt[2] = seq; + wmb(); /* flush PRD, pkt writes */ + writel(pp->pkt_dma, (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); /* flush */ +} + +static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + if (tf->protocol != ATA_PROT_DMA) + ata_tf_load_mmio(ap, tf); +} + + +static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + if (tf->protocol != ATA_PROT_DMA) + ata_exec_command_mmio(ap, tf); +} + + +static void pdc_sata_setup_port(struct ata_ioports *port, unsigned long base) +{ + port->cmd_addr = base; + port->data_addr = base; + port->feature_addr = + port->error_addr = base + 0x4; + port->nsect_addr = base + 0x8; + port->lbal_addr = base + 0xc; + port->lbam_addr = base + 0x10; + port->lbah_addr = base + 0x14; + port->device_addr = base + 0x18; + port->command_addr = + port->status_addr = base + 0x1c; + port->altstatus_addr = + port->ctl_addr = base + 0x38; +} + + +static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) +{ + void *mmio = pe->mmio_base; + u32 tmp; + + /* + * Except for the hotplug stuff, this is voodoo from the + * Promise driver. Label this entire section + * "TODO: figure out why we do this" + */ + + /* change FIFO_SHD to 8 dwords, enable BMR_BURST */ + tmp = readl(mmio + PDC_FLASH_CTL); + tmp |= 0x12000; /* bit 16 (fifo 8 dw) and 13 (bmr burst?) */ + writel(tmp, mmio + PDC_FLASH_CTL); + + /* clear plug/unplug flags for all ports */ + tmp = readl(mmio + PDC_SATA_PLUG_CSR); + writel(tmp | 0xff, mmio + PDC_SATA_PLUG_CSR); + + /* mask plug/unplug ints */ + tmp = readl(mmio + PDC_SATA_PLUG_CSR); + writel(tmp | 0xff0000, mmio + PDC_SATA_PLUG_CSR); + + /* reduce TBG clock to 133 Mhz. */ + tmp = readl(mmio + PDC_TBG_MODE); + tmp &= ~0x30000; /* clear bit 17, 16*/ + tmp |= 0x10000; /* set bit 17:16 = 0:1 */ + writel(tmp, mmio + PDC_TBG_MODE); + + readl(mmio + PDC_TBG_MODE); /* flush */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(10) + 1); + + /* adjust slew rate control register. */ + tmp = readl(mmio + PDC_SLEW_CTL); + tmp &= 0xFFFFF03F; /* clear bit 11 ~ 6 */ + tmp |= 0x00000900; /* set bit 11-9 = 100b , bit 8-6 = 100 */ + writel(tmp, mmio + PDC_SLEW_CTL); +} + +static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + unsigned long base; + void *mmio_base; + unsigned int board_idx = (unsigned int) ent->driver_data; + int rc; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + /* + * If this driver happens to only be useful on Apple's K2, then + * we should check that here as it has a normal Serverworks ID + */ + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = ioremap(pci_resource_start(pdev, 3), + pci_resource_len(pdev, 3)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + base = (unsigned long) mmio_base; + + probe_ent->sht = pdc_port_info[board_idx].sht; + probe_ent->host_flags = pdc_port_info[board_idx].host_flags; + probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; + probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; + probe_ent->port_ops = pdc_port_info[board_idx].port_ops; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + + pdc_sata_setup_port(&probe_ent->port[0], base + 0x200); + pdc_sata_setup_port(&probe_ent->port[1], base + 0x280); + + probe_ent->port[0].scr_addr = base + 0x400; + probe_ent->port[1].scr_addr = base + 0x500; + + /* notice 4-port boards */ + switch (board_idx) { + case board_20319: + probe_ent->n_ports = 4; + + pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); + pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); + + probe_ent->port[2].scr_addr = base + 0x600; + probe_ent->port[3].scr_addr = base + 0x700; + break; + case board_2037x: + probe_ent->n_ports = 2; + break; + default: + BUG(); + break; + } + + pci_set_master(pdev); + + /* initialize adapter */ + pdc_host_init(board_idx, probe_ent); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + + +static int __init pdc_sata_init(void) +{ + int rc; + + rc = pci_module_init(&pdc_sata_pci_driver); + if (rc) + return rc; + + rc = scsi_register_module(MODULE_SCSI_HA, &pdc_sata_sht); + if (rc) { + rc = -ENODEV; + goto err_out; + } + + return 0; + +err_out: + pci_unregister_driver(&pdc_sata_pci_driver); + return rc; +} + + +static void __exit pdc_sata_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &pdc_sata_sht); + pci_unregister_driver(&pdc_sata_pci_driver); +} + + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("Promise SATA TX2/TX4 low-level driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); + +module_init(pdc_sata_init); +module_exit(pdc_sata_exit); diff -urN linux-2.4.26/drivers/scsi/sata_promise.h linux-2.4.27/drivers/scsi/sata_promise.h --- linux-2.4.26/drivers/scsi/sata_promise.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_promise.h 2004-08-07 16:26:05.525382567 -0700 @@ -0,0 +1,154 @@ +/* + * sata_promise.h - Promise SATA common definitions and inline funcs + * + * Copyright 2003-2004 Red Hat, Inc. + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#ifndef __SATA_PROMISE_H__ +#define __SATA_PROMISE_H__ + +#include + +enum pdc_packet_bits { + PDC_PKT_READ = (1 << 2), + PDC_PKT_NODATA = (1 << 3), + + PDC_PKT_SIZEMASK = (1 << 7) | (1 << 6) | (1 << 5), + PDC_PKT_CLEAR_BSY = (1 << 4), + PDC_PKT_WAIT_DRDY = (1 << 3) | (1 << 4), + PDC_LAST_REG = (1 << 3), + + PDC_REG_DEVCTL = (1 << 3) | (1 << 2) | (1 << 1), +}; + +static inline unsigned int pdc_pkt_header(struct ata_taskfile *tf, + dma_addr_t sg_table, + unsigned int devno, u8 *buf) +{ + u8 dev_reg; + u32 *buf32 = (u32 *) buf; + + /* set control bits (byte 0), zero delay seq id (byte 3), + * and seq id (byte 2) + */ + switch (tf->protocol) { + case ATA_PROT_DMA: + if (!(tf->flags & ATA_TFLAG_WRITE)) + buf32[0] = cpu_to_le32(PDC_PKT_READ); + else + buf32[0] = 0; + break; + + case ATA_PROT_NODATA: + buf32[0] = cpu_to_le32(PDC_PKT_NODATA); + break; + + default: + BUG(); + break; + } + + buf32[1] = cpu_to_le32(sg_table); /* S/G table addr */ + buf32[2] = 0; /* no next-packet */ + + if (devno == 0) + dev_reg = ATA_DEVICE_OBS; + else + dev_reg = ATA_DEVICE_OBS | ATA_DEV1; + + /* select device */ + buf[12] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE; + buf[13] = dev_reg; + + /* device control register */ + buf[14] = (1 << 5) | PDC_REG_DEVCTL; + buf[15] = tf->ctl; + + return 16; /* offset of next byte */ +} + +static inline unsigned int pdc_pkt_footer(struct ata_taskfile *tf, u8 *buf, + unsigned int i) +{ + if (tf->flags & ATA_TFLAG_DEVICE) { + buf[i++] = (1 << 5) | ATA_REG_DEVICE; + buf[i++] = tf->device; + } + + /* and finally the command itself; also includes end-of-pkt marker */ + buf[i++] = (1 << 5) | PDC_LAST_REG | ATA_REG_CMD; + buf[i++] = tf->command; + + return i; +} + +static inline unsigned int pdc_prep_lba28(struct ata_taskfile *tf, u8 *buf, unsigned int i) +{ + /* the "(1 << 5)" should be read "(count << 5)" */ + + /* ATA command block registers */ + buf[i++] = (1 << 5) | ATA_REG_FEATURE; + buf[i++] = tf->feature; + + buf[i++] = (1 << 5) | ATA_REG_NSECT; + buf[i++] = tf->nsect; + + buf[i++] = (1 << 5) | ATA_REG_LBAL; + buf[i++] = tf->lbal; + + buf[i++] = (1 << 5) | ATA_REG_LBAM; + buf[i++] = tf->lbam; + + buf[i++] = (1 << 5) | ATA_REG_LBAH; + buf[i++] = tf->lbah; + + return i; +} + +static inline unsigned int pdc_prep_lba48(struct ata_taskfile *tf, u8 *buf, unsigned int i) +{ + /* the "(2 << 5)" should be read "(count << 5)" */ + + /* ATA command block registers */ + buf[i++] = (2 << 5) | ATA_REG_FEATURE; + buf[i++] = tf->hob_feature; + buf[i++] = tf->feature; + + buf[i++] = (2 << 5) | ATA_REG_NSECT; + buf[i++] = tf->hob_nsect; + buf[i++] = tf->nsect; + + buf[i++] = (2 << 5) | ATA_REG_LBAL; + buf[i++] = tf->hob_lbal; + buf[i++] = tf->lbal; + + buf[i++] = (2 << 5) | ATA_REG_LBAM; + buf[i++] = tf->hob_lbam; + buf[i++] = tf->lbam; + + buf[i++] = (2 << 5) | ATA_REG_LBAH; + buf[i++] = tf->hob_lbah; + buf[i++] = tf->lbah; + + return i; +} + + +#endif /* __SATA_PROMISE_H__ */ diff -urN linux-2.4.26/drivers/scsi/sata_sil.c linux-2.4.27/drivers/scsi/sata_sil.c --- linux-2.4.26/drivers/scsi/sata_sil.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_sil.c 2004-08-07 16:26:05.526382608 -0700 @@ -0,0 +1,455 @@ +/* + * sata_sil.c - Silicon Image SATA + * + * Maintained by: Jeff Garzik + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2003 Red Hat, Inc. + * Copyright 2003 Benjamin Herrenschmidt + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "sata_sil" +#define DRV_VERSION "0.54" + +enum { + sil_3112 = 0, + sil_3114 = 1, + + SIL_SYSCFG = 0x48, + SIL_MASK_IDE0_INT = (1 << 22), + SIL_MASK_IDE1_INT = (1 << 23), + SIL_MASK_IDE2_INT = (1 << 24), + SIL_MASK_IDE3_INT = (1 << 25), + SIL_MASK_2PORT = SIL_MASK_IDE0_INT | SIL_MASK_IDE1_INT, + SIL_MASK_4PORT = SIL_MASK_2PORT | + SIL_MASK_IDE2_INT | SIL_MASK_IDE3_INT, + + SIL_IDE2_BMDMA = 0x200, + + SIL_INTR_STEERING = (1 << 1), + SIL_QUIRK_MOD15WRITE = (1 << 0), + SIL_QUIRK_UDMA5MAX = (1 << 1), +}; + +static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static void sil_dev_config(struct ata_port *ap, struct ata_device *dev); +static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); +static void sil_post_set_mode (struct ata_port *ap); + +static struct pci_device_id sil_pci_tbl[] = { + { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 }, + { } /* terminate list */ +}; + + +/* TODO firmware versions should be added - eric */ +struct sil_drivelist { + const char * product; + unsigned int quirk; +} sil_blacklist [] = { + { "ST320012AS", SIL_QUIRK_MOD15WRITE }, + { "ST330013AS", SIL_QUIRK_MOD15WRITE }, + { "ST340017AS", SIL_QUIRK_MOD15WRITE }, + { "ST360015AS", SIL_QUIRK_MOD15WRITE }, + { "ST380023AS", SIL_QUIRK_MOD15WRITE }, + { "ST3120023AS", SIL_QUIRK_MOD15WRITE }, + { "ST340014ASL", SIL_QUIRK_MOD15WRITE }, + { "ST360014ASL", SIL_QUIRK_MOD15WRITE }, + { "ST380011ASL", SIL_QUIRK_MOD15WRITE }, + { "ST3120022ASL", SIL_QUIRK_MOD15WRITE }, + { "ST3160021ASL", SIL_QUIRK_MOD15WRITE }, + { "Maxtor 4D060H3", SIL_QUIRK_UDMA5MAX }, + { } +}; + +static struct pci_driver sil_pci_driver = { + .name = DRV_NAME, + .id_table = sil_pci_tbl, + .probe = sil_init_one, + .remove = ata_pci_remove_one, +}; + +static Scsi_Host_Template sil_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations sil_ops = { + .port_disable = ata_port_disable, + .dev_config = sil_dev_config, + .tf_load = ata_tf_load_mmio, + .tf_read = ata_tf_read_mmio, + .check_status = ata_check_status_mmio, + .exec_command = ata_exec_command_mmio, + .phy_reset = sata_phy_reset, + .post_set_mode = sil_post_set_mode, + .bmdma_setup = ata_bmdma_setup_mmio, + .bmdma_start = ata_bmdma_start_mmio, + .fill_sg = ata_fill_sg, + .eng_timeout = ata_eng_timeout, + .irq_handler = ata_interrupt, + .scr_read = sil_scr_read, + .scr_write = sil_scr_write, + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +static struct ata_port_info sil_port_info[] = { + /* sil_3112 */ + { + .sht = &sil_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SRST | ATA_FLAG_MMIO, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x3f, /* udma0-5 */ + .port_ops = &sil_ops, + }, /* sil_3114 */ + { + .sht = &sil_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SRST | ATA_FLAG_MMIO, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x3f, /* udma0-5 */ + .port_ops = &sil_ops, + }, +}; + +/* per-port register offsets */ +/* TODO: we can probably calculate rather than use a table */ +static const struct { + unsigned long tf; /* ATA taskfile register block */ + unsigned long ctl; /* ATA control/altstatus register block */ + unsigned long bmdma; /* DMA register block */ + unsigned long scr; /* SATA control register block */ + unsigned long sien; /* SATA Interrupt Enable register */ + unsigned long xfer_mode;/* data transfer mode register */ +} sil_port[] = { + /* port 0 ... */ + { 0x80, 0x8A, 0x00, 0x100, 0x148, 0xb4 }, + { 0xC0, 0xCA, 0x08, 0x180, 0x1c8, 0xf4 }, + { 0x280, 0x28A, 0x200, 0x300, 0x348, 0x2b4 }, + { 0x2C0, 0x2CA, 0x208, 0x380, 0x3c8, 0x2f4 }, + /* ... port 3 */ +}; + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("low-level driver for Silicon Image SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, sil_pci_tbl); + +static void sil_post_set_mode (struct ata_port *ap) +{ + struct ata_host_set *host_set = ap->host_set; + struct ata_device *dev; + void *addr = host_set->mmio_base + sil_port[ap->port_no].xfer_mode; + u32 tmp, dev_mode[2]; + unsigned int i; + + for (i = 0; i < 2; i++) { + dev = &ap->device[i]; + if (!ata_dev_present(dev)) + dev_mode[i] = 0; /* PIO0/1/2 */ + else if (dev->flags & ATA_DFLAG_PIO) + dev_mode[i] = 1; /* PIO3/4 */ + else + dev_mode[i] = 3; /* UDMA */ + /* value 2 indicates MDMA */ + } + + tmp = readl(addr); + tmp &= ~((1<<5) | (1<<4) | (1<<1) | (1<<0)); + tmp |= dev_mode[0]; + tmp |= (dev_mode[1] << 4); + writel(tmp, addr); + readl(addr); /* flush */ +} + +static inline unsigned long sil_scr_addr(struct ata_port *ap, unsigned int sc_reg) +{ + unsigned long offset = ap->ioaddr.scr_addr; + + switch (sc_reg) { + case SCR_STATUS: + return offset + 4; + case SCR_ERROR: + return offset + 8; + case SCR_CONTROL: + return offset; + default: + /* do nothing */ + break; + } + + return 0; +} + +static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + void *mmio = (void *) sil_scr_addr(ap, sc_reg); + if (mmio) + return readl(mmio); + return 0xffffffffU; +} + +static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + void *mmio = (void *) sil_scr_addr(ap, sc_reg); + if (mmio) + writel(val, mmio); +} + +/** + * sil_dev_config - Apply device/host-specific errata fixups + * @ap: Port containing device to be examined + * @dev: Device to be examined + * + * After the IDENTIFY [PACKET] DEVICE step is complete, and a + * device is known to be present, this function is called. + * We apply two errata fixups which are specific to Silicon Image, + * a Seagate and a Maxtor fixup. + * + * For certain Seagate devices, we must limit the maximum sectors + * to under 8K. + * + * For certain Maxtor devices, we must not program the drive + * beyond udma5. + * + * Both fixups are unfairly pessimistic. As soon as I get more + * information on these errata, I will create a more exhaustive + * list, and apply the fixups to only the specific + * devices/hosts/firmwares that need it. + * + * 20040111 - Seagate drives affected by the Mod15Write bug are blacklisted + * The Maxtor quirk is in the blacklist, but I'm keeping the original + * pessimistic fix for the following reasons... + * - There seems to be less info on it, only one device gleaned off the + * Windows driver, maybe only one is affected. More info would be greatly + * appreciated. + * - But then again UDMA5 is hardly anything to complain about + */ +static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) +{ + unsigned int n, quirks = 0; + unsigned char model_num[40]; + const char *s; + unsigned int len; + + ata_dev_id_string(dev, model_num, ATA_ID_PROD_OFS, + sizeof(model_num)); + s = &model_num[0]; + len = strnlen(s, sizeof(model_num)); + + /* ATAPI specifies that empty space is blank-filled; remove blanks */ + while ((len > 0) && (s[len - 1] == ' ')) + len--; + + for (n = 0; sil_blacklist[n].product; n++) + if (!memcmp(sil_blacklist[n].product, s, + strlen(sil_blacklist[n].product))) { + quirks = sil_blacklist[n].quirk; + break; + } + + /* limit requests to 15 sectors */ + if (quirks & SIL_QUIRK_MOD15WRITE) { + printk(KERN_INFO "ata%u(%u): applying Seagate errata fix\n", + ap->id, dev->devno); + ap->host->max_sectors = 15; + ap->host->hostt->max_sectors = 15; + return; + } + + /* limit to udma5 */ + if (quirks & SIL_QUIRK_UDMA5MAX) { + printk(KERN_INFO "ata%u(%u): applying Maxtor errata fix %s\n", + ap->id, dev->devno, s); + ap->udma_mask &= ATA_UDMA5; + return; + } +} + +static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + unsigned long base; + void *mmio_base; + int rc; + unsigned int i; + u32 tmp, irq_mask; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + /* + * If this driver happens to only be useful on Apple's K2, then + * we should check that here as it has a normal Serverworks ID + */ + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + INIT_LIST_HEAD(&probe_ent->node); + probe_ent->pdev = pdev; + probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops; + probe_ent->sht = sil_port_info[ent->driver_data].sht; + probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2; + probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask; + probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask; + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->host_flags = sil_port_info[ent->driver_data].host_flags; + + mmio_base = ioremap(pci_resource_start(pdev, 5), + pci_resource_len(pdev, 5)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + + probe_ent->mmio_base = mmio_base; + + base = (unsigned long) mmio_base; + + for (i = 0; i < probe_ent->n_ports; i++) { + probe_ent->port[i].cmd_addr = base + sil_port[i].tf; + probe_ent->port[i].altstatus_addr = + probe_ent->port[i].ctl_addr = base + sil_port[i].ctl; + probe_ent->port[i].bmdma_addr = base + sil_port[i].bmdma; + probe_ent->port[i].scr_addr = base + sil_port[i].scr; + ata_std_ports(&probe_ent->port[i]); + } + + if (ent->driver_data == sil_3114) { + irq_mask = SIL_MASK_4PORT; + + /* flip the magic "make 4 ports work" bit */ + tmp = readl(mmio_base + SIL_IDE2_BMDMA); + if ((tmp & SIL_INTR_STEERING) == 0) + writel(tmp | SIL_INTR_STEERING, + mmio_base + SIL_IDE2_BMDMA); + + } else { + irq_mask = SIL_MASK_2PORT; + } + + /* make sure IDE0/1/2/3 interrupts are not masked */ + tmp = readl(mmio_base + SIL_SYSCFG); + if (tmp & irq_mask) { + tmp &= ~irq_mask; + writel(tmp, mmio_base + SIL_SYSCFG); + readl(mmio_base + SIL_SYSCFG); /* flush */ + } + + /* mask all SATA phy-related interrupts */ + /* TODO: unmask bit 6 (SError N bit) for hotplug */ + for (i = 0; i < probe_ent->n_ports; i++) + writel(0, mmio_base + sil_port[i].sien); + + pci_set_master(pdev); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + +static int __init sil_init(void) +{ + int rc; + + rc = pci_module_init(&sil_pci_driver); + if (rc) + return rc; + + rc = scsi_register_module(MODULE_SCSI_HA, &sil_sht); + if (rc) { + rc = -ENODEV; + goto err_out; + } + + return 0; + +err_out: + pci_unregister_driver(&sil_pci_driver); + return rc; +} + +static void __exit sil_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &sil_sht); + pci_unregister_driver(&sil_pci_driver); +} + + +module_init(sil_init); +module_exit(sil_exit); diff -urN linux-2.4.26/drivers/scsi/sata_sis.c linux-2.4.27/drivers/scsi/sata_sis.c --- linux-2.4.26/drivers/scsi/sata_sis.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_sis.c 2004-08-07 16:26:05.527382649 -0700 @@ -0,0 +1,294 @@ +/* + * sata_sis.c - Silicon Integrated Systems SATA + * + * Maintained by: Uwe Koziolek + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2004 Uwe Koziolek + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "sata_sis" +#define DRV_VERSION "0.10" + +enum { + sis_180 = 0, + SIS_SCR_PCI_BAR = 5, + + /* PCI configuration registers */ + SIS_GENCTL = 0x54, /* IDE General Control register */ + SIS_SCR_BASE = 0xc0, /* sata0 phy SCR registers */ + SIS_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ + + /* random bits */ + SIS_FLAG_CFGSCR = (1 << 30), /* host flag: SCRs via PCI cfg */ + + GENCTL_IOMAPPED_SCR = (1 << 26), /* if set, SCRs are in IO space */ +}; + +static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); + +static struct pci_device_id sis_pci_tbl[] = { + { PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, + { PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, + { } /* terminate list */ +}; + + +static struct pci_driver sis_pci_driver = { + .name = DRV_NAME, + .id_table = sis_pci_tbl, + .probe = sis_init_one, + .remove = ata_pci_remove_one, +}; + +static Scsi_Host_Template sis_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = ATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations sis_ops = { + .port_disable = ata_port_disable, + .tf_load = ata_tf_load_pio, + .tf_read = ata_tf_read_pio, + .check_status = ata_check_status_pio, + .exec_command = ata_exec_command_pio, + .phy_reset = sata_phy_reset, + .bmdma_setup = ata_bmdma_setup_pio, + .bmdma_start = ata_bmdma_start_pio, + .fill_sg = ata_fill_sg, + .eng_timeout = ata_eng_timeout, + .irq_handler = ata_interrupt, + .scr_read = sis_scr_read, + .scr_write = sis_scr_write, + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + + +MODULE_AUTHOR("Uwe Koziolek"); +MODULE_DESCRIPTION("low-level driver for Silicon Integratad Systems SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, sis_pci_tbl); + +static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg) +{ + unsigned int addr = SIS_SCR_BASE + (4 * sc_reg); + + if (port_no) + addr += SIS_SATA1_OFS; + return addr; +} + +static u32 sis_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg); + u32 val; + + if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */ + return 0xffffffff; + pci_read_config_dword(ap->host_set->pdev, cfg_addr, &val); + return val; +} + +static void sis_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) +{ + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr); + + if (scr == SCR_ERROR) /* doesn't exist in PCI cfg space */ + return; + pci_write_config_dword(ap->host_set->pdev, cfg_addr, val); +} + +static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + + if (ap->flags & SIS_FLAG_CFGSCR) + return sis_scr_cfg_read(ap, sc_reg); + return inl(ap->ioaddr.scr_addr + (sc_reg * 4)); +} + +static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + if (sc_reg > SCR_CONTROL) + return; + + if (ap->flags & SIS_FLAG_CFGSCR) + sis_scr_cfg_write(ap, sc_reg, val); + else + outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)); +} + +/* move to PCI layer, integrate w/ MSI stuff */ +static void pci_enable_intx(struct pci_dev *pdev) +{ + u16 pci_command; + + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_INTX_DISABLE) { + pci_command &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } +} + +static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct ata_probe_ent *probe_ent = NULL; + int rc; + u32 genctl; + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + probe_ent->sht = &sis_sht; + probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET | + ATA_FLAG_NO_LEGACY; + + /* check and see if the SCRs are in IO space or PCI cfg space */ + pci_read_config_dword(pdev, SIS_GENCTL, &genctl); + if ((genctl & GENCTL_IOMAPPED_SCR) == 0) + probe_ent->host_flags |= SIS_FLAG_CFGSCR; + + /* if hardware thinks SCRs are in IO space, but there are + * no IO resources assigned, change to PCI cfg space. + */ + if ((!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) && + ((pci_resource_start(pdev, SIS_SCR_PCI_BAR) == 0) || + (pci_resource_len(pdev, SIS_SCR_PCI_BAR) < 128))) { + genctl &= ~GENCTL_IOMAPPED_SCR; + pci_write_config_dword(pdev, SIS_GENCTL, genctl); + probe_ent->host_flags |= SIS_FLAG_CFGSCR; + } + + probe_ent->pio_mask = 0x03; + probe_ent->udma_mask = 0x7f; + probe_ent->port_ops = &sis_ops; + + probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0); + ata_std_ports(&probe_ent->port[0]); + probe_ent->port[0].ctl_addr = + pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; + probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4); + if (!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) + probe_ent->port[0].scr_addr = + pci_resource_start(pdev, SIS_SCR_PCI_BAR); + + probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2); + ata_std_ports(&probe_ent->port[1]); + probe_ent->port[1].ctl_addr = + pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; + probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8; + if (!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) + probe_ent->port[1].scr_addr = + pci_resource_start(pdev, SIS_SCR_PCI_BAR) + 64; + + probe_ent->n_ports = 2; + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + + pci_set_master(pdev); + pci_enable_intx(pdev); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_regions: + pci_release_regions(pdev); + +err_out: + pci_disable_device(pdev); + return rc; + +} + +static int __init sis_init(void) +{ + int rc = pci_module_init(&sis_pci_driver); + if (rc) + return rc; + + rc = scsi_register_module(MODULE_SCSI_HA, &sis_sht); + if (rc) { + pci_unregister_driver(&sis_pci_driver); + return -ENODEV; + } + + return 0; +} + +static void __exit sis_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &sis_sht); + pci_unregister_driver(&sis_pci_driver); +} + +module_init(sis_init); +module_exit(sis_exit); + diff -urN linux-2.4.26/drivers/scsi/sata_svw.c linux-2.4.27/drivers/scsi/sata_svw.c --- linux-2.4.26/drivers/scsi/sata_svw.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_svw.c 2004-08-07 16:26:05.528382690 -0700 @@ -0,0 +1,415 @@ +/* + * sata_svw.c - ServerWorks / Apple K2 SATA + * + * Maintained by: Benjamin Herrenschmidt and + * Jeff Garzik + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2003 Benjamin Herrenschmidt + * + * Bits from Jeff Garzik, Copyright RedHat, Inc. + * + * This driver probably works with non-Apple versions of the + * Broadcom chipset... + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#ifdef CONFIG_PPC_OF +#include +#include +#endif /* CONFIG_PPC_OF */ + +#define DRV_NAME "sata_svw" +#define DRV_VERSION "1.04" + +/* Taskfile registers offsets */ +#define K2_SATA_TF_CMD_OFFSET 0x00 +#define K2_SATA_TF_DATA_OFFSET 0x00 +#define K2_SATA_TF_ERROR_OFFSET 0x04 +#define K2_SATA_TF_NSECT_OFFSET 0x08 +#define K2_SATA_TF_LBAL_OFFSET 0x0c +#define K2_SATA_TF_LBAM_OFFSET 0x10 +#define K2_SATA_TF_LBAH_OFFSET 0x14 +#define K2_SATA_TF_DEVICE_OFFSET 0x18 +#define K2_SATA_TF_CMDSTAT_OFFSET 0x1c +#define K2_SATA_TF_CTL_OFFSET 0x20 + +/* DMA base */ +#define K2_SATA_DMA_CMD_OFFSET 0x30 + +/* SCRs base */ +#define K2_SATA_SCR_STATUS_OFFSET 0x40 +#define K2_SATA_SCR_ERROR_OFFSET 0x44 +#define K2_SATA_SCR_CONTROL_OFFSET 0x48 + +/* Others */ +#define K2_SATA_SICR1_OFFSET 0x80 +#define K2_SATA_SICR2_OFFSET 0x84 +#define K2_SATA_SIM_OFFSET 0x88 + +/* Port stride */ +#define K2_SATA_PORT_OFFSET 0x100 + + +static u32 k2_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void k2_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, + u32 val) +{ + if (sc_reg > SCR_CONTROL) + return; + writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void k2_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + writeb(tf->ctl, ioaddr->ctl_addr); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + writew(tf->feature | (((u16)tf->hob_feature) << 8), ioaddr->feature_addr); + writew(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr); + writew(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr); + writew(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr); + writew(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr); + } else if (is_addr) { + writew(tf->feature, ioaddr->feature_addr); + writew(tf->nsect, ioaddr->nsect_addr); + writew(tf->lbal, ioaddr->lbal_addr); + writew(tf->lbam, ioaddr->lbam_addr); + writew(tf->lbah, ioaddr->lbah_addr); + } + + if (tf->flags & ATA_TFLAG_DEVICE) + writeb(tf->device, ioaddr->device_addr); + + ata_wait_idle(ap); +} + + +static void k2_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u16 nsect, lbal, lbam, lbah; + + nsect = tf->nsect = readw(ioaddr->nsect_addr); + lbal = tf->lbal = readw(ioaddr->lbal_addr); + lbam = tf->lbam = readw(ioaddr->lbam_addr); + lbah = tf->lbah = readw(ioaddr->lbah_addr); + tf->device = readw(ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + tf->hob_feature = readw(ioaddr->error_addr) >> 8; + tf->hob_nsect = nsect >> 8; + tf->hob_lbal = lbal >> 8; + tf->hob_lbam = lbam >> 8; + tf->hob_lbah = lbah >> 8; + } +} + + +static u8 k2_stat_check_status(struct ata_port *ap) +{ + return readl((void *) ap->ioaddr.status_addr); +} + +#ifdef CONFIG_PPC_OF +/* + * k2_sata_proc_info + * inout : decides on the direction of the dataflow and the meaning of the + * variables + * buffer: If inout==FALSE data is being written to it else read from it + * *start: If inout==FALSE start of the valid data in the buffer + * offset: If inout==FALSE offset from the beginning of the imaginary file + * from which we start writing into the buffer + * length: If inout==FALSE max number of bytes to be written into the buffer + * else number of bytes in the buffer + */ +static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, + off_t offset, int count, int inout) +{ + struct ata_port *ap; + struct device_node *np; + int len, index; + + /* Find the ata_port */ + ap = (struct ata_port *) &shost->hostdata[0]; + if (ap == NULL) + return 0; + + /* Find the OF node for the PCI device proper */ + np = pci_device_to_OF_node(ap->host_set->pdev); + if (np == NULL) + return 0; + + /* Match it to a port node */ + index = (ap == ap->host_set->ports[0]) ? 0 : 1; + for (np = np->child; np != NULL; np = np->sibling) { + u32 *reg = (u32 *)get_property(np, "reg", NULL); + if (!reg) + continue; + if (index == *reg) + break; + } + if (np == NULL) + return 0; + + len = sprintf(page, "devspec: %s\n", np->full_name); + + return len; +} +#endif /* CONFIG_PPC_OF */ + + +static Scsi_Host_Template k2_sata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, +#ifdef CONFIG_PPC_OF + .proc_info = k2_sata_proc_info, +#endif + .bios_param = ata_std_bios_param, +}; + + +static struct ata_port_operations k2_sata_ops = { + .port_disable = ata_port_disable, + .tf_load = k2_sata_tf_load, + .tf_read = k2_sata_tf_read, + .check_status = k2_stat_check_status, + .exec_command = ata_exec_command_mmio, + .phy_reset = sata_phy_reset, + .bmdma_setup = ata_bmdma_setup_mmio, + .bmdma_start = ata_bmdma_start_mmio, + .fill_sg = ata_fill_sg, + .eng_timeout = ata_eng_timeout, + .irq_handler = ata_interrupt, + .scr_read = k2_sata_scr_read, + .scr_write = k2_sata_scr_write, + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +static void k2_sata_setup_port(struct ata_ioports *port, unsigned long base) +{ + port->cmd_addr = base + K2_SATA_TF_CMD_OFFSET; + port->data_addr = base + K2_SATA_TF_DATA_OFFSET; + port->feature_addr = + port->error_addr = base + K2_SATA_TF_ERROR_OFFSET; + port->nsect_addr = base + K2_SATA_TF_NSECT_OFFSET; + port->lbal_addr = base + K2_SATA_TF_LBAL_OFFSET; + port->lbam_addr = base + K2_SATA_TF_LBAM_OFFSET; + port->lbah_addr = base + K2_SATA_TF_LBAH_OFFSET; + port->device_addr = base + K2_SATA_TF_DEVICE_OFFSET; + port->command_addr = + port->status_addr = base + K2_SATA_TF_CMDSTAT_OFFSET; + port->altstatus_addr = + port->ctl_addr = base + K2_SATA_TF_CTL_OFFSET; + port->bmdma_addr = base + K2_SATA_DMA_CMD_OFFSET; + port->scr_addr = base + K2_SATA_SCR_STATUS_OFFSET; +} + + +static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + unsigned long base; + void *mmio_base; + int rc; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + /* + * If this driver happens to only be useful on Apple's K2, then + * we should check that here as it has a normal Serverworks ID + */ + rc = pci_enable_device(pdev); + if (rc) + return rc; + /* + * Check if we have resources mapped at all (second function may + * have been disabled by firmware) + */ + if (pci_resource_len(pdev, 5) == 0) + return -ENODEV; + + /* Request PCI regions */ + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = ioremap(pci_resource_start(pdev, 5), + pci_resource_len(pdev, 5)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + base = (unsigned long) mmio_base; + + /* Clear a magic bit in SCR1 according to Darwin, those help + * some funky seagate drives (though so far, those were already + * set by the firmware on the machines I had access to + */ + writel(readl(mmio_base + K2_SATA_SICR1_OFFSET) & ~0x00040000, + mmio_base + K2_SATA_SICR1_OFFSET); + + /* Clear SATA error & interrupts we don't use */ + writel(0xffffffff, mmio_base + K2_SATA_SCR_ERROR_OFFSET); + writel(0x0, mmio_base + K2_SATA_SIM_OFFSET); + + probe_ent->sht = &k2_sata_sht; + probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET | + ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO; + probe_ent->port_ops = &k2_sata_ops; + probe_ent->n_ports = 4; + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + + /* We don't care much about the PIO/UDMA masks, but the core won't like us + * if we don't fill these + */ + probe_ent->pio_mask = 0x1f; + probe_ent->udma_mask = 0x7f; + + /* We have 4 ports per PCI function */ + k2_sata_setup_port(&probe_ent->port[0], base + 0 * K2_SATA_PORT_OFFSET); + k2_sata_setup_port(&probe_ent->port[1], base + 1 * K2_SATA_PORT_OFFSET); + k2_sata_setup_port(&probe_ent->port[2], base + 2 * K2_SATA_PORT_OFFSET); + k2_sata_setup_port(&probe_ent->port[3], base + 3 * K2_SATA_PORT_OFFSET); + + pci_set_master(pdev); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + + +static struct pci_device_id k2_sata_pci_tbl[] = { + { 0x1166, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { } +}; + + +static struct pci_driver k2_sata_pci_driver = { + .name = DRV_NAME, + .id_table = k2_sata_pci_tbl, + .probe = k2_sata_init_one, + .remove = ata_pci_remove_one, +}; + + +static int __init k2_sata_init(void) +{ + int rc; + + rc = pci_module_init(&k2_sata_pci_driver); + if (rc) + return rc; + + rc = scsi_register_module(MODULE_SCSI_HA, &k2_sata_sht); + if (rc) { + rc = -ENODEV; + goto err_out; + } + + return 0; + +err_out: + pci_unregister_driver(&k2_sata_pci_driver); + return rc; +} + + +static void __exit k2_sata_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &k2_sata_sht); + pci_unregister_driver(&k2_sata_pci_driver); +} + + +MODULE_AUTHOR("Benjamin Herrenschmidt"); +MODULE_DESCRIPTION("low-level driver for K2 SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, k2_sata_pci_tbl); + +module_init(k2_sata_init); +module_exit(k2_sata_exit); diff -urN linux-2.4.26/drivers/scsi/sata_sx4.c linux-2.4.27/drivers/scsi/sata_sx4.c --- linux-2.4.26/drivers/scsi/sata_sx4.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_sx4.c 2004-08-07 16:26:05.531382813 -0700 @@ -0,0 +1,1459 @@ +/* + * sata_sx4.c - Promise SATA + * + * Maintained by: Jeff Garzik + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2003-2004 Red Hat, Inc. + * + * The contents of this file are subject to the Open + * Software License version 1.1 that can be found at + * http://www.opensource.org/licenses/osl-1.1.txt and is included herein + * by reference. + * + * Alternatively, the contents of this file may be used under the terms + * of the GNU General Public License version 2 (the "GPL") as distributed + * in the kernel source COPYING file, in which case the provisions of + * the GPL are applicable instead of the above. If you wish to allow + * the use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under + * the OSL, indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by the GPL. + * If you do not delete the provisions above, a recipient may use your + * version of this file under either the OSL or the GPL. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include +#include +#include "sata_promise.h" + +#define DRV_NAME "sata_sx4" +#define DRV_VERSION "0.50" + + +enum { + PDC_PRD_TBL = 0x44, /* Direct command DMA table addr */ + + PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */ + PDC_HDMA_PKT_SUBMIT = 0x100, /* Host DMA packet pointer addr */ + PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */ + PDC_HDMA_CTLSTAT = 0x12C, /* Host DMA control / status */ + + PDC_20621_SEQCTL = 0x400, + PDC_20621_SEQMASK = 0x480, + PDC_20621_GENERAL_CTL = 0x484, + PDC_20621_PAGE_SIZE = (32 * 1024), + + /* chosen, not constant, values; we design our own DIMM mem map */ + PDC_20621_DIMM_WINDOW = 0x0C, /* page# for 32K DIMM window */ + PDC_20621_DIMM_BASE = 0x00200000, + PDC_20621_DIMM_DATA = (64 * 1024), + PDC_DIMM_DATA_STEP = (256 * 1024), + PDC_DIMM_WINDOW_STEP = (8 * 1024), + PDC_DIMM_HOST_PRD = (6 * 1024), + PDC_DIMM_HOST_PKT = (128 * 0), + PDC_DIMM_HPKT_PRD = (128 * 1), + PDC_DIMM_ATA_PKT = (128 * 2), + PDC_DIMM_APKT_PRD = (128 * 3), + PDC_DIMM_HEADER_SZ = PDC_DIMM_APKT_PRD + 128, + PDC_PAGE_WINDOW = 0x40, + PDC_PAGE_DATA = PDC_PAGE_WINDOW + + (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE), + PDC_PAGE_SET = PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE, + + PDC_CHIP0_OFS = 0xC0000, /* offset of chip #0 */ + + PDC_20621_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) | + (1<<23), + + board_20621 = 0, /* FastTrak S150 SX4 */ + + PDC_RESET = (1 << 11), /* HDMA reset */ + + PDC_MAX_HDMA = 32, + PDC_HDMA_Q_MASK = (PDC_MAX_HDMA - 1), + + PDC_DIMM0_SPD_DEV_ADDRESS = 0x50, + PDC_DIMM1_SPD_DEV_ADDRESS = 0x51, + PDC_MAX_DIMM_MODULE = 0x02, + PDC_I2C_CONTROL_OFFSET = 0x48, + PDC_I2C_ADDR_DATA_OFFSET = 0x4C, + PDC_DIMM0_CONTROL_OFFSET = 0x80, + PDC_DIMM1_CONTROL_OFFSET = 0x84, + PDC_SDRAM_CONTROL_OFFSET = 0x88, + PDC_I2C_WRITE = 0x00000000, + PDC_I2C_READ = 0x00000040, + PDC_I2C_START = 0x00000080, + PDC_I2C_MASK_INT = 0x00000020, + PDC_I2C_COMPLETE = 0x00010000, + PDC_I2C_NO_ACK = 0x00100000, + PDC_DIMM_SPD_SUBADDRESS_START = 0x00, + PDC_DIMM_SPD_SUBADDRESS_END = 0x7F, + PDC_DIMM_SPD_ROW_NUM = 3, + PDC_DIMM_SPD_COLUMN_NUM = 4, + PDC_DIMM_SPD_MODULE_ROW = 5, + PDC_DIMM_SPD_TYPE = 11, + PDC_DIMM_SPD_FRESH_RATE = 12, + PDC_DIMM_SPD_BANK_NUM = 17, + PDC_DIMM_SPD_CAS_LATENCY = 18, + PDC_DIMM_SPD_ATTRIBUTE = 21, + PDC_DIMM_SPD_ROW_PRE_CHARGE = 27, + PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28, + PDC_DIMM_SPD_RAS_CAS_DELAY = 29, + PDC_DIMM_SPD_ACTIVE_PRECHARGE = 30, + PDC_DIMM_SPD_SYSTEM_FREQ = 126, + PDC_CTL_STATUS = 0x08, + PDC_DIMM_WINDOW_CTLR = 0x0C, + PDC_TIME_CONTROL = 0x3C, + PDC_TIME_PERIOD = 0x40, + PDC_TIME_COUNTER = 0x44, + PDC_GENERAL_CTLR = 0x484, + PCI_PLL_INIT = 0x8A531824, + PCI_X_TCOUNT = 0xEE1E5CFF +}; + + +struct pdc_port_priv { + u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512]; + u8 *pkt; + dma_addr_t pkt_dma; +}; + +struct pdc_host_priv { + void *dimm_mmio; + + unsigned int doing_hdma; + unsigned int hdma_prod; + unsigned int hdma_cons; + struct { + struct ata_queued_cmd *qc; + unsigned int seq; + unsigned long pkt_ofs; + } hdma[32]; +}; + + +static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static void pdc20621_dma_setup(struct ata_queued_cmd *qc); +static void pdc20621_dma_start(struct ata_queued_cmd *qc); +static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs); +static void pdc_eng_timeout(struct ata_port *ap); +static void pdc_20621_phy_reset (struct ata_port *ap); +static int pdc_port_start(struct ata_port *ap); +static void pdc_port_stop(struct ata_port *ap); +static void pdc20621_fill_sg(struct ata_queued_cmd *qc); +static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf); +static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf); +static void pdc20621_host_stop(struct ata_host_set *host_set); +static inline void pdc_dma_complete (struct ata_port *ap, + struct ata_queued_cmd *qc, int have_err); +static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe); +static int pdc20621_detect_dimm(struct ata_probe_ent *pe); +static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, + u32 device, u32 subaddr, u32 *pdata); +static int pdc20621_prog_dimm0(struct ata_probe_ent *pe); +static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe); +#ifdef ATA_VERBOSE_DEBUG +static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, + void *psource, u32 offset, u32 size); +#endif +static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, + void *psource, u32 offset, u32 size); + + +static Scsi_Host_Template pdc_sata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations pdc_20621_ops = { + .port_disable = ata_port_disable, + .tf_load = pdc_tf_load_mmio, + .tf_read = ata_tf_read_mmio, + .check_status = ata_check_status_mmio, + .exec_command = pdc_exec_command_mmio, + .phy_reset = pdc_20621_phy_reset, + .bmdma_setup = pdc20621_dma_setup, + .bmdma_start = pdc20621_dma_start, + .fill_sg = pdc20621_fill_sg, + .eng_timeout = pdc_eng_timeout, + .irq_handler = pdc20621_interrupt, + .port_start = pdc_port_start, + .port_stop = pdc_port_stop, + .host_stop = pdc20621_host_stop, +}; + +static struct ata_port_info pdc_port_info[] = { + /* board_20621 */ + { + .sht = &pdc_sata_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SRST | ATA_FLAG_MMIO, + .pio_mask = 0x03, /* pio3-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_20621_ops, + }, + +}; + +static struct pci_device_id pdc_sata_pci_tbl[] = { + { PCI_VENDOR_ID_PROMISE, 0x6622, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20621 }, + { } /* terminate list */ +}; + + +static struct pci_driver pdc_sata_pci_driver = { + .name = DRV_NAME, + .id_table = pdc_sata_pci_tbl, + .probe = pdc_sata_init_one, + .remove = ata_pci_remove_one, +}; + + +static void pdc20621_host_stop(struct ata_host_set *host_set) +{ + struct pdc_host_priv *hpriv = host_set->private_data; + void *dimm_mmio = hpriv->dimm_mmio; + + iounmap(dimm_mmio); + kfree(hpriv); +} + +static int pdc_port_start(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct pdc_port_priv *pp; + int rc; + + rc = ata_port_start(ap); + if (rc) + return rc; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + rc = -ENOMEM; + goto err_out; + } + memset(pp, 0, sizeof(*pp)); + + pp->pkt = pci_alloc_consistent(pdev, 128, &pp->pkt_dma); + if (!pp->pkt) { + rc = -ENOMEM; + goto err_out_kfree; + } + + ap->private_data = pp; + + return 0; + +err_out_kfree: + kfree(pp); +err_out: + ata_port_stop(ap); + return rc; +} + + +static void pdc_port_stop(struct ata_port *ap) +{ + struct pci_dev *pdev = ap->host_set->pdev; + struct pdc_port_priv *pp = ap->private_data; + + ap->private_data = NULL; + pci_free_consistent(pdev, 128, pp->pkt, pp->pkt_dma); + kfree(pp); + ata_port_stop(ap); +} + + +static void pdc_20621_phy_reset (struct ata_port *ap) +{ + VPRINTK("ENTER\n"); + ap->cbl = ATA_CBL_SATA; + ata_port_probe(ap); + ata_bus_reset(ap); +} + +static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf, + unsigned int portno, + unsigned int total_len) +{ + u32 addr; + unsigned int dw = PDC_DIMM_APKT_PRD >> 2; + u32 *buf32 = (u32 *) buf; + + /* output ATA packet S/G table */ + addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA + + (PDC_DIMM_DATA_STEP * portno); + VPRINTK("ATA sg addr 0x%x, %d\n", addr, addr); + buf32[dw] = cpu_to_le32(addr); + buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT); + + VPRINTK("ATA PSG @ %x == (0x%x, 0x%x)\n", + PDC_20621_DIMM_BASE + + (PDC_DIMM_WINDOW_STEP * portno) + + PDC_DIMM_APKT_PRD, + buf32[dw], buf32[dw + 1]); +} + +static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf, + unsigned int portno, + unsigned int total_len) +{ + u32 addr; + unsigned int dw = PDC_DIMM_HPKT_PRD >> 2; + u32 *buf32 = (u32 *) buf; + + /* output Host DMA packet S/G table */ + addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA + + (PDC_DIMM_DATA_STEP * portno); + + buf32[dw] = cpu_to_le32(addr); + buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT); + + VPRINTK("HOST PSG @ %x == (0x%x, 0x%x)\n", + PDC_20621_DIMM_BASE + + (PDC_DIMM_WINDOW_STEP * portno) + + PDC_DIMM_HPKT_PRD, + buf32[dw], buf32[dw + 1]); +} + +static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf, + unsigned int devno, u8 *buf, + unsigned int portno) +{ + unsigned int i, dw; + u32 *buf32 = (u32 *) buf; + u8 dev_reg; + + unsigned int dimm_sg = PDC_20621_DIMM_BASE + + (PDC_DIMM_WINDOW_STEP * portno) + + PDC_DIMM_APKT_PRD; + VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg); + + i = PDC_DIMM_ATA_PKT; + + /* + * Set up ATA packet + */ + if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE))) + buf[i++] = PDC_PKT_READ; + else if (tf->protocol == ATA_PROT_NODATA) + buf[i++] = PDC_PKT_NODATA; + else + buf[i++] = 0; + buf[i++] = 0; /* reserved */ + buf[i++] = portno + 1; /* seq. id */ + buf[i++] = 0xff; /* delay seq. id */ + + /* dimm dma S/G, and next-pkt */ + dw = i >> 2; + buf32[dw] = cpu_to_le32(dimm_sg); + buf32[dw + 1] = 0; + i += 8; + + if (devno == 0) + dev_reg = ATA_DEVICE_OBS; + else + dev_reg = ATA_DEVICE_OBS | ATA_DEV1; + + /* select device */ + buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE; + buf[i++] = dev_reg; + + /* device control register */ + buf[i++] = (1 << 5) | PDC_REG_DEVCTL; + buf[i++] = tf->ctl; + + return i; +} + +static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf, + unsigned int portno) +{ + unsigned int dw; + u32 tmp, *buf32 = (u32 *) buf; + + unsigned int host_sg = PDC_20621_DIMM_BASE + + (PDC_DIMM_WINDOW_STEP * portno) + + PDC_DIMM_HOST_PRD; + unsigned int dimm_sg = PDC_20621_DIMM_BASE + + (PDC_DIMM_WINDOW_STEP * portno) + + PDC_DIMM_HPKT_PRD; + VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg); + VPRINTK("host_sg == 0x%x, %d\n", host_sg, host_sg); + + dw = PDC_DIMM_HOST_PKT >> 2; + + /* + * Set up Host DMA packet + */ + if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE))) + tmp = PDC_PKT_READ; + else + tmp = 0; + tmp |= ((portno + 1 + 4) << 16); /* seq. id */ + tmp |= (0xff << 24); /* delay seq. id */ + buf32[dw + 0] = cpu_to_le32(tmp); + buf32[dw + 1] = cpu_to_le32(host_sg); + buf32[dw + 2] = cpu_to_le32(dimm_sg); + buf32[dw + 3] = 0; + + VPRINTK("HOST PKT @ %x == (0x%x 0x%x 0x%x 0x%x)\n", + PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * portno) + + PDC_DIMM_HOST_PKT, + buf32[dw + 0], + buf32[dw + 1], + buf32[dw + 2], + buf32[dw + 3]); +} + +static void pdc20621_fill_sg(struct ata_queued_cmd *qc) +{ + struct scatterlist *sg = qc->sg; + struct ata_port *ap = qc->ap; + struct pdc_port_priv *pp = ap->private_data; + void *mmio = ap->host_set->mmio_base; + struct pdc_host_priv *hpriv = ap->host_set->private_data; + void *dimm_mmio = hpriv->dimm_mmio; + unsigned int portno = ap->port_no; + unsigned int i, last, idx, total_len = 0, sgt_len; + u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ]; + + VPRINTK("ata%u: ENTER\n", ap->id); + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + /* + * Build S/G table + */ + last = qc->n_elem; + idx = 0; + for (i = 0; i < last; i++) { + buf[idx++] = cpu_to_le32(sg_dma_address(&sg[i])); + buf[idx++] = cpu_to_le32(sg_dma_len(&sg[i])); + total_len += sg[i].length; + } + buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT); + sgt_len = idx * 4; + + /* + * Build ATA, host DMA packets + */ + pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); + pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno); + + pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); + i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); + + if (qc->tf.flags & ATA_TFLAG_LBA48) + i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i); + else + i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i); + + pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i); + + /* copy three S/G tables and two packets to DIMM MMIO window */ + memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP), + &pp->dimm_buf, PDC_DIMM_HEADER_SZ); + memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP) + + PDC_DIMM_HOST_PRD, + &pp->dimm_buf[PDC_DIMM_HEADER_SZ], sgt_len); + + /* force host FIFO dump */ + writel(0x00000001, mmio + PDC_20621_GENERAL_CTL); + + readl(dimm_mmio); /* MMIO PCI posting flush */ + + VPRINTK("ata pkt buf ofs %u, prd size %u, mmio copied\n", i, sgt_len); +} + +static void __pdc20621_push_hdma(struct ata_queued_cmd *qc, + unsigned int seq, + u32 pkt_ofs) +{ + struct ata_port *ap = qc->ap; + struct ata_host_set *host_set = ap->host_set; + void *mmio = host_set->mmio_base; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); + readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */ + + writel(pkt_ofs, mmio + PDC_HDMA_PKT_SUBMIT); + readl(mmio + PDC_HDMA_PKT_SUBMIT); /* flush */ +} + +static void pdc20621_push_hdma(struct ata_queued_cmd *qc, + unsigned int seq, + u32 pkt_ofs) +{ + struct ata_port *ap = qc->ap; + struct pdc_host_priv *pp = ap->host_set->private_data; + unsigned int idx = pp->hdma_prod & PDC_HDMA_Q_MASK; + + if (!pp->doing_hdma) { + __pdc20621_push_hdma(qc, seq, pkt_ofs); + pp->doing_hdma = 1; + return; + } + + pp->hdma[idx].qc = qc; + pp->hdma[idx].seq = seq; + pp->hdma[idx].pkt_ofs = pkt_ofs; + pp->hdma_prod++; +} + +static void pdc20621_pop_hdma(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct pdc_host_priv *pp = ap->host_set->private_data; + unsigned int idx = pp->hdma_cons & PDC_HDMA_Q_MASK; + + /* if nothing on queue, we're done */ + if (pp->hdma_prod == pp->hdma_cons) { + pp->doing_hdma = 0; + return; + } + + __pdc20621_push_hdma(pp->hdma[idx].qc, pp->hdma[idx].seq, + pp->hdma[idx].pkt_ofs); + pp->hdma_cons++; +} + +#ifdef ATA_VERBOSE_DEBUG +static void pdc20621_dump_hdma(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int port_no = ap->port_no; + struct pdc_host_priv *hpriv = ap->host_set->private_data; + void *dimm_mmio = hpriv->dimm_mmio; + + dimm_mmio += (port_no * PDC_DIMM_WINDOW_STEP); + dimm_mmio += PDC_DIMM_HOST_PKT; + + printk(KERN_ERR "HDMA[0] == 0x%08X\n", readl(dimm_mmio)); + printk(KERN_ERR "HDMA[1] == 0x%08X\n", readl(dimm_mmio + 4)); + printk(KERN_ERR "HDMA[2] == 0x%08X\n", readl(dimm_mmio + 8)); + printk(KERN_ERR "HDMA[3] == 0x%08X\n", readl(dimm_mmio + 12)); +} +#else +static inline void pdc20621_dump_hdma(struct ata_queued_cmd *qc) { } +#endif /* ATA_VERBOSE_DEBUG */ + +static void pdc20621_dma_setup(struct ata_queued_cmd *qc) +{ + /* nothing for now. later, we will call standard + * code in libata-core for ATAPI here */ +} + +static void pdc20621_dma_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_host_set *host_set = ap->host_set; + unsigned int port_no = ap->port_no; + void *mmio = host_set->mmio_base; + unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); + u8 seq = (u8) (port_no + 1); + unsigned int doing_hdma = 0, port_ofs; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + VPRINTK("ata%u: ENTER\n", ap->id); + + port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); + + /* if writing, we (1) DMA to DIMM, then (2) do ATA command */ + if (rw) { + doing_hdma = 1; + seq += 4; + } + + wmb(); /* flush PRD, pkt writes */ + + if (doing_hdma) { + pdc20621_dump_hdma(qc); + pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT); + VPRINTK("queued ofs 0x%x (%u), seq %u\n", + port_ofs + PDC_DIMM_HOST_PKT, + port_ofs + PDC_DIMM_HOST_PKT, + seq); + } else { + writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); + readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */ + + writel(port_ofs + PDC_DIMM_ATA_PKT, + (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + VPRINTK("submitted ofs 0x%x (%u), seq %u\n", + port_ofs + PDC_DIMM_ATA_PKT, + port_ofs + PDC_DIMM_ATA_PKT, + seq); + } +} + +static inline unsigned int pdc20621_host_intr( struct ata_port *ap, + struct ata_queued_cmd *qc, + unsigned int doing_hdma, + void *mmio) +{ + unsigned int port_no = ap->port_no; + unsigned int port_ofs = + PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no); + u8 status; + unsigned int handled = 0; + + VPRINTK("ENTER\n"); + + if ((qc->tf.protocol == ATA_PROT_DMA) && /* read */ + (!(qc->tf.flags & ATA_TFLAG_WRITE))) { + + /* step two - DMA from DIMM to host */ + if (doing_hdma) { + VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->id, + readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); + pdc_dma_complete(ap, qc, 0); + pdc20621_pop_hdma(qc); + } + + /* step one - exec ATA command */ + else { + u8 seq = (u8) (port_no + 1 + 4); + VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->id, + readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); + + /* submit hdma pkt */ + pdc20621_dump_hdma(qc); + pdc20621_push_hdma(qc, seq, + port_ofs + PDC_DIMM_HOST_PKT); + } + handled = 1; + + } else if (qc->tf.protocol == ATA_PROT_DMA) { /* write */ + + /* step one - DMA from host to DIMM */ + if (doing_hdma) { + u8 seq = (u8) (port_no + 1); + VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->id, + readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); + + /* submit ata pkt */ + writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); + readl(mmio + PDC_20621_SEQCTL + (seq * 4)); + writel(port_ofs + PDC_DIMM_ATA_PKT, + (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + } + + /* step two - execute ATA command */ + else { + VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->id, + readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); + pdc_dma_complete(ap, qc, 0); + pdc20621_pop_hdma(qc); + } + handled = 1; + + /* command completion, but no data xfer */ + } else if (qc->tf.protocol == ATA_PROT_NODATA) { + + status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status); + ata_qc_complete(qc, status); + handled = 1; + + } else { + ap->stats.idle_irq++; + } + + return handled; +} + +static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + struct ata_port *ap; + u32 mask = 0; + unsigned int i, tmp, port_no; + unsigned int handled = 0; + void *mmio_base; + + VPRINTK("ENTER\n"); + + if (!host_set || !host_set->mmio_base) { + VPRINTK("QUICK EXIT\n"); + return IRQ_NONE; + } + + mmio_base = host_set->mmio_base; + + /* reading should also clear interrupts */ + mmio_base += PDC_CHIP0_OFS; + mask = readl(mmio_base + PDC_20621_SEQMASK); + VPRINTK("mask == 0x%x\n", mask); + + if (mask == 0xffffffff) { + VPRINTK("QUICK EXIT 2\n"); + return IRQ_NONE; + } + mask &= 0xffff; /* only 16 tags possible */ + if (!mask) { + VPRINTK("QUICK EXIT 3\n"); + return IRQ_NONE; + } + + spin_lock(&host_set->lock); + + for (i = 1; i < 9; i++) { + port_no = i - 1; + if (port_no > 3) + port_no -= 4; + if (port_no >= host_set->n_ports) + ap = NULL; + else + ap = host_set->ports[port_no]; + tmp = mask & (1 << i); + VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp); + if (tmp && ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && (!(qc->tf.ctl & ATA_NIEN))) + handled += pdc20621_host_intr(ap, qc, (i > 4), + mmio_base); + } + } + + spin_unlock(&host_set->lock); + + VPRINTK("mask == 0x%x\n", mask); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} + +static inline void pdc_dma_complete (struct ata_port *ap, + struct ata_queued_cmd *qc, + int have_err) +{ + u8 err_bit = have_err ? ATA_ERR : 0; + + /* get drive status; clear intr; complete txn */ + ata_qc_complete(qc, ata_wait_idle(ap) | err_bit); +} + +static void pdc_eng_timeout(struct ata_port *ap) +{ + u8 drv_stat; + struct ata_queued_cmd *qc; + + DPRINTK("ENTER\n"); + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + goto out; + } + + /* hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + qc->scsidone = scsi_finish_command; + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + printk(KERN_ERR "ata%u: DMA timeout\n", ap->id); + ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR); + break; + + case ATA_PROT_NODATA: + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + + printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x\n", + ap->id, qc->tf.command, drv_stat); + + ata_qc_complete(qc, drv_stat); + break; + + default: + drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); + + printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n", + ap->id, qc->tf.command, drv_stat); + + ata_qc_complete(qc, drv_stat); + break; + } + +out: + DPRINTK("EXIT\n"); +} + +static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + if (tf->protocol != ATA_PROT_DMA) + ata_tf_load_mmio(ap, tf); +} + + +static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) +{ + if (tf->protocol != ATA_PROT_DMA) + ata_exec_command_mmio(ap, tf); +} + + +static void pdc_sata_setup_port(struct ata_ioports *port, unsigned long base) +{ + port->cmd_addr = base; + port->data_addr = base; + port->feature_addr = + port->error_addr = base + 0x4; + port->nsect_addr = base + 0x8; + port->lbal_addr = base + 0xc; + port->lbam_addr = base + 0x10; + port->lbah_addr = base + 0x14; + port->device_addr = base + 0x18; + port->command_addr = + port->status_addr = base + 0x1c; + port->altstatus_addr = + port->ctl_addr = base + 0x38; +} + + +#ifdef ATA_VERBOSE_DEBUG +static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, + u32 offset, u32 size) +{ + u32 window_size; + u16 idx; + u8 page_mask; + long dist; + void *mmio = pe->mmio_base; + struct pdc_host_priv *hpriv = pe->private_data; + void *dimm_mmio = hpriv->dimm_mmio; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + page_mask = 0x00; + window_size = 0x2000 * 4; /* 32K byte uchar size */ + idx = (u16) (offset / window_size); + + writel(0x01, mmio + PDC_GENERAL_CTLR); + readl(mmio + PDC_GENERAL_CTLR); + writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); + readl(mmio + PDC_DIMM_WINDOW_CTLR); + + offset -= (idx * window_size); + idx++; + dist = ((long) (window_size - (offset + size))) >= 0 ? size : + (long) (window_size - offset); + memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), + dist); + + psource += dist; + size -= dist; + for (; (long) size >= (long) window_size ;) { + writel(0x01, mmio + PDC_GENERAL_CTLR); + readl(mmio + PDC_GENERAL_CTLR); + writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); + readl(mmio + PDC_DIMM_WINDOW_CTLR); + memcpy_fromio((char *) psource, (char *) (dimm_mmio), + window_size / 4); + psource += window_size; + size -= window_size; + idx ++; + } + + if (size) { + writel(0x01, mmio + PDC_GENERAL_CTLR); + readl(mmio + PDC_GENERAL_CTLR); + writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); + readl(mmio + PDC_DIMM_WINDOW_CTLR); + memcpy_fromio((char *) psource, (char *) (dimm_mmio), + size / 4); + } +} +#endif + + +static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, + u32 offset, u32 size) +{ + u32 window_size; + u16 idx; + u8 page_mask; + long dist; + void *mmio = pe->mmio_base; + struct pdc_host_priv *hpriv = pe->private_data; + void *dimm_mmio = hpriv->dimm_mmio; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + page_mask = 0x00; + window_size = 0x2000 * 4; /* 32K byte uchar size */ + idx = (u16) (offset / window_size); + + writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); + readl(mmio + PDC_DIMM_WINDOW_CTLR); + offset -= (idx * window_size); + idx++; + dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size : + (long) (window_size - offset); + memcpy_toio((char *) (dimm_mmio + offset / 4), (char *) psource, dist); + writel(0x01, mmio + PDC_GENERAL_CTLR); + readl(mmio + PDC_GENERAL_CTLR); + + psource += dist; + size -= dist; + for (; (long) size >= (long) window_size ;) { + writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); + readl(mmio + PDC_DIMM_WINDOW_CTLR); + memcpy_toio((char *) (dimm_mmio), (char *) psource, + window_size / 4); + writel(0x01, mmio + PDC_GENERAL_CTLR); + readl(mmio + PDC_GENERAL_CTLR); + psource += window_size; + size -= window_size; + idx ++; + } + + if (size) { + writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); + readl(mmio + PDC_DIMM_WINDOW_CTLR); + memcpy_toio((char *) (dimm_mmio), (char *) psource, size / 4); + writel(0x01, mmio + PDC_GENERAL_CTLR); + readl(mmio + PDC_GENERAL_CTLR); + } +} + + +static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, + u32 subaddr, u32 *pdata) +{ + void *mmio = pe->mmio_base; + u32 i2creg = 0; + u32 status; + u32 count =0; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + i2creg |= device << 24; + i2creg |= subaddr << 16; + + /* Set the device and subaddress */ + writel(i2creg, mmio + PDC_I2C_ADDR_DATA_OFFSET); + readl(mmio + PDC_I2C_ADDR_DATA_OFFSET); + + /* Write Control to perform read operation, mask int */ + writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT, + mmio + PDC_I2C_CONTROL_OFFSET); + + for (count = 0; count <= 1000; count ++) { + status = readl(mmio + PDC_I2C_CONTROL_OFFSET); + if (status & PDC_I2C_COMPLETE) { + status = readl(mmio + PDC_I2C_ADDR_DATA_OFFSET); + break; + } else if (count == 1000) + return 0; + } + + *pdata = (status >> 8) & 0x000000ff; + return 1; +} + + +static int pdc20621_detect_dimm(struct ata_probe_ent *pe) +{ + u32 data=0 ; + if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + PDC_DIMM_SPD_SYSTEM_FREQ, &data)) { + if (data == 100) + return 100; + } else + return 0; + + if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { + if(data <= 0x75) + return 133; + } else + return 0; + + return 0; +} + + +static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) +{ + u32 spd0[50]; + u32 data = 0; + int size, i; + u8 bdimmsize; + void *mmio = pe->mmio_base; + static const struct { + unsigned int reg; + unsigned int ofs; + } pdc_i2c_read_data [] = { + { PDC_DIMM_SPD_TYPE, 11 }, + { PDC_DIMM_SPD_FRESH_RATE, 12 }, + { PDC_DIMM_SPD_COLUMN_NUM, 4 }, + { PDC_DIMM_SPD_ATTRIBUTE, 21 }, + { PDC_DIMM_SPD_ROW_NUM, 3 }, + { PDC_DIMM_SPD_BANK_NUM, 17 }, + { PDC_DIMM_SPD_MODULE_ROW, 5 }, + { PDC_DIMM_SPD_ROW_PRE_CHARGE, 27 }, + { PDC_DIMM_SPD_ROW_ACTIVE_DELAY, 28 }, + { PDC_DIMM_SPD_RAS_CAS_DELAY, 29 }, + { PDC_DIMM_SPD_ACTIVE_PRECHARGE, 30 }, + { PDC_DIMM_SPD_CAS_LATENCY, 18 }, + }; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + for(i=0; i spd0[28]) + ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10; + data |= ((spd0[30] - spd0[29] + 9) / 10 - 2) << 12; + + if (spd0[18] & 0x08) + data |= ((0x03) << 14); + else if (spd0[18] & 0x04) + data |= ((0x02) << 14); + else if (spd0[18] & 0x01) + data |= ((0x01) << 14); + else + data |= (0 << 14); + + /* + Calculate the size of bDIMMSize (power of 2) and + merge the DIMM size by program start/end address. + */ + + bdimmsize = spd0[4] + (spd0[5] / 2) + spd0[3] + (spd0[17] / 2) + 3; + size = (1 << bdimmsize) >> 20; /* size = xxx(MB) */ + data |= (((size / 16) - 1) << 16); + data |= (0 << 23); + data |= 8; + writel(data, mmio + PDC_DIMM0_CONTROL_OFFSET); + readl(mmio + PDC_DIMM0_CONTROL_OFFSET); + return size; +} + + +static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) +{ + u32 data, spd0; + int error, i; + void *mmio = pe->mmio_base; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + /* + Set To Default : DIMM Module Global Control Register (0x022259F1) + DIMM Arbitration Disable (bit 20) + DIMM Data/Control Output Driving Selection (bit12 - bit15) + Refresh Enable (bit 17) + */ + + data = 0x022259F1; + writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); + readl(mmio + PDC_SDRAM_CONTROL_OFFSET); + + /* Turn on for ECC */ + pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + PDC_DIMM_SPD_TYPE, &spd0); + if (spd0 == 0x02) { + data |= (0x01 << 16); + writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); + readl(mmio + PDC_SDRAM_CONTROL_OFFSET); + printk(KERN_ERR "Local DIMM ECC Enabled\n"); + } + + /* DIMM Initialization Select/Enable (bit 18/19) */ + data &= (~(1<<18)); + data |= (1<<19); + writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); + + error = 1; + for (i = 1; i <= 10; i++) { /* polling ~5 secs */ + data = readl(mmio + PDC_SDRAM_CONTROL_OFFSET); + if (!(data & (1<<19))) { + error = 0; + break; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((i * 100) * HZ / 1000 + 1); + } + return error; +} + + +static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) +{ + int speed, size, length; + u32 addr,spd0,pci_status; + u32 tmp=0; + u32 time_period=0; + u32 tcount=0; + u32 ticks=0; + u32 clock=0; + u32 fparam=0; + void *mmio = pe->mmio_base; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + /* Initialize PLL based upon PCI Bus Frequency */ + + /* Initialize Time Period Register */ + writel(0xffffffff, mmio + PDC_TIME_PERIOD); + time_period = readl(mmio + PDC_TIME_PERIOD); + VPRINTK("Time Period Register (0x40): 0x%x\n", time_period); + + /* Enable timer */ + writel(0x00001a0, mmio + PDC_TIME_CONTROL); + readl(mmio + PDC_TIME_CONTROL); + + /* Wait 3 seconds */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(3 * HZ); + + /* + When timer is enabled, counter is decreased every internal + clock cycle. + */ + + tcount = readl(mmio + PDC_TIME_COUNTER); + VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount); + + /* + If SX4 is on PCI-X bus, after 3 seconds, the timer counter + register should be >= (0xffffffff - 3x10^8). + */ + if(tcount >= PCI_X_TCOUNT) { + ticks = (time_period - tcount); + VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks); + + clock = (ticks / 300000); + VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock); + + clock = (clock * 33); + VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock); + + /* PLL F Param (bit 22:16) */ + fparam = (1400000 / clock) - 2; + VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam); + + /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */ + pci_status = (0x8a001824 | (fparam << 16)); + } else + pci_status = PCI_PLL_INIT; + + /* Initialize PLL. */ + VPRINTK("pci_status: 0x%x\n", pci_status); + writel(pci_status, mmio + PDC_CTL_STATUS); + readl(mmio + PDC_CTL_STATUS); + + /* + Read SPD of DIMM by I2C interface, + and program the DIMM Module Controller. + */ + if (!(speed = pdc20621_detect_dimm(pe))) { + printk(KERN_ERR "Detect Local DIMM Fail\n"); + return 1; /* DIMM error */ + } + VPRINTK("Local DIMM Speed = %d\n", speed); + + /* Programming DIMM0 Module Control Register (index_CID0:80h) */ + size = pdc20621_prog_dimm0(pe); + VPRINTK("Local DIMM Size = %dMB\n",size); + + /* Programming DIMM Module Global Control Register (index_CID0:88h) */ + if (pdc20621_prog_dimm_global(pe)) { + printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n"); + return 1; + } + +#ifdef ATA_VERBOSE_DEBUG + { + u8 test_parttern1[40] = {0x55,0xAA,'P','r','o','m','i','s','e',' ', + 'N','o','t',' ','Y','e','t',' ','D','e','f','i','n','e','d',' ', + '1','.','1','0', + '9','8','0','3','1','6','1','2',0,0}; + u8 test_parttern2[40] = {0}; + + pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40); + pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40); + + pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40); + pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); + printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], + test_parttern2[1], &(test_parttern2[2])); + pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040, + 40); + printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], + test_parttern2[1], &(test_parttern2[2])); + + pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40); + pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); + printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], + test_parttern2[1], &(test_parttern2[2])); + } +#endif + + /* ECC initiliazation. */ + + pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + PDC_DIMM_SPD_TYPE, &spd0); + if (spd0 == 0x02) { + VPRINTK("Start ECC initialization\n"); + addr = 0; + length = size * 1024 * 1024; + while (addr < length) { + pdc20621_put_to_dimm(pe, (void *) &tmp, addr, + sizeof(u32)); + addr += sizeof(u32); + } + VPRINTK("Finish ECC initialization\n"); + } + return 0; +} + + +static void pdc_20621_init(struct ata_probe_ent *pe) +{ + u32 tmp; + void *mmio = pe->mmio_base; + + /* hard-code chip #0 */ + mmio += PDC_CHIP0_OFS; + + /* + * Select page 0x40 for our 32k DIMM window + */ + tmp = readl(mmio + PDC_20621_DIMM_WINDOW) & 0xffff0000; + tmp |= PDC_PAGE_WINDOW; /* page 40h; arbitrarily selected */ + writel(tmp, mmio + PDC_20621_DIMM_WINDOW); + + /* + * Reset Host DMA + */ + tmp = readl(mmio + PDC_HDMA_CTLSTAT); + tmp |= PDC_RESET; + writel(tmp, mmio + PDC_HDMA_CTLSTAT); + readl(mmio + PDC_HDMA_CTLSTAT); /* flush */ + + udelay(10); + + tmp = readl(mmio + PDC_HDMA_CTLSTAT); + tmp &= ~PDC_RESET; + writel(tmp, mmio + PDC_HDMA_CTLSTAT); + readl(mmio + PDC_HDMA_CTLSTAT); /* flush */ +} + +static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + unsigned long base; + void *mmio_base, *dimm_mmio = NULL; + struct pdc_host_priv *hpriv = NULL; + unsigned int board_idx = (unsigned int) ent->driver_data; + int rc; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + /* + * If this driver happens to only be useful on Apple's K2, then + * we should check that here as it has a normal Serverworks ID + */ + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = ioremap(pci_resource_start(pdev, 3), + pci_resource_len(pdev, 3)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + base = (unsigned long) mmio_base; + + hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto err_out_iounmap; + } + memset(hpriv, 0, sizeof(*hpriv)); + + dimm_mmio = ioremap(pci_resource_start(pdev, 4), + pci_resource_len(pdev, 4)); + if (!dimm_mmio) { + kfree(hpriv); + rc = -ENOMEM; + goto err_out_iounmap; + } + + hpriv->dimm_mmio = dimm_mmio; + + probe_ent->sht = pdc_port_info[board_idx].sht; + probe_ent->host_flags = pdc_port_info[board_idx].host_flags; + probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; + probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; + probe_ent->port_ops = pdc_port_info[board_idx].port_ops; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + + probe_ent->private_data = hpriv; + base += PDC_CHIP0_OFS; + + pdc_sata_setup_port(&probe_ent->port[0], base + 0x200); + pdc_sata_setup_port(&probe_ent->port[1], base + 0x280); + + /* notice 4-port boards */ + switch (board_idx) { + case board_20621: + probe_ent->n_ports = 4; + + pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); + pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); + break; + default: + BUG(); + break; + } + + pci_set_master(pdev); + + /* initialize adapter */ + /* initialize local dimm */ + if (pdc20621_dimm_init(probe_ent)) { + rc = -ENOMEM; + goto err_out_iounmap_dimm; + } + pdc_20621_init(probe_ent); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_iounmap_dimm: /* only get to this label if 20621 */ + kfree(hpriv); + iounmap(dimm_mmio); +err_out_iounmap: + iounmap(mmio_base); +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + + +static int __init pdc_sata_init(void) +{ + int rc; + + rc = pci_module_init(&pdc_sata_pci_driver); + if (rc) + return rc; + + rc = scsi_register_module(MODULE_SCSI_HA, &pdc_sata_sht); + if (rc) { + rc = -ENODEV; + goto err_out; + } + + return 0; + +err_out: + pci_unregister_driver(&pdc_sata_pci_driver); + return rc; +} + + +static void __exit pdc_sata_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &pdc_sata_sht); + pci_unregister_driver(&pdc_sata_pci_driver); +} + + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("Promise SATA low-level driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); + +module_init(pdc_sata_init); +module_exit(pdc_sata_exit); diff -urN linux-2.4.26/drivers/scsi/sata_via.c linux-2.4.27/drivers/scsi/sata_via.c --- linux-2.4.26/drivers/scsi/sata_via.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_via.c 2004-08-07 16:26:05.532382855 -0700 @@ -0,0 +1,303 @@ +/* + sata_via.c - VIA Serial ATA controllers + + Maintained by: Jeff Garzik + Please ALWAYS copy linux-ide@vger.kernel.org + on emails. + + Copyright 2003-2004 Red Hat, Inc. All rights reserved. + Copyright 2003-2004 Jeff Garzik + + The contents of this file are subject to the Open + Software License version 1.1 that can be found at + http://www.opensource.org/licenses/osl-1.1.txt and is included herein + by reference. + + Alternatively, the contents of this file may be used under the terms + of the GNU General Public License version 2 (the "GPL") as distributed + in the kernel source COPYING file, in which case the provisions of + the GPL are applicable instead of the above. If you wish to allow + the use of your version of this file only under the terms of the + GPL and not to allow others to use your version of this file under + the OSL, indicate your decision by deleting the provisions above and + replace them with the notice and other provisions required by the GPL. + If you do not delete the provisions above, a recipient may use your + version of this file under either the OSL or the GPL. + + */ + +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include +#include + +#define DRV_NAME "sata_via" +#define DRV_VERSION "0.20" + +enum { + via_sata = 0, + + SATA_CHAN_ENAB = 0x40, /* SATA channel enable */ + SATA_INT_GATE = 0x41, /* SATA interrupt gating */ + SATA_NATIVE_MODE = 0x42, /* Native mode enable */ + SATA_PATA_SHARING = 0x49, /* PATA/SATA sharing func ctrl */ + + PORT0 = (1 << 1), + PORT1 = (1 << 0), + + ENAB_ALL = PORT0 | PORT1, + + INT_GATE_ALL = PORT0 | PORT1, + + NATIVE_MODE_ALL = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4), + + SATA_EXT_PHY = (1 << 6), /* 0==use PATA, 1==ext phy */ + SATA_2DEV = (1 << 5), /* SATA is master/slave */ +}; + +static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); +static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg); +static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); + +static struct pci_device_id svia_pci_tbl[] = { + { 0x1106, 0x3149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, via_sata }, + + { } /* terminate list */ +}; + +static struct pci_driver svia_pci_driver = { + .name = DRV_NAME, + .id_table = svia_pci_tbl, + .probe = svia_init_one, + .remove = ata_pci_remove_one, +}; + +static Scsi_Host_Template svia_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .bios_param = ata_std_bios_param, +}; + +static struct ata_port_operations svia_sata_ops = { + .port_disable = ata_port_disable, + + .tf_load = ata_tf_load_pio, + .tf_read = ata_tf_read_pio, + .check_status = ata_check_status_pio, + .exec_command = ata_exec_command_pio, + + .phy_reset = sata_phy_reset, + + .bmdma_setup = ata_bmdma_setup_pio, + .bmdma_start = ata_bmdma_start_pio, + .fill_sg = ata_fill_sg, + .eng_timeout = ata_eng_timeout, + + .irq_handler = ata_interrupt, + + .scr_read = svia_scr_read, + .scr_write = svia_scr_write, + + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +MODULE_AUTHOR("Jeff Garzik"); +MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, svia_pci_tbl); + +static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + return inl(ap->ioaddr.scr_addr + (4 * sc_reg)); +} + +static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) +{ + if (sc_reg > SCR_CONTROL) + return; + outl(val, ap->ioaddr.scr_addr + (4 * sc_reg)); +} + +static const unsigned int svia_bar_sizes[] = { + 8, 4, 8, 4, 16, 256 +}; + +static unsigned long svia_scr_addr(unsigned long addr, unsigned int port) +{ + return addr + (port * 128); +} + +static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + unsigned int i; + int rc; + struct ata_probe_ent *probe_ent; + u8 tmp8; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8); + if (tmp8 & SATA_2DEV) { + printk(KERN_ERR DRV_NAME "(%s): SATA master/slave not supported (0x%x)\n", + pci_name(pdev), (int) tmp8); + rc = -EIO; + goto err_out_regions; + } + + for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++) + if ((pci_resource_start(pdev, i) == 0) || + (pci_resource_len(pdev, i) < svia_bar_sizes[i])) { + printk(KERN_ERR DRV_NAME "(%s): invalid PCI BAR %u (sz 0x%lx, val 0x%lx)\n", + pci_name(pdev), i, + pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); + rc = -ENODEV; + goto err_out_regions; + } + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent) { + printk(KERN_ERR DRV_NAME "(%s): out of memory\n", + pci_name(pdev)); + rc = -ENOMEM; + goto err_out_regions; + } + memset(probe_ent, 0, sizeof(*probe_ent)); + INIT_LIST_HEAD(&probe_ent->node); + probe_ent->pdev = pdev; + probe_ent->sht = &svia_sht; + probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_SRST | + ATA_FLAG_NO_LEGACY; + probe_ent->port_ops = &svia_sata_ops; + probe_ent->n_ports = 2; + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->pio_mask = 0x1f; + probe_ent->udma_mask = 0x7f; + + probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0); + ata_std_ports(&probe_ent->port[0]); + probe_ent->port[0].altstatus_addr = + probe_ent->port[0].ctl_addr = + pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; + probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4); + probe_ent->port[0].scr_addr = + svia_scr_addr(pci_resource_start(pdev, 5), 0); + + probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2); + ata_std_ports(&probe_ent->port[1]); + probe_ent->port[1].altstatus_addr = + probe_ent->port[1].ctl_addr = + pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; + probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8; + probe_ent->port[1].scr_addr = + svia_scr_addr(pci_resource_start(pdev, 5), 1); + + pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8); + printk(KERN_INFO DRV_NAME "(%s): routed to hard irq line %d\n", + pci_name(pdev), + (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f); + + /* make sure SATA channels are enabled */ + pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8); + if ((tmp8 & ENAB_ALL) != ENAB_ALL) { + printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channels (0x%x)\n", + pci_name(pdev), (int) tmp8); + tmp8 |= ENAB_ALL; + pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8); + } + + /* make sure interrupts for each channel sent to us */ + pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8); + if ((tmp8 & INT_GATE_ALL) != INT_GATE_ALL) { + printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel interrupts (0x%x)\n", + pci_name(pdev), (int) tmp8); + tmp8 |= INT_GATE_ALL; + pci_write_config_byte(pdev, SATA_INT_GATE, tmp8); + } + + /* make sure native mode is enabled */ + pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8); + if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) { + printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel native mode (0x%x)\n", + pci_name(pdev), (int) tmp8); + tmp8 |= NATIVE_MODE_ALL; + pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8); + } + + pci_set_master(pdev); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + +static int __init svia_init(void) +{ + int rc; + + rc = pci_module_init(&svia_pci_driver); + if (rc) + return rc; + + rc = scsi_register_module(MODULE_SCSI_HA, &svia_sht); + if (rc) { + pci_unregister_driver(&svia_pci_driver); + /* TODO: does scsi_register_module return errno val? */ + return -ENODEV; + } + + return 0; +} + +static void __exit svia_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &svia_sht); + pci_unregister_driver(&svia_pci_driver); +} + +module_init(svia_init); +module_exit(svia_exit); + diff -urN linux-2.4.26/drivers/scsi/sata_vsc.c linux-2.4.27/drivers/scsi/sata_vsc.c --- linux-2.4.26/drivers/scsi/sata_vsc.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/scsi/sata_vsc.c 2004-08-07 16:26:05.533382896 -0700 @@ -0,0 +1,401 @@ +/* + * sata_vsc.c - Vitesse VSC7174 4 port DPA SATA + * + * Maintained by: Jeremy Higdon @ SGI + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2004 SGI + * + * Bits from Jeff Garzik, Copyright RedHat, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "scsi.h" +#include +#include + +#define DRV_NAME "sata_vsc" +#define DRV_VERSION "0.01" + +/* Interrupt register offsets (from chip base address) */ +#define VSC_SATA_INT_STAT_OFFSET 0x00 +#define VSC_SATA_INT_MASK_OFFSET 0x04 + +/* Taskfile registers offsets */ +#define VSC_SATA_TF_CMD_OFFSET 0x00 +#define VSC_SATA_TF_DATA_OFFSET 0x00 +#define VSC_SATA_TF_ERROR_OFFSET 0x04 +#define VSC_SATA_TF_FEATURE_OFFSET 0x06 +#define VSC_SATA_TF_NSECT_OFFSET 0x08 +#define VSC_SATA_TF_LBAL_OFFSET 0x0c +#define VSC_SATA_TF_LBAM_OFFSET 0x10 +#define VSC_SATA_TF_LBAH_OFFSET 0x14 +#define VSC_SATA_TF_DEVICE_OFFSET 0x18 +#define VSC_SATA_TF_STATUS_OFFSET 0x1c +#define VSC_SATA_TF_COMMAND_OFFSET 0x1d +#define VSC_SATA_TF_ALTSTATUS_OFFSET 0x28 +#define VSC_SATA_TF_CTL_OFFSET 0x29 + +/* DMA base */ +#define VSC_SATA_UP_DESCRIPTOR_OFFSET 0x64 +#define VSC_SATA_UP_DATA_BUFFER_OFFSET 0x6C +#define VSC_SATA_DMA_CMD_OFFSET 0x70 + +/* SCRs base */ +#define VSC_SATA_SCR_STATUS_OFFSET 0x100 +#define VSC_SATA_SCR_ERROR_OFFSET 0x104 +#define VSC_SATA_SCR_CONTROL_OFFSET 0x108 + +/* Port stride */ +#define VSC_SATA_PORT_OFFSET 0x200 + + +static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) +{ + if (sc_reg > SCR_CONTROL) + return 0xffffffffU; + return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, + u32 val) +{ + if (sc_reg > SCR_CONTROL) + return; + writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); +} + + +static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) +{ + unsigned long mask_addr; + u8 mask; + + mask_addr = (unsigned long) ap->host_set->mmio_base + + VSC_SATA_INT_MASK_OFFSET + ap->port_no; + mask = readb(mask_addr); + if (ctl & ATA_NIEN) + mask |= 0x80; + else + mask &= 0x7F; + writeb(mask, mask_addr); +} + + +static void vsc_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + /* + * The only thing the ctl register is used for is SRST. + * That is not enabled or disabled via tf_load. + * However, if ATA_NIEN is changed, then we need to change the interrupt register. + */ + if ((tf->ctl & ATA_NIEN) != (ap->last_ctl & ATA_NIEN)) { + ap->last_ctl = tf->ctl; + vsc_intr_mask_update(ap, tf->ctl & ATA_NIEN); + } + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + writew(tf->feature | (((u16)tf->hob_feature) << 8), ioaddr->feature_addr); + writew(tf->nsect | (((u16)tf->hob_nsect) << 8), ioaddr->nsect_addr); + writew(tf->lbal | (((u16)tf->hob_lbal) << 8), ioaddr->lbal_addr); + writew(tf->lbam | (((u16)tf->hob_lbam) << 8), ioaddr->lbam_addr); + writew(tf->lbah | (((u16)tf->hob_lbah) << 8), ioaddr->lbah_addr); + } else if (is_addr) { + writew(tf->feature, ioaddr->feature_addr); + writew(tf->nsect, ioaddr->nsect_addr); + writew(tf->lbal, ioaddr->lbal_addr); + writew(tf->lbam, ioaddr->lbam_addr); + writew(tf->lbah, ioaddr->lbah_addr); + } + + if (tf->flags & ATA_TFLAG_DEVICE) + writeb(tf->device, ioaddr->device_addr); + + ata_wait_idle(ap); +} + + +static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u16 nsect, lbal, lbam, lbah; + + nsect = tf->nsect = readw(ioaddr->nsect_addr); + lbal = tf->lbal = readw(ioaddr->lbal_addr); + lbam = tf->lbam = readw(ioaddr->lbam_addr); + lbah = tf->lbah = readw(ioaddr->lbah_addr); + tf->device = readw(ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + tf->hob_feature = readb(ioaddr->error_addr); + tf->hob_nsect = nsect >> 8; + tf->hob_lbal = lbal >> 8; + tf->hob_lbam = lbam >> 8; + tf->hob_lbah = lbah >> 8; + } +} + + +/* + * vsc_sata_interrupt + * + * Read the interrupt register and process for the devices that have them pending. + */ +irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + unsigned int i; + unsigned int handled = 0; + u32 int_status; + + spin_lock(&host_set->lock); + + int_status = readl(host_set->mmio_base + VSC_SATA_INT_STAT_OFFSET); + + for (i = 0; i < host_set->n_ports; i++) { + if (int_status & ((u32) 0xFF << (8 * i))) { + struct ata_port *ap; + + ap = host_set->ports[i]; + if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && (!(qc->tf.ctl & ATA_NIEN))) + handled += ata_host_intr(ap, qc); + } + } + } + + spin_unlock(&host_set->lock); + + return IRQ_RETVAL(handled); +} + + +static Scsi_Host_Template vsc_sata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .detect = ata_scsi_detect, + .release = ata_scsi_release, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .use_new_eh_code = ATA_SHT_NEW_EH_CODE, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .bios_param = ata_std_bios_param, +}; + + +static struct ata_port_operations vsc_sata_ops = { + .port_disable = ata_port_disable, + .tf_load = vsc_sata_tf_load, + .tf_read = vsc_sata_tf_read, + .exec_command = ata_exec_command_mmio, + .check_status = ata_check_status_mmio, + .phy_reset = sata_phy_reset, + .bmdma_setup = ata_bmdma_setup_mmio, + .bmdma_start = ata_bmdma_start_mmio, + .fill_sg = ata_fill_sg, + .eng_timeout = ata_eng_timeout, + .irq_handler = vsc_sata_interrupt, + .scr_read = vsc_sata_scr_read, + .scr_write = vsc_sata_scr_write, + .port_start = ata_port_start, + .port_stop = ata_port_stop, +}; + +static void __devinit vsc_sata_setup_port(struct ata_ioports *port, unsigned long base) +{ + port->cmd_addr = base + VSC_SATA_TF_CMD_OFFSET; + port->data_addr = base + VSC_SATA_TF_DATA_OFFSET; + port->error_addr = base + VSC_SATA_TF_ERROR_OFFSET; + port->feature_addr = base + VSC_SATA_TF_FEATURE_OFFSET; + port->nsect_addr = base + VSC_SATA_TF_NSECT_OFFSET; + port->lbal_addr = base + VSC_SATA_TF_LBAL_OFFSET; + port->lbam_addr = base + VSC_SATA_TF_LBAM_OFFSET; + port->lbah_addr = base + VSC_SATA_TF_LBAH_OFFSET; + port->device_addr = base + VSC_SATA_TF_DEVICE_OFFSET; + port->status_addr = base + VSC_SATA_TF_STATUS_OFFSET; + port->command_addr = base + VSC_SATA_TF_COMMAND_OFFSET; + port->altstatus_addr = base + VSC_SATA_TF_ALTSTATUS_OFFSET; + port->ctl_addr = base + VSC_SATA_TF_CTL_OFFSET; + port->bmdma_addr = base + VSC_SATA_DMA_CMD_OFFSET; + port->scr_addr = base + VSC_SATA_SCR_STATUS_OFFSET; + writel(0, base + VSC_SATA_UP_DESCRIPTOR_OFFSET); + writel(0, base + VSC_SATA_UP_DATA_BUFFER_OFFSET); +} + + +static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + unsigned long base; + void *mmio_base; + int rc; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + /* + * Check if we have needed resource mapped. + */ + if (pci_resource_len(pdev, 0) == 0) { + rc = -ENODEV; + goto err_out; + } + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + /* + * Use 32 bit DMA mask, because 64 bit address support is poor. + */ + rc = pci_set_dma_mask(pdev, 0xFFFFFFFFULL); + if (rc) + goto err_out_regions; + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->pdev = pdev; + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + base = (unsigned long) mmio_base; + + /* + * Due to a bug in the chip, the default cache line size can't be used + */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80); + + probe_ent->sht = &vsc_sata_sht; + probe_ent->host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_SATA_RESET; + probe_ent->port_ops = &vsc_sata_ops; + probe_ent->n_ports = 4; + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + + /* We don't care much about the PIO/UDMA masks, but the core won't like us + * if we don't fill these + */ + probe_ent->pio_mask = 0x1f; + probe_ent->udma_mask = 0x7f; + + /* We have 4 ports per PCI function */ + vsc_sata_setup_port(&probe_ent->port[0], base + 1 * VSC_SATA_PORT_OFFSET); + vsc_sata_setup_port(&probe_ent->port[1], base + 2 * VSC_SATA_PORT_OFFSET); + vsc_sata_setup_port(&probe_ent->port[2], base + 3 * VSC_SATA_PORT_OFFSET); + vsc_sata_setup_port(&probe_ent->port[3], base + 4 * VSC_SATA_PORT_OFFSET); + + pci_set_master(pdev); + + ata_add_to_probe_list(probe_ent); + + return 0; + +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + + +/* + * 0x1725/0x7174 is the Vitesse VSC-7174 + * 0x8086/0x3200 is the Intel 31244, which is supposed to be identical + * compatibility is untested as of yet + */ +static struct pci_device_id vsc_sata_pci_tbl[] = { + { 0x1725, 0x7174, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, + { 0x8086, 0x3200, PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 }, + { } +}; + + +static struct pci_driver vsc_sata_pci_driver = { + .name = DRV_NAME, + .id_table = vsc_sata_pci_tbl, + .probe = vsc_sata_init_one, + .remove = ata_pci_remove_one, +}; + + +static int __init vsc_sata_init(void) +{ + int rc; + + DPRINTK("pci_module_init\n"); + rc = pci_module_init(&vsc_sata_pci_driver); + if (rc) + return rc; + + DPRINTK("scsi_register_host\n"); + rc = scsi_register_module(MODULE_SCSI_HA, &vsc_sata_sht); + if (rc) { + rc = -ENODEV; + goto err_out; + } + + DPRINTK("done\n"); + return 0; + +err_out: + pci_unregister_driver(&vsc_sata_pci_driver); + return rc; +} + + +static void __exit vsc_sata_exit(void) +{ + scsi_unregister_module(MODULE_SCSI_HA, &vsc_sata_sht); + pci_unregister_driver(&vsc_sata_pci_driver); +} + + +MODULE_AUTHOR("Jeremy Higdon"); +MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, vsc_sata_pci_tbl); + +module_init(vsc_sata_init); +module_exit(vsc_sata_exit); diff -urN linux-2.4.26/drivers/scsi/scsi_scan.c linux-2.4.27/drivers/scsi/scsi_scan.c --- linux-2.4.26/drivers/scsi/scsi_scan.c 2004-04-14 06:05:31.000000000 -0700 +++ linux-2.4.27/drivers/scsi/scsi_scan.c 2004-08-07 16:26:05.534382937 -0700 @@ -148,7 +148,7 @@ {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN}, {"CANON", "IPUBJD", "*", BLIST_SPARSELUN}, {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN}, - {"DEC","HSG80","*", BLIST_FORCELUN | BLIST_NOSTARTONADD}, + {"DEC","HSG80","*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_NOSTARTONADD}, {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, {"COMPAQ","CR3500","*", BLIST_FORCELUN}, {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, diff -urN linux-2.4.26/drivers/scsi/st.c linux-2.4.27/drivers/scsi/st.c --- linux-2.4.26/drivers/scsi/st.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/scsi/st.c 2004-08-07 16:26:05.537383060 -0700 @@ -1202,6 +1202,7 @@ ST_mode *STm; ST_partstat *STps; int dev = TAPE_NR(inode->i_rdev); + loff_t pos = *ppos; read_lock(&st_dev_arr_lock); STp = scsi_tapes[dev]; @@ -1433,7 +1434,7 @@ residual *= STp->block_size; if (residual <= do_count) { /* Within the data in this write() */ - filp->f_pos += do_count - residual; + pos += do_count - residual; count -= do_count - residual; if (STps->drv_block >= 0) { if (STp->block_size == 0 && @@ -1489,7 +1490,7 @@ retval = total - count; goto out; } - filp->f_pos += do_count; + pos += do_count; b_point += do_count; count -= do_count; if (STps->drv_block >= 0) { @@ -1508,7 +1509,7 @@ retval = i; goto out; } - filp->f_pos += count; + pos += count; count = 0; } @@ -1543,6 +1544,7 @@ retval = total - count; out: + *ppos = pos; if (SRpnt != NULL) scsi_release_request(SRpnt); up(&STp->lock); @@ -1743,6 +1745,7 @@ ST_mode *STm; ST_partstat *STps; int dev = TAPE_NR(inode->i_rdev); + loff_t pos = *ppos; read_lock(&st_dev_arr_lock); STp = scsi_tapes[dev]; @@ -1887,7 +1890,7 @@ retval = i; goto out; } - filp->f_pos += transfer; + pos += transfer; buf += transfer; total += transfer; } @@ -1917,6 +1920,7 @@ retval = total; out: + *ppos = pos; if (SRpnt != NULL) { scsi_release_request(SRpnt); SRpnt = NULL; @@ -3774,6 +3778,7 @@ read: st_read, write: st_write, ioctl: st_ioctl, + llseek: no_llseek, open: st_open, flush: st_flush, release: st_release, diff -urN linux-2.4.26/drivers/sound/724hwmcode.h linux-2.4.27/drivers/sound/724hwmcode.h --- linux-2.4.26/drivers/sound/724hwmcode.h 2000-11-11 18:33:13.000000000 -0800 +++ linux-2.4.27/drivers/sound/724hwmcode.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,1575 +0,0 @@ -//============================================================================= -// Copyright (c) 1997-1999 Yamaha Corporation. All Rights Reserved. -// -// Title: -// hwmcode.c -// Desc: -// micro-code for CTRL & DSP -//============================================================================= -#ifndef _HWMCODE_ -#define _HWMCODE_ - -static unsigned long int DspInst[] __initdata = { - 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, - 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, - 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, - 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - -static unsigned long int CntrlInst[] __initdata = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x001A82, 0x032D0D, 0x000810, 0x10043A, - 0x02D38D, 0x000810, 0x18043A, 0x00010D, - 0x020015, 0x0000FD, 0x000020, 0x038860, - 0x039060, 0x038060, 0x038040, 0x038040, - 0x038040, 0x018040, 0x000A7D, 0x038040, - 0x038040, 0x018040, 0x200402, 0x000882, - 0x08001A, 0x000904, 0x015986, 0x000007, - 0x260007, 0x000007, 0x000007, 0x018A06, - 0x000007, 0x030C8D, 0x000810, 0x18043A, - 0x260007, 0x00087D, 0x018042, 0x00160A, - 0x04A206, 0x000007, 0x00218D, 0x000810, - 0x08043A, 0x21C206, 0x000007, 0x0007FD, - 0x018042, 0x08000A, 0x000904, 0x029386, - 0x000195, 0x090D04, 0x000007, 0x000820, - 0x0000F5, 0x000B7D, 0x01F060, 0x0000FD, - 0x032206, 0x018040, 0x000A7D, 0x038042, - 0x13804A, 0x18000A, 0x001820, 0x059060, - 0x058860, 0x018040, 0x0000FD, 0x018042, - 0x70000A, 0x000115, 0x071144, 0x032386, - 0x030000, 0x007020, 0x034A06, 0x018040, - 0x00348D, 0x000810, 0x08043A, 0x21EA06, - 0x000007, 0x02D38D, 0x000810, 0x18043A, - 0x018206, 0x000007, 0x240007, 0x000F8D, - 0x000810, 0x00163A, 0x002402, 0x005C02, - 0x0028FD, 0x000020, 0x018040, 0x08000D, - 0x000815, 0x510984, 0x000007, 0x00004D, - 0x000E5D, 0x000E02, 0x00418D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00008D, - 0x000924, 0x000F02, 0x00458D, 0x000810, - 0x08043A, 0x2C8A06, 0x000007, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x018386, 0x000007, 0x01AA06, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x218086, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x055A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x034986, 0x000007, 0x002104, 0x034986, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x06C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00688D, 0x000810, 0x08043A, 0x288A06, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x060206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x215886, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x212086, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x07DA86, 0x00057D, 0x002820, - 0x03B060, 0x07F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x07FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x008D8D, 0x000810, - 0x08043A, 0x288A06, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x095186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x007FBD, 0x383DC4, - 0x000007, 0x001A7D, 0x001375, 0x018042, - 0x09004A, 0x10000A, 0x0B8D04, 0x139504, - 0x000007, 0x000820, 0x019060, 0x001104, - 0x212086, 0x010040, 0x0017FD, 0x018042, - 0x08000A, 0x000904, 0x212286, 0x000007, - 0x00197D, 0x038042, 0x09804A, 0x10000A, - 0x000924, 0x001664, 0x0011FD, 0x038042, - 0x2B804A, 0x19804A, 0x00008D, 0x218944, - 0x000007, 0x002244, 0x0AE186, 0x000007, - 0x001A64, 0x002A24, 0x00197D, 0x080102, - 0x100122, 0x000820, 0x039060, 0x018040, - 0x003DFD, 0x00008D, 0x000820, 0x018040, - 0x001375, 0x001A7D, 0x010042, 0x09804A, - 0x10000A, 0x00021D, 0x0189E4, 0x2992E4, - 0x309144, 0x000007, 0x00060D, 0x000A15, - 0x000C1D, 0x001025, 0x00A9E4, 0x012BE4, - 0x000464, 0x01B3E4, 0x0232E4, 0x000464, - 0x000464, 0x000464, 0x000464, 0x00040D, - 0x08B1C4, 0x000007, 0x000820, 0x000BF5, - 0x030040, 0x00197D, 0x038042, 0x09804A, - 0x000A24, 0x08000A, 0x080E64, 0x000007, - 0x100122, 0x000820, 0x031060, 0x010040, - 0x0064AC, 0x00027D, 0x000020, 0x018040, - 0x00107D, 0x018042, 0x0011FD, 0x3B804A, - 0x09804A, 0x20000A, 0x000095, 0x1A1144, - 0x00A144, 0x0D2086, 0x00040D, 0x00B984, - 0x0D2186, 0x0018FD, 0x018042, 0x0010FD, - 0x09804A, 0x28000A, 0x000095, 0x010924, - 0x002A64, 0x0D1186, 0x000007, 0x002904, - 0x0D2286, 0x000007, 0x0D2A06, 0x080002, - 0x00008D, 0x00387D, 0x000820, 0x018040, - 0x00127D, 0x018042, 0x10000A, 0x003904, - 0x0DD186, 0x00080D, 0x7FFFB5, 0x00B984, - 0x0DA186, 0x000025, 0x0E7A06, 0x00002D, - 0x000015, 0x00082D, 0x02C78D, 0x000820, - 0x0EC206, 0x00000D, 0x7F8035, 0x00B984, - 0x0E7186, 0x400025, 0x00008D, 0x110944, - 0x000007, 0x00018D, 0x109504, 0x000007, - 0x009164, 0x000424, 0x000424, 0x000424, - 0x100102, 0x280002, 0x02C68D, 0x000820, - 0x0EC206, 0x00018D, 0x00042D, 0x00008D, - 0x109504, 0x000007, 0x00020D, 0x109184, - 0x000007, 0x02C70D, 0x000820, 0x00008D, - 0x0038FD, 0x018040, 0x003BFD, 0x001020, - 0x03A860, 0x000815, 0x313184, 0x212184, - 0x000007, 0x03B060, 0x03A060, 0x018040, - 0x0022FD, 0x000095, 0x010924, 0x000424, - 0x000424, 0x001264, 0x100102, 0x000820, - 0x039060, 0x018040, 0x001924, 0x00FB8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x000424, - 0x000424, 0x00117D, 0x018042, 0x08000A, - 0x000A24, 0x280502, 0x280C02, 0x09800D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x0022FD, 0x018042, 0x08000A, 0x000095, - 0x280DC4, 0x011924, 0x00197D, 0x018042, - 0x0011FD, 0x09804A, 0x10000A, 0x0000B5, - 0x113144, 0x0A8D04, 0x000007, 0x080A44, - 0x129504, 0x000007, 0x0023FD, 0x001020, - 0x038040, 0x101244, 0x000007, 0x000820, - 0x039060, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x10FA86, 0x000007, - 0x003BFD, 0x000100, 0x000A10, 0x0B807A, - 0x13804A, 0x090984, 0x000007, 0x000095, - 0x013D04, 0x118086, 0x10000A, 0x100002, - 0x090984, 0x000007, 0x038042, 0x11804A, - 0x090D04, 0x000007, 0x10000A, 0x090D84, - 0x000007, 0x00257D, 0x000820, 0x018040, - 0x00010D, 0x000810, 0x28143A, 0x00127D, - 0x018042, 0x20000A, 0x00197D, 0x018042, - 0x00117D, 0x31804A, 0x10000A, 0x003124, - 0x01280D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x300102, 0x003124, 0x000424, 0x000424, - 0x001224, 0x280502, 0x001A4C, 0x130186, - 0x700002, 0x00002D, 0x030000, 0x00387D, - 0x018042, 0x10000A, 0x132A06, 0x002124, - 0x0000AD, 0x100002, 0x00010D, 0x000924, - 0x006B24, 0x01368D, 0x00397D, 0x000820, - 0x058040, 0x038042, 0x09844A, 0x000606, - 0x08040A, 0x003264, 0x00008D, 0x000A24, - 0x001020, 0x00227D, 0x018040, 0x013C0D, - 0x000810, 0x08043A, 0x29D206, 0x000007, - 0x002820, 0x00207D, 0x018040, 0x00117D, - 0x038042, 0x13804A, 0x33800A, 0x00387D, - 0x018042, 0x08000A, 0x000904, 0x163A86, - 0x000007, 0x00008D, 0x030964, 0x01478D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x380102, - 0x000424, 0x000424, 0x001224, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x14A286, - 0x000007, 0x280502, 0x001A4C, 0x163986, - 0x000007, 0x032164, 0x00632C, 0x003DFD, - 0x018042, 0x08000A, 0x000095, 0x090904, - 0x000007, 0x000820, 0x001A4C, 0x156186, - 0x018040, 0x030000, 0x157A06, 0x002124, - 0x00010D, 0x000924, 0x006B24, 0x015B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x003A64, - 0x000095, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x01628D, 0x000810, 0x08043A, 0x29D206, - 0x000007, 0x14D206, 0x000007, 0x007020, - 0x08010A, 0x10012A, 0x0020FD, 0x038860, - 0x039060, 0x018040, 0x00227D, 0x018042, - 0x003DFD, 0x08000A, 0x31844A, 0x000904, - 0x16D886, 0x18008B, 0x00008D, 0x189904, - 0x00312C, 0x17AA06, 0x000007, 0x00324C, - 0x173386, 0x000007, 0x001904, 0x173086, - 0x000007, 0x000095, 0x199144, 0x00222C, - 0x003124, 0x00636C, 0x000E3D, 0x001375, - 0x000BFD, 0x010042, 0x09804A, 0x10000A, - 0x038AEC, 0x0393EC, 0x00224C, 0x17A986, - 0x000007, 0x00008D, 0x189904, 0x00226C, - 0x00322C, 0x30050A, 0x301DAB, 0x002083, - 0x0018FD, 0x018042, 0x08000A, 0x018924, - 0x300502, 0x001083, 0x001875, 0x010042, - 0x10000A, 0x00008D, 0x010924, 0x001375, - 0x330542, 0x330CCB, 0x332CCB, 0x3334CB, - 0x333CCB, 0x3344CB, 0x334CCB, 0x3354CB, - 0x305C8B, 0x006083, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x187A86, 0x000007, - 0x001E2D, 0x0005FD, 0x018042, 0x08000A, - 0x028924, 0x280502, 0x00060D, 0x000810, - 0x280C3A, 0x00008D, 0x000810, 0x28143A, - 0x0A808D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001275, 0x030042, 0x21004A, - 0x00008D, 0x1A0944, 0x000007, 0x01980D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0001F5, 0x030042, 0x0D004A, 0x10000A, - 0x089144, 0x000007, 0x000820, 0x010040, - 0x0025F5, 0x0A3144, 0x000007, 0x000820, - 0x032860, 0x030040, 0x00217D, 0x038042, - 0x0B804A, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00008D, 0x000124, 0x00012C, - 0x000E64, 0x001A64, 0x00636C, 0x08010A, - 0x10012A, 0x000820, 0x031060, 0x030040, - 0x0020FD, 0x018042, 0x08000A, 0x00227D, - 0x018042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x00197D, 0x018042, 0x08000A, - 0x0022FD, 0x038042, 0x10000A, 0x000820, - 0x031060, 0x030040, 0x090D04, 0x000007, - 0x000820, 0x030040, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x038042, 0x13804A, 0x19804A, 0x110D04, - 0x198D04, 0x000007, 0x08000A, 0x001020, - 0x031860, 0x030860, 0x030040, 0x00008D, - 0x0B0944, 0x000007, 0x000820, 0x010040, - 0x0005F5, 0x030042, 0x08000A, 0x000820, - 0x010040, 0x0000F5, 0x010042, 0x08000A, - 0x000904, 0x1C6086, 0x001E75, 0x030042, - 0x01044A, 0x000C0A, 0x1C7206, 0x000007, - 0x000402, 0x000C02, 0x00177D, 0x001AF5, - 0x018042, 0x03144A, 0x031C4A, 0x03244A, - 0x032C4A, 0x03344A, 0x033C4A, 0x03444A, - 0x004C0A, 0x00043D, 0x0013F5, 0x001AFD, - 0x030042, 0x0B004A, 0x1B804A, 0x13804A, - 0x20000A, 0x089144, 0x19A144, 0x0389E4, - 0x0399EC, 0x005502, 0x005D0A, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x006502, 0x006D0A, 0x030042, 0x0B004A, - 0x19004A, 0x2B804A, 0x13804A, 0x21804A, - 0x30000A, 0x089144, 0x19A144, 0x2AB144, - 0x0389E4, 0x0399EC, 0x007502, 0x007D0A, - 0x03A9E4, 0x000702, 0x00107D, 0x000415, - 0x018042, 0x08000A, 0x0109E4, 0x000F02, - 0x002AF5, 0x0019FD, 0x010042, 0x09804A, - 0x10000A, 0x000934, 0x001674, 0x0029F5, - 0x010042, 0x10000A, 0x00917C, 0x002075, - 0x010042, 0x08000A, 0x000904, 0x1ED286, - 0x0026F5, 0x0027F5, 0x030042, 0x09004A, - 0x10000A, 0x000A3C, 0x00167C, 0x001A75, - 0x000BFD, 0x010042, 0x51804A, 0x48000A, - 0x160007, 0x001075, 0x010042, 0x282C0A, - 0x281D12, 0x282512, 0x001F32, 0x1E0007, - 0x0E0007, 0x001975, 0x010042, 0x002DF5, - 0x0D004A, 0x10000A, 0x009144, 0x1FB286, - 0x010042, 0x28340A, 0x000E5D, 0x00008D, - 0x000375, 0x000820, 0x010040, 0x05D2F4, - 0x54D104, 0x00735C, 0x205386, 0x000007, - 0x0C0007, 0x080007, 0x0A0007, 0x02040D, - 0x000810, 0x08043A, 0x332206, 0x000007, - 0x205A06, 0x000007, 0x080007, 0x002275, - 0x010042, 0x20000A, 0x002104, 0x212086, - 0x001E2D, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x209286, 0x000007, 0x002010, - 0x30043A, 0x00057D, 0x0180C3, 0x08000A, - 0x028924, 0x280502, 0x280C02, 0x0A810D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x0004FD, 0x018042, 0x70000A, 0x030000, - 0x007020, 0x06FA06, 0x018040, 0x02180D, - 0x000810, 0x08043A, 0x2B2206, 0x000007, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x218A86, 0x000007, 0x01F206, 0x000007, - 0x000875, 0x0009FD, 0x00010D, 0x220A06, - 0x000295, 0x000B75, 0x00097D, 0x00000D, - 0x000515, 0x010042, 0x18000A, 0x001904, - 0x287886, 0x0006F5, 0x001020, 0x010040, - 0x0004F5, 0x000820, 0x010040, 0x000775, - 0x010042, 0x09804A, 0x10000A, 0x001124, - 0x000904, 0x22BA86, 0x000815, 0x080102, - 0x101204, 0x22DA06, 0x000575, 0x081204, - 0x000007, 0x100102, 0x000575, 0x000425, - 0x021124, 0x100102, 0x000820, 0x031060, - 0x010040, 0x001924, 0x287886, 0x00008D, - 0x000464, 0x009D04, 0x278886, 0x180102, - 0x000575, 0x010042, 0x28040A, 0x00018D, - 0x000924, 0x280D02, 0x00000D, 0x000924, - 0x281502, 0x10000D, 0x000820, 0x0002F5, - 0x010040, 0x200007, 0x001175, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x23C286, - 0x000007, 0x000100, 0x080B20, 0x130B60, - 0x1B0B60, 0x030A60, 0x010040, 0x050042, - 0x3D004A, 0x35004A, 0x2D004A, 0x20000A, - 0x0006F5, 0x010042, 0x28140A, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x24CA86, 0x004015, 0x000095, 0x010D04, - 0x24B886, 0x100022, 0x10002A, 0x24E206, - 0x000007, 0x333104, 0x2AA904, 0x000007, - 0x032124, 0x280502, 0x001124, 0x000424, - 0x000424, 0x003224, 0x00292C, 0x00636C, - 0x25F386, 0x000007, 0x02B164, 0x000464, - 0x000464, 0x00008D, 0x000A64, 0x280D02, - 0x10008D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x00008D, 0x38B904, 0x000007, - 0x03296C, 0x30010A, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x25BA86, 0x000007, - 0x02312C, 0x28050A, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x267A86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x26C086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x26CA86, 0x000007, 0x003124, 0x300502, - 0x003924, 0x300583, 0x000883, 0x0005F5, - 0x010042, 0x28040A, 0x00008D, 0x008124, - 0x280D02, 0x00008D, 0x008124, 0x281502, - 0x10018D, 0x000820, 0x0002F5, 0x010040, - 0x220007, 0x001025, 0x000575, 0x030042, - 0x09004A, 0x10000A, 0x0A0904, 0x121104, - 0x000007, 0x001020, 0x050860, 0x050040, - 0x0006FD, 0x018042, 0x09004A, 0x10000A, - 0x0000A5, 0x0A0904, 0x121104, 0x000007, - 0x000820, 0x019060, 0x010040, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x284286, - 0x000007, 0x230A06, 0x000007, 0x000606, - 0x000007, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x289286, 0x000007, 0x000100, - 0x080B20, 0x138B60, 0x1B8B60, 0x238B60, - 0x2B8B60, 0x338B60, 0x3B8B60, 0x438B60, - 0x4B8B60, 0x538B60, 0x5B8B60, 0x638B60, - 0x6B8B60, 0x738B60, 0x7B8B60, 0x038F60, - 0x0B8F60, 0x138F60, 0x1B8F60, 0x238F60, - 0x2B8F60, 0x338F60, 0x3B8F60, 0x438F60, - 0x4B8F60, 0x538F60, 0x5B8F60, 0x638F60, - 0x6B8F60, 0x738F60, 0x7B8F60, 0x038A60, - 0x000606, 0x018040, 0x00008D, 0x000A64, - 0x280D02, 0x000A24, 0x00027D, 0x018042, - 0x10000A, 0x001224, 0x0003FD, 0x018042, - 0x08000A, 0x000904, 0x2A8286, 0x000007, - 0x00018D, 0x000A24, 0x000464, 0x000464, - 0x080102, 0x000924, 0x000424, 0x000424, - 0x100102, 0x02000D, 0x009144, 0x2AD986, - 0x000007, 0x0001FD, 0x018042, 0x08000A, - 0x000A44, 0x2ABB86, 0x018042, 0x0A000D, - 0x000820, 0x0002FD, 0x018040, 0x200007, - 0x00027D, 0x001020, 0x000606, 0x018040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x2B2A86, 0x000007, 0x00037D, 0x018042, - 0x08000A, 0x000904, 0x2B5A86, 0x000007, - 0x000075, 0x002E7D, 0x010042, 0x0B804A, - 0x000020, 0x000904, 0x000686, 0x010040, - 0x31844A, 0x30048B, 0x000883, 0x00008D, - 0x000810, 0x28143A, 0x00008D, 0x000810, - 0x280C3A, 0x000675, 0x010042, 0x08000A, - 0x003815, 0x010924, 0x280502, 0x0B000D, - 0x000820, 0x0002F5, 0x010040, 0x000606, - 0x220007, 0x000464, 0x000464, 0x000606, - 0x000007, 0x000134, 0x007F8D, 0x00093C, - 0x281D12, 0x282512, 0x001F32, 0x0E0007, - 0x00010D, 0x00037D, 0x000820, 0x018040, - 0x05D2F4, 0x000007, 0x080007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2D0286, - 0x000007, 0x000606, 0x000007, 0x000007, - 0x000012, 0x100007, 0x320007, 0x600007, - 0x100080, 0x48001A, 0x004904, 0x2D6186, - 0x000007, 0x001210, 0x58003A, 0x000145, - 0x5C5D04, 0x000007, 0x000080, 0x48001A, - 0x004904, 0x2DB186, 0x000007, 0x001210, - 0x50003A, 0x005904, 0x2E0886, 0x000045, - 0x0000C5, 0x7FFFF5, 0x7FFF7D, 0x07D524, - 0x004224, 0x500102, 0x200502, 0x000082, - 0x40001A, 0x004104, 0x2E3986, 0x000007, - 0x003865, 0x40001A, 0x004020, 0x00104D, - 0x04C184, 0x301B86, 0x000040, 0x040007, - 0x000165, 0x000145, 0x004020, 0x000040, - 0x000765, 0x080080, 0x40001A, 0x004104, - 0x2EC986, 0x000007, 0x001210, 0x40003A, - 0x004104, 0x2F2286, 0x00004D, 0x0000CD, - 0x004810, 0x20043A, 0x000882, 0x40001A, - 0x004104, 0x2F3186, 0x000007, 0x004820, - 0x005904, 0x300886, 0x000040, 0x0007E5, - 0x200480, 0x2816A0, 0x3216E0, 0x3A16E0, - 0x4216E0, 0x021260, 0x000040, 0x000032, - 0x400075, 0x00007D, 0x07D574, 0x200512, - 0x000082, 0x40001A, 0x004104, 0x2FE186, - 0x000007, 0x037206, 0x640007, 0x060007, - 0x0000E5, 0x000020, 0x000040, 0x000A65, - 0x000020, 0x020040, 0x020040, 0x000040, - 0x000165, 0x000042, 0x70000A, 0x007104, - 0x30A286, 0x000007, 0x018206, 0x640007, - 0x050000, 0x007020, 0x000040, 0x037206, - 0x640007, 0x000007, 0x00306D, 0x028860, - 0x029060, 0x08000A, 0x028860, 0x008040, - 0x100012, 0x00100D, 0x009184, 0x314186, - 0x000E0D, 0x009184, 0x325186, 0x000007, - 0x300007, 0x001020, 0x003B6D, 0x008040, - 0x000080, 0x08001A, 0x000904, 0x316186, - 0x000007, 0x001220, 0x000DED, 0x008040, - 0x008042, 0x10000A, 0x40000D, 0x109544, - 0x000007, 0x001020, 0x000DED, 0x008040, - 0x008042, 0x20040A, 0x000082, 0x08001A, - 0x000904, 0x31F186, 0x000007, 0x003B6D, - 0x008042, 0x08000A, 0x000E15, 0x010984, - 0x329B86, 0x600007, 0x08001A, 0x000C15, - 0x010984, 0x328386, 0x000020, 0x1A0007, - 0x0002ED, 0x008040, 0x620007, 0x00306D, - 0x028042, 0x0A804A, 0x000820, 0x0A804A, - 0x000606, 0x10804A, 0x000007, 0x282512, - 0x001F32, 0x05D2F4, 0x54D104, 0x00735C, - 0x000786, 0x000007, 0x0C0007, 0x0A0007, - 0x1C0007, 0x003465, 0x020040, 0x004820, - 0x025060, 0x40000A, 0x024060, 0x000040, - 0x454944, 0x000007, 0x004020, 0x003AE5, - 0x000040, 0x0028E5, 0x000042, 0x48000A, - 0x004904, 0x386886, 0x002C65, 0x000042, - 0x40000A, 0x0000D5, 0x454104, 0x000007, - 0x000655, 0x054504, 0x34F286, 0x0001D5, - 0x054504, 0x34F086, 0x002B65, 0x000042, - 0x003AE5, 0x50004A, 0x40000A, 0x45C3D4, - 0x000007, 0x454504, 0x000007, 0x0000CD, - 0x444944, 0x000007, 0x454504, 0x000007, - 0x00014D, 0x554944, 0x000007, 0x045144, - 0x34E986, 0x002C65, 0x000042, 0x48000A, - 0x4CD104, 0x000007, 0x04C144, 0x34F386, - 0x000007, 0x160007, 0x002CE5, 0x040042, - 0x40000A, 0x004020, 0x000040, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x356086, - 0x000007, 0x002402, 0x36A206, 0x005C02, - 0x0025E5, 0x000042, 0x40000A, 0x004274, - 0x002AE5, 0x000042, 0x40000A, 0x004274, - 0x500112, 0x0029E5, 0x000042, 0x40000A, - 0x004234, 0x454104, 0x000007, 0x004020, - 0x000040, 0x003EE5, 0x000020, 0x000040, - 0x002DE5, 0x400152, 0x50000A, 0x045144, - 0x364A86, 0x0000C5, 0x003EE5, 0x004020, - 0x000040, 0x002BE5, 0x000042, 0x40000A, - 0x404254, 0x000007, 0x002AE5, 0x004020, - 0x000040, 0x500132, 0x040134, 0x005674, - 0x0029E5, 0x020042, 0x42000A, 0x000042, - 0x50000A, 0x05417C, 0x0028E5, 0x000042, - 0x48000A, 0x0000C5, 0x4CC144, 0x371086, - 0x0026E5, 0x0027E5, 0x020042, 0x40004A, - 0x50000A, 0x00423C, 0x00567C, 0x0028E5, - 0x004820, 0x000040, 0x281D12, 0x282512, - 0x001F72, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x37AA86, 0x0E0007, 0x160007, - 0x1E0007, 0x003EE5, 0x000042, 0x40000A, - 0x004104, 0x37E886, 0x002D65, 0x000042, - 0x28340A, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x50004A, 0x05D2F4, - 0x54D104, 0x00735C, 0x385186, 0x000007, - 0x000606, 0x080007, 0x0C0007, 0x080007, - 0x0A0007, 0x0001E5, 0x020045, 0x004020, - 0x000060, 0x000365, 0x000040, 0x002E65, - 0x001A20, 0x0A1A60, 0x000040, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x000606, 0x50004A, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -// -------------------------------------------- -// DS-1E Controller InstructionRAM Code -// 1999/06/21 -// Buf441 slot is Enabled. -// -------------------------------------------- -// 04/09?@creat -// 04/12 stop nise fix -// 06/21?@WorkingOff timming -static unsigned long int CntrlInst1E[] __initdata = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x00800D, 0x000810, 0x20043A, 0x001A82, - 0x03460D, 0x000810, 0x10043A, 0x02EC0D, - 0x000810, 0x18043A, 0x00010D, 0x020015, - 0x0000FD, 0x000020, 0x038860, 0x039060, - 0x038060, 0x038040, 0x038040, 0x038040, - 0x018040, 0x000A7D, 0x038040, 0x038040, - 0x018040, 0x200402, 0x000882, 0x08001A, - 0x000904, 0x017186, 0x000007, 0x260007, - 0x400007, 0x000007, 0x03258D, 0x000810, - 0x18043A, 0x260007, 0x284402, 0x00087D, - 0x018042, 0x00160A, 0x05A206, 0x000007, - 0x440007, 0x00230D, 0x000810, 0x08043A, - 0x22FA06, 0x000007, 0x0007FD, 0x018042, - 0x08000A, 0x000904, 0x02AB86, 0x000195, - 0x090D04, 0x000007, 0x000820, 0x0000F5, - 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, - 0x018040, 0x000A7D, 0x038042, 0x13804A, - 0x18000A, 0x001820, 0x059060, 0x058860, - 0x018040, 0x0000FD, 0x018042, 0x70000A, - 0x000115, 0x071144, 0x033B86, 0x030000, - 0x007020, 0x036206, 0x018040, 0x00360D, - 0x000810, 0x08043A, 0x232206, 0x000007, - 0x02EC0D, 0x000810, 0x18043A, 0x019A06, - 0x000007, 0x240007, 0x000F8D, 0x000810, - 0x00163A, 0x002402, 0x005C02, 0x0028FD, - 0x000020, 0x018040, 0x08000D, 0x000815, - 0x510984, 0x000007, 0x00004D, 0x000E5D, - 0x000E02, 0x00430D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x00008D, 0x000924, - 0x000F02, 0x00470D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x480480, 0x001210, - 0x28043A, 0x00778D, 0x000810, 0x280C3A, - 0x00068D, 0x000810, 0x28143A, 0x284402, - 0x03258D, 0x000810, 0x18043A, 0x07FF8D, - 0x000820, 0x0002FD, 0x018040, 0x260007, - 0x200007, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x051286, 0x000007, 0x240007, - 0x02EC0D, 0x000810, 0x18043A, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x019B86, 0x000007, 0x01B206, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x22B886, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x065A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x036186, 0x000007, 0x002104, 0x036186, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x07C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00788D, 0x000810, 0x08043A, 0x2A1206, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x070206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x229086, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x225886, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x08DA86, 0x00057D, 0x002820, - 0x03B060, 0x08F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x08FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x009D8D, 0x000810, - 0x08043A, 0x2A1206, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x0A5186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x00107D, 0x018042, - 0x08000A, 0x000215, 0x010984, 0x3A8186, - 0x000007, 0x007FBD, 0x383DC4, 0x000007, - 0x001A7D, 0x001375, 0x018042, 0x09004A, - 0x10000A, 0x0B8D04, 0x139504, 0x000007, - 0x000820, 0x019060, 0x001104, 0x225886, - 0x010040, 0x0017FD, 0x018042, 0x08000A, - 0x000904, 0x225A86, 0x000007, 0x00197D, - 0x038042, 0x09804A, 0x10000A, 0x000924, - 0x001664, 0x0011FD, 0x038042, 0x2B804A, - 0x19804A, 0x00008D, 0x218944, 0x000007, - 0x002244, 0x0C1986, 0x000007, 0x001A64, - 0x002A24, 0x00197D, 0x080102, 0x100122, - 0x000820, 0x039060, 0x018040, 0x003DFD, - 0x00008D, 0x000820, 0x018040, 0x001375, - 0x001A7D, 0x010042, 0x09804A, 0x10000A, - 0x00021D, 0x0189E4, 0x2992E4, 0x309144, - 0x000007, 0x00060D, 0x000A15, 0x000C1D, - 0x001025, 0x00A9E4, 0x012BE4, 0x000464, - 0x01B3E4, 0x0232E4, 0x000464, 0x000464, - 0x000464, 0x000464, 0x00040D, 0x08B1C4, - 0x000007, 0x000820, 0x000BF5, 0x030040, - 0x00197D, 0x038042, 0x09804A, 0x000A24, - 0x08000A, 0x080E64, 0x000007, 0x100122, - 0x000820, 0x031060, 0x010040, 0x0064AC, - 0x00027D, 0x000020, 0x018040, 0x00107D, - 0x018042, 0x0011FD, 0x3B804A, 0x09804A, - 0x20000A, 0x000095, 0x1A1144, 0x00A144, - 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, - 0x0018FD, 0x018042, 0x0010FD, 0x09804A, - 0x28000A, 0x000095, 0x010924, 0x002A64, - 0x0E4986, 0x000007, 0x002904, 0x0E5A86, - 0x000007, 0x0E6206, 0x080002, 0x00008D, - 0x00387D, 0x000820, 0x018040, 0x00127D, - 0x018042, 0x10000A, 0x003904, 0x0F0986, - 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, - 0x000025, 0x0FB206, 0x00002D, 0x000015, - 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, - 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, - 0x400025, 0x00008D, 0x110944, 0x000007, - 0x00018D, 0x109504, 0x000007, 0x009164, - 0x000424, 0x000424, 0x000424, 0x100102, - 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, - 0x00018D, 0x00042D, 0x00008D, 0x109504, - 0x000007, 0x00020D, 0x109184, 0x000007, - 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, - 0x018040, 0x003BFD, 0x001020, 0x03A860, - 0x000815, 0x313184, 0x212184, 0x000007, - 0x03B060, 0x03A060, 0x018040, 0x0022FD, - 0x000095, 0x010924, 0x000424, 0x000424, - 0x001264, 0x100102, 0x000820, 0x039060, - 0x018040, 0x001924, 0x010F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x000424, 0x000424, - 0x00117D, 0x018042, 0x08000A, 0x000A24, - 0x280502, 0x280C02, 0x09800D, 0x000820, - 0x0002FD, 0x018040, 0x200007, 0x0022FD, - 0x018042, 0x08000A, 0x000095, 0x280DC4, - 0x011924, 0x00197D, 0x018042, 0x0011FD, - 0x09804A, 0x10000A, 0x0000B5, 0x113144, - 0x0A8D04, 0x000007, 0x080A44, 0x129504, - 0x000007, 0x0023FD, 0x001020, 0x038040, - 0x101244, 0x000007, 0x000820, 0x039060, - 0x018040, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x123286, 0x000007, 0x003BFD, - 0x000100, 0x000A10, 0x0B807A, 0x13804A, - 0x090984, 0x000007, 0x000095, 0x013D04, - 0x12B886, 0x10000A, 0x100002, 0x090984, - 0x000007, 0x038042, 0x11804A, 0x090D04, - 0x000007, 0x10000A, 0x090D84, 0x000007, - 0x00257D, 0x000820, 0x018040, 0x00010D, - 0x000810, 0x28143A, 0x00127D, 0x018042, - 0x20000A, 0x00197D, 0x018042, 0x00117D, - 0x31804A, 0x10000A, 0x003124, 0x013B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x300102, - 0x003124, 0x000424, 0x000424, 0x001224, - 0x280502, 0x001A4C, 0x143986, 0x700002, - 0x00002D, 0x030000, 0x00387D, 0x018042, - 0x10000A, 0x146206, 0x002124, 0x0000AD, - 0x100002, 0x00010D, 0x000924, 0x006B24, - 0x014A0D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x003264, 0x00008D, 0x000A24, 0x001020, - 0x00227D, 0x018040, 0x014F8D, 0x000810, - 0x08043A, 0x2B5A06, 0x000007, 0x002820, - 0x00207D, 0x018040, 0x00117D, 0x038042, - 0x13804A, 0x33800A, 0x00387D, 0x018042, - 0x08000A, 0x000904, 0x177286, 0x000007, - 0x00008D, 0x030964, 0x015B0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x380102, 0x000424, - 0x000424, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x280502, 0x001A4C, 0x177186, 0x000007, - 0x032164, 0x00632C, 0x003DFD, 0x018042, - 0x08000A, 0x000095, 0x090904, 0x000007, - 0x000820, 0x001A4C, 0x169986, 0x018040, - 0x030000, 0x16B206, 0x002124, 0x00010D, - 0x000924, 0x006B24, 0x016F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x003A64, 0x000095, - 0x001224, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x171286, 0x000007, 0x01760D, - 0x000810, 0x08043A, 0x2B5A06, 0x000007, - 0x160A06, 0x000007, 0x007020, 0x08010A, - 0x10012A, 0x0020FD, 0x038860, 0x039060, - 0x018040, 0x00227D, 0x018042, 0x003DFD, - 0x08000A, 0x31844A, 0x000904, 0x181086, - 0x18008B, 0x00008D, 0x189904, 0x00312C, - 0x18E206, 0x000007, 0x00324C, 0x186B86, - 0x000007, 0x001904, 0x186886, 0x000007, - 0x000095, 0x199144, 0x00222C, 0x003124, - 0x00636C, 0x000E3D, 0x001375, 0x000BFD, - 0x010042, 0x09804A, 0x10000A, 0x038AEC, - 0x0393EC, 0x00224C, 0x18E186, 0x000007, - 0x00008D, 0x189904, 0x00226C, 0x00322C, - 0x30050A, 0x301DAB, 0x002083, 0x0018FD, - 0x018042, 0x08000A, 0x018924, 0x300502, - 0x001083, 0x001875, 0x010042, 0x10000A, - 0x00008D, 0x010924, 0x001375, 0x330542, - 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, - 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, - 0x006083, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x19B286, 0x000007, 0x001E2D, - 0x0005FD, 0x018042, 0x08000A, 0x028924, - 0x280502, 0x00060D, 0x000810, 0x280C3A, - 0x00008D, 0x000810, 0x28143A, 0x0A808D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x001275, 0x030042, 0x21004A, 0x00008D, - 0x1A0944, 0x000007, 0x01AB8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, - 0x030042, 0x0D004A, 0x10000A, 0x089144, - 0x000007, 0x000820, 0x010040, 0x0025F5, - 0x0A3144, 0x000007, 0x000820, 0x032860, - 0x030040, 0x00217D, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00008D, 0x000124, 0x00012C, 0x000E64, - 0x001A64, 0x00636C, 0x08010A, 0x10012A, - 0x000820, 0x031060, 0x030040, 0x0020FD, - 0x018042, 0x08000A, 0x00227D, 0x018042, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00197D, 0x018042, 0x08000A, 0x0022FD, - 0x038042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x090D04, 0x000007, 0x000820, - 0x030040, 0x038042, 0x0B804A, 0x10000A, - 0x000820, 0x031060, 0x030040, 0x038042, - 0x13804A, 0x19804A, 0x110D04, 0x198D04, - 0x000007, 0x08000A, 0x001020, 0x031860, - 0x030860, 0x030040, 0x00008D, 0x0B0944, - 0x000007, 0x000820, 0x010040, 0x0005F5, - 0x030042, 0x08000A, 0x000820, 0x010040, - 0x0000F5, 0x010042, 0x08000A, 0x000904, - 0x1D9886, 0x001E75, 0x030042, 0x01044A, - 0x000C0A, 0x1DAA06, 0x000007, 0x000402, - 0x000C02, 0x00177D, 0x001AF5, 0x018042, - 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, - 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, - 0x00043D, 0x0013F5, 0x001AFD, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x005502, 0x005D0A, 0x030042, 0x0B004A, - 0x1B804A, 0x13804A, 0x20000A, 0x089144, - 0x19A144, 0x0389E4, 0x0399EC, 0x006502, - 0x006D0A, 0x030042, 0x0B004A, 0x19004A, - 0x2B804A, 0x13804A, 0x21804A, 0x30000A, - 0x089144, 0x19A144, 0x2AB144, 0x0389E4, - 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, - 0x000702, 0x00107D, 0x000415, 0x018042, - 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, - 0x0019FD, 0x010042, 0x09804A, 0x10000A, - 0x000934, 0x001674, 0x0029F5, 0x010042, - 0x10000A, 0x00917C, 0x002075, 0x010042, - 0x08000A, 0x000904, 0x200A86, 0x0026F5, - 0x0027F5, 0x030042, 0x09004A, 0x10000A, - 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, - 0x010042, 0x51804A, 0x48000A, 0x160007, - 0x001075, 0x010042, 0x282C0A, 0x281D12, - 0x282512, 0x001F32, 0x1E0007, 0x0E0007, - 0x001975, 0x010042, 0x002DF5, 0x0D004A, - 0x10000A, 0x009144, 0x20EA86, 0x010042, - 0x28340A, 0x000E5D, 0x00008D, 0x000375, - 0x000820, 0x010040, 0x05D2F4, 0x54D104, - 0x00735C, 0x218B86, 0x000007, 0x0C0007, - 0x080007, 0x0A0007, 0x02178D, 0x000810, - 0x08043A, 0x34B206, 0x000007, 0x219206, - 0x000007, 0x080007, 0x002275, 0x010042, - 0x20000A, 0x002104, 0x225886, 0x001E2D, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x21CA86, 0x000007, 0x002010, 0x30043A, - 0x00057D, 0x0180C3, 0x08000A, 0x028924, - 0x280502, 0x280C02, 0x0A810D, 0x000820, - 0x0002F5, 0x010040, 0x220007, 0x0004FD, - 0x018042, 0x70000A, 0x030000, 0x007020, - 0x07FA06, 0x018040, 0x022B8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x22C286, - 0x000007, 0x020206, 0x000007, 0x000875, - 0x0009FD, 0x00010D, 0x234206, 0x000295, - 0x000B75, 0x00097D, 0x00000D, 0x000515, - 0x010042, 0x18000A, 0x001904, 0x2A0086, - 0x0006F5, 0x001020, 0x010040, 0x0004F5, - 0x000820, 0x010040, 0x000775, 0x010042, - 0x09804A, 0x10000A, 0x001124, 0x000904, - 0x23F286, 0x000815, 0x080102, 0x101204, - 0x241206, 0x000575, 0x081204, 0x000007, - 0x100102, 0x000575, 0x000425, 0x021124, - 0x100102, 0x000820, 0x031060, 0x010040, - 0x001924, 0x2A0086, 0x00008D, 0x000464, - 0x009D04, 0x291086, 0x180102, 0x000575, - 0x010042, 0x28040A, 0x00018D, 0x000924, - 0x280D02, 0x00000D, 0x000924, 0x281502, - 0x10000D, 0x000820, 0x0002F5, 0x010040, - 0x200007, 0x001175, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x24FA86, 0x000007, - 0x000100, 0x080B20, 0x130B60, 0x1B0B60, - 0x030A60, 0x010040, 0x050042, 0x3D004A, - 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, - 0x010042, 0x28140A, 0x0004F5, 0x010042, - 0x08000A, 0x000315, 0x010D04, 0x260286, - 0x004015, 0x000095, 0x010D04, 0x25F086, - 0x100022, 0x10002A, 0x261A06, 0x000007, - 0x333104, 0x2AA904, 0x000007, 0x032124, - 0x280502, 0x284402, 0x001124, 0x400102, - 0x000424, 0x000424, 0x003224, 0x00292C, - 0x00636C, 0x277386, 0x000007, 0x02B164, - 0x000464, 0x000464, 0x00008D, 0x000A64, - 0x280D02, 0x10008D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x00008D, 0x38B904, - 0x000007, 0x03296C, 0x30010A, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x270286, - 0x000007, 0x00212C, 0x28050A, 0x00316C, - 0x00046C, 0x00046C, 0x28450A, 0x001124, - 0x006B64, 0x100102, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x004124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x27FA86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x284086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x284A86, 0x000007, 0x284402, 0x003124, - 0x300502, 0x003924, 0x300583, 0x000883, - 0x0005F5, 0x010042, 0x28040A, 0x00008D, - 0x008124, 0x280D02, 0x00008D, 0x008124, - 0x281502, 0x10018D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001025, 0x000575, - 0x030042, 0x09004A, 0x10000A, 0x0A0904, - 0x121104, 0x000007, 0x001020, 0x050860, - 0x050040, 0x0006FD, 0x018042, 0x09004A, - 0x10000A, 0x0000A5, 0x0A0904, 0x121104, - 0x000007, 0x000820, 0x019060, 0x010040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x29CA86, 0x000007, 0x244206, 0x000007, - 0x000606, 0x000007, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x2A1A86, 0x000007, - 0x000100, 0x080B20, 0x138B60, 0x1B8B60, - 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, - 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, - 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, - 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, - 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, - 0x038A60, 0x000606, 0x018040, 0x00008D, - 0x000A64, 0x280D02, 0x000A24, 0x00027D, - 0x018042, 0x10000A, 0x001224, 0x0003FD, - 0x018042, 0x08000A, 0x000904, 0x2C0A86, - 0x000007, 0x00018D, 0x000A24, 0x000464, - 0x000464, 0x080102, 0x000924, 0x000424, - 0x000424, 0x100102, 0x02000D, 0x009144, - 0x2C6186, 0x000007, 0x0001FD, 0x018042, - 0x08000A, 0x000A44, 0x2C4386, 0x018042, - 0x0A000D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00027D, 0x001020, 0x000606, - 0x018040, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x2CB286, 0x000007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2CE286, - 0x000007, 0x000075, 0x002E7D, 0x010042, - 0x0B804A, 0x000020, 0x000904, 0x000686, - 0x010040, 0x31844A, 0x30048B, 0x000883, - 0x00008D, 0x000810, 0x28143A, 0x00008D, - 0x000810, 0x280C3A, 0x000675, 0x010042, - 0x08000A, 0x003815, 0x010924, 0x280502, - 0x0B000D, 0x000820, 0x0002F5, 0x010040, - 0x000606, 0x220007, 0x000464, 0x000464, - 0x000606, 0x000007, 0x000134, 0x007F8D, - 0x00093C, 0x281D12, 0x282512, 0x001F32, - 0x0E0007, 0x00010D, 0x00037D, 0x000820, - 0x018040, 0x05D2F4, 0x000007, 0x080007, - 0x00037D, 0x018042, 0x08000A, 0x000904, - 0x2E8A86, 0x000007, 0x000606, 0x000007, - 0x000007, 0x000012, 0x100007, 0x320007, - 0x600007, 0x460007, 0x100080, 0x48001A, - 0x004904, 0x2EF186, 0x000007, 0x001210, - 0x58003A, 0x000145, 0x5C5D04, 0x000007, - 0x000080, 0x48001A, 0x004904, 0x2F4186, - 0x000007, 0x001210, 0x50003A, 0x005904, - 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, - 0x7FFF7D, 0x07D524, 0x004224, 0x500102, - 0x200502, 0x000082, 0x40001A, 0x004104, - 0x2FC986, 0x000007, 0x003865, 0x40001A, - 0x004020, 0x00104D, 0x04C184, 0x31AB86, - 0x000040, 0x040007, 0x000165, 0x000145, - 0x004020, 0x000040, 0x000765, 0x080080, - 0x40001A, 0x004104, 0x305986, 0x000007, - 0x001210, 0x40003A, 0x004104, 0x30B286, - 0x00004D, 0x0000CD, 0x004810, 0x20043A, - 0x000882, 0x40001A, 0x004104, 0x30C186, - 0x000007, 0x004820, 0x005904, 0x319886, - 0x000040, 0x0007E5, 0x200480, 0x2816A0, - 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, - 0x000040, 0x000032, 0x400075, 0x00007D, - 0x07D574, 0x200512, 0x000082, 0x40001A, - 0x004104, 0x317186, 0x000007, 0x038A06, - 0x640007, 0x0000E5, 0x000020, 0x000040, - 0x000A65, 0x000020, 0x020040, 0x020040, - 0x000040, 0x000165, 0x000042, 0x70000A, - 0x007104, 0x323286, 0x000007, 0x060007, - 0x019A06, 0x640007, 0x050000, 0x007020, - 0x000040, 0x038A06, 0x640007, 0x000007, - 0x00306D, 0x028860, 0x029060, 0x08000A, - 0x028860, 0x008040, 0x100012, 0x00100D, - 0x009184, 0x32D186, 0x000E0D, 0x009184, - 0x33E186, 0x000007, 0x300007, 0x001020, - 0x003B6D, 0x008040, 0x000080, 0x08001A, - 0x000904, 0x32F186, 0x000007, 0x001220, - 0x000DED, 0x008040, 0x008042, 0x10000A, - 0x40000D, 0x109544, 0x000007, 0x001020, - 0x000DED, 0x008040, 0x008042, 0x20040A, - 0x000082, 0x08001A, 0x000904, 0x338186, - 0x000007, 0x003B6D, 0x008042, 0x08000A, - 0x000E15, 0x010984, 0x342B86, 0x600007, - 0x08001A, 0x000C15, 0x010984, 0x341386, - 0x000020, 0x1A0007, 0x0002ED, 0x008040, - 0x620007, 0x00306D, 0x028042, 0x0A804A, - 0x000820, 0x0A804A, 0x000606, 0x10804A, - 0x000007, 0x282512, 0x001F32, 0x05D2F4, - 0x54D104, 0x00735C, 0x000786, 0x000007, - 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, - 0x020040, 0x004820, 0x025060, 0x40000A, - 0x024060, 0x000040, 0x454944, 0x000007, - 0x004020, 0x003AE5, 0x000040, 0x0028E5, - 0x000042, 0x48000A, 0x004904, 0x39F886, - 0x002C65, 0x000042, 0x40000A, 0x0000D5, - 0x454104, 0x000007, 0x000655, 0x054504, - 0x368286, 0x0001D5, 0x054504, 0x368086, - 0x002B65, 0x000042, 0x003AE5, 0x50004A, - 0x40000A, 0x45C3D4, 0x000007, 0x454504, - 0x000007, 0x0000CD, 0x444944, 0x000007, - 0x454504, 0x000007, 0x00014D, 0x554944, - 0x000007, 0x045144, 0x367986, 0x002C65, - 0x000042, 0x48000A, 0x4CD104, 0x000007, - 0x04C144, 0x368386, 0x000007, 0x160007, - 0x002CE5, 0x040042, 0x40000A, 0x004020, - 0x000040, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x36F086, 0x000007, 0x002402, - 0x383206, 0x005C02, 0x0025E5, 0x000042, - 0x40000A, 0x004274, 0x002AE5, 0x000042, - 0x40000A, 0x004274, 0x500112, 0x0029E5, - 0x000042, 0x40000A, 0x004234, 0x454104, - 0x000007, 0x004020, 0x000040, 0x003EE5, - 0x000020, 0x000040, 0x002DE5, 0x400152, - 0x50000A, 0x045144, 0x37DA86, 0x0000C5, - 0x003EE5, 0x004020, 0x000040, 0x002BE5, - 0x000042, 0x40000A, 0x404254, 0x000007, - 0x002AE5, 0x004020, 0x000040, 0x500132, - 0x040134, 0x005674, 0x0029E5, 0x020042, - 0x42000A, 0x000042, 0x50000A, 0x05417C, - 0x0028E5, 0x000042, 0x48000A, 0x0000C5, - 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, - 0x020042, 0x40004A, 0x50000A, 0x00423C, - 0x00567C, 0x0028E5, 0x004820, 0x000040, - 0x281D12, 0x282512, 0x001F72, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x393A86, - 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, - 0x000042, 0x40000A, 0x004104, 0x397886, - 0x002D65, 0x000042, 0x28340A, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, - 0x39E186, 0x000007, 0x000606, 0x080007, - 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, - 0x020045, 0x004020, 0x000060, 0x000365, - 0x000040, 0x002E65, 0x001A20, 0x0A1A60, - 0x000040, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x000606, 0x50004A, - 0x0017FD, 0x018042, 0x08000A, 0x000904, - 0x225A86, 0x000007, 0x00107D, 0x018042, - 0x0011FD, 0x33804A, 0x19804A, 0x20000A, - 0x000095, 0x2A1144, 0x01A144, 0x3B9086, - 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, - 0x018042, 0x0010FD, 0x09804A, 0x38000A, - 0x000095, 0x010924, 0x003A64, 0x3B8186, - 0x000007, 0x003904, 0x3B9286, 0x000007, - 0x3B9A06, 0x00000D, 0x00008D, 0x000820, - 0x00387D, 0x018040, 0x700002, 0x00117D, - 0x018042, 0x00197D, 0x29804A, 0x30000A, - 0x380002, 0x003124, 0x000424, 0x000424, - 0x002A24, 0x280502, 0x00068D, 0x000810, - 0x28143A, 0x00750D, 0x00B124, 0x002264, - 0x3D0386, 0x284402, 0x000810, 0x280C3A, - 0x0B800D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00758D, 0x00B124, 0x100102, - 0x012144, 0x3E4986, 0x001810, 0x10003A, - 0x00387D, 0x018042, 0x08000A, 0x000904, - 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, - 0x00008D, 0x023164, 0x000A64, 0x280D02, - 0x0B808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00387D, 0x018042, 0x08000A, - 0x000904, 0x3E3286, 0x030000, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x3D8286, - 0x000007, 0x002810, 0x28043A, 0x00750D, - 0x030924, 0x002264, 0x280D02, 0x02316C, - 0x28450A, 0x0B810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x00008D, 0x000A24, - 0x3E4A06, 0x100102, 0x001810, 0x10003A, - 0x0000BD, 0x003810, 0x30043A, 0x00187D, - 0x018042, 0x0018FD, 0x09804A, 0x20000A, - 0x0000AD, 0x028924, 0x07212C, 0x001010, - 0x300583, 0x300D8B, 0x3014BB, 0x301C83, - 0x002083, 0x00137D, 0x038042, 0x33844A, - 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, - 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, - 0x001E0D, 0x0005FD, 0x018042, 0x20000A, - 0x020924, 0x00068D, 0x00A96C, 0x00009D, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x3F6A86, 0x000007, 0x280502, 0x280D0A, - 0x284402, 0x001810, 0x28143A, 0x0C008D, - 0x000820, 0x0002FD, 0x018040, 0x220007, - 0x003904, 0x225886, 0x001E0D, 0x00057D, - 0x018042, 0x20000A, 0x020924, 0x0000A5, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x402A86, 0x000007, 0x280502, 0x280C02, - 0x002010, 0x28143A, 0x0C010D, 0x000820, - 0x0002FD, 0x018040, 0x225A06, 0x220007, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; - -#endif //_HWMCODE_ - - diff -urN linux-2.4.26/drivers/sound/Hwmcode.h linux-2.4.27/drivers/sound/Hwmcode.h --- linux-2.4.26/drivers/sound/Hwmcode.h 2000-06-20 07:52:36.000000000 -0700 +++ linux-2.4.27/drivers/sound/Hwmcode.h 1969-12-31 16:00:00.000000000 -0800 @@ -1,804 +0,0 @@ -//============================================================================= -// Copyright (c) 1997 Yamaha Corporation. All Rights Reserved. -// -// Title: -// hwmcode.c -// Desc: -// micro-code for CTRL & DSP -// HISTORY: -// April 03, 1997: 1st try by M. Mukojima -//============================================================================= -#define YDSXG_DSPLENGTH 0x0080 -#define YDSXG_CTRLLENGTH 0x3000 - - -static unsigned long int gdwDSPCode[YDSXG_DSPLENGTH >> 2] = { - 0x00000081, 0x000001a4, 0x0000000a, 0x0000002f, - 0x00080253, 0x01800317, 0x0000407b, 0x0000843f, - 0x0001483c, 0x0001943c, 0x0005d83c, 0x00001c3c, - 0x0000c07b, 0x00050c3f, 0x0121503c, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000 -}; - - -// -------------------------------------------- -// DS-1E Controller InstructionRAM Code -// 1999/06/21 -// Buf441 slot is Enabled. -// -------------------------------------------- -// 04/09?@creat -// 04/12 stop nise fix -// 06/21?@WorkingOff timming -static unsigned long gdwCtrl1eCode[YDSXG_CTRLLENGTH >> 2] = { - 0x000007, 0x240007, 0x0C0007, 0x1C0007, - 0x060007, 0x700002, 0x000020, 0x030040, - 0x007104, 0x004286, 0x030040, 0x000F0D, - 0x000810, 0x20043A, 0x000282, 0x00020D, - 0x000810, 0x20043A, 0x001282, 0x200E82, - 0x00800D, 0x000810, 0x20043A, 0x001A82, - 0x03460D, 0x000810, 0x10043A, 0x02EC0D, - 0x000810, 0x18043A, 0x00010D, 0x020015, - 0x0000FD, 0x000020, 0x038860, 0x039060, - 0x038060, 0x038040, 0x038040, 0x038040, - 0x018040, 0x000A7D, 0x038040, 0x038040, - 0x018040, 0x200402, 0x000882, 0x08001A, - 0x000904, 0x017186, 0x000007, 0x260007, - 0x400007, 0x000007, 0x03258D, 0x000810, - 0x18043A, 0x260007, 0x284402, 0x00087D, - 0x018042, 0x00160A, 0x05A206, 0x000007, - 0x440007, 0x00230D, 0x000810, 0x08043A, - 0x22FA06, 0x000007, 0x0007FD, 0x018042, - 0x08000A, 0x000904, 0x02AB86, 0x000195, - 0x090D04, 0x000007, 0x000820, 0x0000F5, - 0x000B7D, 0x01F060, 0x0000FD, 0x033A06, - 0x018040, 0x000A7D, 0x038042, 0x13804A, - 0x18000A, 0x001820, 0x059060, 0x058860, - 0x018040, 0x0000FD, 0x018042, 0x70000A, - 0x000115, 0x071144, 0x033B86, 0x030000, - 0x007020, 0x036206, 0x018040, 0x00360D, - 0x000810, 0x08043A, 0x232206, 0x000007, - 0x02EC0D, 0x000810, 0x18043A, 0x019A06, - 0x000007, 0x240007, 0x000F8D, 0x000810, - 0x00163A, 0x002402, 0x005C02, 0x0028FD, - 0x000020, 0x018040, 0x08000D, 0x000815, - 0x510984, 0x000007, 0x00004D, 0x000E5D, - 0x000E02, 0x00430D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x00008D, 0x000924, - 0x000F02, 0x00470D, 0x000810, 0x08043A, - 0x2E1206, 0x000007, 0x480480, 0x001210, - 0x28043A, 0x00778D, 0x000810, 0x280C3A, - 0x00068D, 0x000810, 0x28143A, 0x284402, - 0x03258D, 0x000810, 0x18043A, 0x07FF8D, - 0x000820, 0x0002FD, 0x018040, 0x260007, - 0x200007, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x051286, 0x000007, 0x240007, - 0x02EC0D, 0x000810, 0x18043A, 0x00387D, - 0x018042, 0x08000A, 0x001015, 0x010984, - 0x019B86, 0x000007, 0x01B206, 0x000007, - 0x0008FD, 0x018042, 0x18000A, 0x001904, - 0x22B886, 0x280007, 0x001810, 0x28043A, - 0x280C02, 0x00000D, 0x000810, 0x28143A, - 0x08808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00020D, 0x189904, 0x000007, - 0x00402D, 0x0000BD, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x065A86, 0x000007, - 0x000100, 0x000A20, 0x00047D, 0x018040, - 0x018042, 0x20000A, 0x003015, 0x012144, - 0x036186, 0x000007, 0x002104, 0x036186, - 0x000007, 0x000F8D, 0x000810, 0x280C3A, - 0x023944, 0x07C986, 0x000007, 0x001810, - 0x28043A, 0x08810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x002810, 0x78003A, - 0x00788D, 0x000810, 0x08043A, 0x2A1206, - 0x000007, 0x00400D, 0x001015, 0x189904, - 0x292904, 0x393904, 0x000007, 0x070206, - 0x000007, 0x0004F5, 0x00007D, 0x000020, - 0x00008D, 0x010860, 0x018040, 0x00047D, - 0x038042, 0x21804A, 0x18000A, 0x021944, - 0x229086, 0x000007, 0x004075, 0x71F104, - 0x000007, 0x010042, 0x28000A, 0x002904, - 0x225886, 0x000007, 0x003C0D, 0x30A904, - 0x000007, 0x00077D, 0x018042, 0x08000A, - 0x000904, 0x08DA86, 0x00057D, 0x002820, - 0x03B060, 0x08F206, 0x018040, 0x003020, - 0x03A860, 0x018040, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x08FA86, 0x000007, - 0x00057D, 0x018042, 0x28040A, 0x000E8D, - 0x000810, 0x280C3A, 0x00000D, 0x000810, - 0x28143A, 0x09000D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x003DFD, 0x000020, - 0x018040, 0x00107D, 0x009D8D, 0x000810, - 0x08043A, 0x2A1206, 0x000007, 0x000815, - 0x08001A, 0x010984, 0x0A5186, 0x00137D, - 0x200500, 0x280F20, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x038A60, 0x018040, 0x00107D, 0x018042, - 0x08000A, 0x000215, 0x010984, 0x3A8186, - 0x000007, 0x007FBD, 0x383DC4, 0x000007, - 0x001A7D, 0x001375, 0x018042, 0x09004A, - 0x10000A, 0x0B8D04, 0x139504, 0x000007, - 0x000820, 0x019060, 0x001104, 0x225886, - 0x010040, 0x0017FD, 0x018042, 0x08000A, - 0x000904, 0x225A86, 0x000007, 0x00197D, - 0x038042, 0x09804A, 0x10000A, 0x000924, - 0x001664, 0x0011FD, 0x038042, 0x2B804A, - 0x19804A, 0x00008D, 0x218944, 0x000007, - 0x002244, 0x0C1986, 0x000007, 0x001A64, - 0x002A24, 0x00197D, 0x080102, 0x100122, - 0x000820, 0x039060, 0x018040, 0x003DFD, - 0x00008D, 0x000820, 0x018040, 0x001375, - 0x001A7D, 0x010042, 0x09804A, 0x10000A, - 0x00021D, 0x0189E4, 0x2992E4, 0x309144, - 0x000007, 0x00060D, 0x000A15, 0x000C1D, - 0x001025, 0x00A9E4, 0x012BE4, 0x000464, - 0x01B3E4, 0x0232E4, 0x000464, 0x000464, - 0x000464, 0x000464, 0x00040D, 0x08B1C4, - 0x000007, 0x000820, 0x000BF5, 0x030040, - 0x00197D, 0x038042, 0x09804A, 0x000A24, - 0x08000A, 0x080E64, 0x000007, 0x100122, - 0x000820, 0x031060, 0x010040, 0x0064AC, - 0x00027D, 0x000020, 0x018040, 0x00107D, - 0x018042, 0x0011FD, 0x3B804A, 0x09804A, - 0x20000A, 0x000095, 0x1A1144, 0x00A144, - 0x0E5886, 0x00040D, 0x00B984, 0x0E5986, - 0x0018FD, 0x018042, 0x0010FD, 0x09804A, - 0x28000A, 0x000095, 0x010924, 0x002A64, - 0x0E4986, 0x000007, 0x002904, 0x0E5A86, - 0x000007, 0x0E6206, 0x080002, 0x00008D, - 0x00387D, 0x000820, 0x018040, 0x00127D, - 0x018042, 0x10000A, 0x003904, 0x0F0986, - 0x00080D, 0x7FFFB5, 0x00B984, 0x0ED986, - 0x000025, 0x0FB206, 0x00002D, 0x000015, - 0x00082D, 0x02E00D, 0x000820, 0x0FFA06, - 0x00000D, 0x7F8035, 0x00B984, 0x0FA986, - 0x400025, 0x00008D, 0x110944, 0x000007, - 0x00018D, 0x109504, 0x000007, 0x009164, - 0x000424, 0x000424, 0x000424, 0x100102, - 0x280002, 0x02DF0D, 0x000820, 0x0FFA06, - 0x00018D, 0x00042D, 0x00008D, 0x109504, - 0x000007, 0x00020D, 0x109184, 0x000007, - 0x02DF8D, 0x000820, 0x00008D, 0x0038FD, - 0x018040, 0x003BFD, 0x001020, 0x03A860, - 0x000815, 0x313184, 0x212184, 0x000007, - 0x03B060, 0x03A060, 0x018040, 0x0022FD, - 0x000095, 0x010924, 0x000424, 0x000424, - 0x001264, 0x100102, 0x000820, 0x039060, - 0x018040, 0x001924, 0x010F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x000424, 0x000424, - 0x00117D, 0x018042, 0x08000A, 0x000A24, - 0x280502, 0x280C02, 0x09800D, 0x000820, - 0x0002FD, 0x018040, 0x200007, 0x0022FD, - 0x018042, 0x08000A, 0x000095, 0x280DC4, - 0x011924, 0x00197D, 0x018042, 0x0011FD, - 0x09804A, 0x10000A, 0x0000B5, 0x113144, - 0x0A8D04, 0x000007, 0x080A44, 0x129504, - 0x000007, 0x0023FD, 0x001020, 0x038040, - 0x101244, 0x000007, 0x000820, 0x039060, - 0x018040, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x123286, 0x000007, 0x003BFD, - 0x000100, 0x000A10, 0x0B807A, 0x13804A, - 0x090984, 0x000007, 0x000095, 0x013D04, - 0x12B886, 0x10000A, 0x100002, 0x090984, - 0x000007, 0x038042, 0x11804A, 0x090D04, - 0x000007, 0x10000A, 0x090D84, 0x000007, - 0x00257D, 0x000820, 0x018040, 0x00010D, - 0x000810, 0x28143A, 0x00127D, 0x018042, - 0x20000A, 0x00197D, 0x018042, 0x00117D, - 0x31804A, 0x10000A, 0x003124, 0x013B8D, - 0x00397D, 0x000820, 0x058040, 0x038042, - 0x09844A, 0x000606, 0x08040A, 0x300102, - 0x003124, 0x000424, 0x000424, 0x001224, - 0x280502, 0x001A4C, 0x143986, 0x700002, - 0x00002D, 0x030000, 0x00387D, 0x018042, - 0x10000A, 0x146206, 0x002124, 0x0000AD, - 0x100002, 0x00010D, 0x000924, 0x006B24, - 0x014A0D, 0x00397D, 0x000820, 0x058040, - 0x038042, 0x09844A, 0x000606, 0x08040A, - 0x003264, 0x00008D, 0x000A24, 0x001020, - 0x00227D, 0x018040, 0x014F8D, 0x000810, - 0x08043A, 0x2B5A06, 0x000007, 0x002820, - 0x00207D, 0x018040, 0x00117D, 0x038042, - 0x13804A, 0x33800A, 0x00387D, 0x018042, - 0x08000A, 0x000904, 0x177286, 0x000007, - 0x00008D, 0x030964, 0x015B0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x380102, 0x000424, - 0x000424, 0x001224, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x15DA86, 0x000007, - 0x280502, 0x001A4C, 0x177186, 0x000007, - 0x032164, 0x00632C, 0x003DFD, 0x018042, - 0x08000A, 0x000095, 0x090904, 0x000007, - 0x000820, 0x001A4C, 0x169986, 0x018040, - 0x030000, 0x16B206, 0x002124, 0x00010D, - 0x000924, 0x006B24, 0x016F0D, 0x00397D, - 0x000820, 0x058040, 0x038042, 0x09844A, - 0x000606, 0x08040A, 0x003A64, 0x000095, - 0x001224, 0x0002FD, 0x018042, 0x08000A, - 0x000904, 0x171286, 0x000007, 0x01760D, - 0x000810, 0x08043A, 0x2B5A06, 0x000007, - 0x160A06, 0x000007, 0x007020, 0x08010A, - 0x10012A, 0x0020FD, 0x038860, 0x039060, - 0x018040, 0x00227D, 0x018042, 0x003DFD, - 0x08000A, 0x31844A, 0x000904, 0x181086, - 0x18008B, 0x00008D, 0x189904, 0x00312C, - 0x18E206, 0x000007, 0x00324C, 0x186B86, - 0x000007, 0x001904, 0x186886, 0x000007, - 0x000095, 0x199144, 0x00222C, 0x003124, - 0x00636C, 0x000E3D, 0x001375, 0x000BFD, - 0x010042, 0x09804A, 0x10000A, 0x038AEC, - 0x0393EC, 0x00224C, 0x18E186, 0x000007, - 0x00008D, 0x189904, 0x00226C, 0x00322C, - 0x30050A, 0x301DAB, 0x002083, 0x0018FD, - 0x018042, 0x08000A, 0x018924, 0x300502, - 0x001083, 0x001875, 0x010042, 0x10000A, - 0x00008D, 0x010924, 0x001375, 0x330542, - 0x330CCB, 0x332CCB, 0x3334CB, 0x333CCB, - 0x3344CB, 0x334CCB, 0x3354CB, 0x305C8B, - 0x006083, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x19B286, 0x000007, 0x001E2D, - 0x0005FD, 0x018042, 0x08000A, 0x028924, - 0x280502, 0x00060D, 0x000810, 0x280C3A, - 0x00008D, 0x000810, 0x28143A, 0x0A808D, - 0x000820, 0x0002F5, 0x010040, 0x220007, - 0x001275, 0x030042, 0x21004A, 0x00008D, - 0x1A0944, 0x000007, 0x01AB8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0001F5, - 0x030042, 0x0D004A, 0x10000A, 0x089144, - 0x000007, 0x000820, 0x010040, 0x0025F5, - 0x0A3144, 0x000007, 0x000820, 0x032860, - 0x030040, 0x00217D, 0x038042, 0x0B804A, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00008D, 0x000124, 0x00012C, 0x000E64, - 0x001A64, 0x00636C, 0x08010A, 0x10012A, - 0x000820, 0x031060, 0x030040, 0x0020FD, - 0x018042, 0x08000A, 0x00227D, 0x018042, - 0x10000A, 0x000820, 0x031060, 0x030040, - 0x00197D, 0x018042, 0x08000A, 0x0022FD, - 0x038042, 0x10000A, 0x000820, 0x031060, - 0x030040, 0x090D04, 0x000007, 0x000820, - 0x030040, 0x038042, 0x0B804A, 0x10000A, - 0x000820, 0x031060, 0x030040, 0x038042, - 0x13804A, 0x19804A, 0x110D04, 0x198D04, - 0x000007, 0x08000A, 0x001020, 0x031860, - 0x030860, 0x030040, 0x00008D, 0x0B0944, - 0x000007, 0x000820, 0x010040, 0x0005F5, - 0x030042, 0x08000A, 0x000820, 0x010040, - 0x0000F5, 0x010042, 0x08000A, 0x000904, - 0x1D9886, 0x001E75, 0x030042, 0x01044A, - 0x000C0A, 0x1DAA06, 0x000007, 0x000402, - 0x000C02, 0x00177D, 0x001AF5, 0x018042, - 0x03144A, 0x031C4A, 0x03244A, 0x032C4A, - 0x03344A, 0x033C4A, 0x03444A, 0x004C0A, - 0x00043D, 0x0013F5, 0x001AFD, 0x030042, - 0x0B004A, 0x1B804A, 0x13804A, 0x20000A, - 0x089144, 0x19A144, 0x0389E4, 0x0399EC, - 0x005502, 0x005D0A, 0x030042, 0x0B004A, - 0x1B804A, 0x13804A, 0x20000A, 0x089144, - 0x19A144, 0x0389E4, 0x0399EC, 0x006502, - 0x006D0A, 0x030042, 0x0B004A, 0x19004A, - 0x2B804A, 0x13804A, 0x21804A, 0x30000A, - 0x089144, 0x19A144, 0x2AB144, 0x0389E4, - 0x0399EC, 0x007502, 0x007D0A, 0x03A9E4, - 0x000702, 0x00107D, 0x000415, 0x018042, - 0x08000A, 0x0109E4, 0x000F02, 0x002AF5, - 0x0019FD, 0x010042, 0x09804A, 0x10000A, - 0x000934, 0x001674, 0x0029F5, 0x010042, - 0x10000A, 0x00917C, 0x002075, 0x010042, - 0x08000A, 0x000904, 0x200A86, 0x0026F5, - 0x0027F5, 0x030042, 0x09004A, 0x10000A, - 0x000A3C, 0x00167C, 0x001A75, 0x000BFD, - 0x010042, 0x51804A, 0x48000A, 0x160007, - 0x001075, 0x010042, 0x282C0A, 0x281D12, - 0x282512, 0x001F32, 0x1E0007, 0x0E0007, - 0x001975, 0x010042, 0x002DF5, 0x0D004A, - 0x10000A, 0x009144, 0x20EA86, 0x010042, - 0x28340A, 0x000E5D, 0x00008D, 0x000375, - 0x000820, 0x010040, 0x05D2F4, 0x54D104, - 0x00735C, 0x218B86, 0x000007, 0x0C0007, - 0x080007, 0x0A0007, 0x02178D, 0x000810, - 0x08043A, 0x34B206, 0x000007, 0x219206, - 0x000007, 0x080007, 0x002275, 0x010042, - 0x20000A, 0x002104, 0x225886, 0x001E2D, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x21CA86, 0x000007, 0x002010, 0x30043A, - 0x00057D, 0x0180C3, 0x08000A, 0x028924, - 0x280502, 0x280C02, 0x0A810D, 0x000820, - 0x0002F5, 0x010040, 0x220007, 0x0004FD, - 0x018042, 0x70000A, 0x030000, 0x007020, - 0x07FA06, 0x018040, 0x022B8D, 0x000810, - 0x08043A, 0x2CAA06, 0x000007, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x22C286, - 0x000007, 0x020206, 0x000007, 0x000875, - 0x0009FD, 0x00010D, 0x234206, 0x000295, - 0x000B75, 0x00097D, 0x00000D, 0x000515, - 0x010042, 0x18000A, 0x001904, 0x2A0086, - 0x0006F5, 0x001020, 0x010040, 0x0004F5, - 0x000820, 0x010040, 0x000775, 0x010042, - 0x09804A, 0x10000A, 0x001124, 0x000904, - 0x23F286, 0x000815, 0x080102, 0x101204, - 0x241206, 0x000575, 0x081204, 0x000007, - 0x100102, 0x000575, 0x000425, 0x021124, - 0x100102, 0x000820, 0x031060, 0x010040, - 0x001924, 0x2A0086, 0x00008D, 0x000464, - 0x009D04, 0x291086, 0x180102, 0x000575, - 0x010042, 0x28040A, 0x00018D, 0x000924, - 0x280D02, 0x00000D, 0x000924, 0x281502, - 0x10000D, 0x000820, 0x0002F5, 0x010040, - 0x200007, 0x001175, 0x0002FD, 0x018042, - 0x08000A, 0x000904, 0x24FA86, 0x000007, - 0x000100, 0x080B20, 0x130B60, 0x1B0B60, - 0x030A60, 0x010040, 0x050042, 0x3D004A, - 0x35004A, 0x2D004A, 0x20000A, 0x0006F5, - 0x010042, 0x28140A, 0x0004F5, 0x010042, - 0x08000A, 0x000315, 0x010D04, 0x260286, - 0x004015, 0x000095, 0x010D04, 0x25F086, - 0x100022, 0x10002A, 0x261A06, 0x000007, - 0x333104, 0x2AA904, 0x000007, 0x032124, - 0x280502, 0x284402, 0x001124, 0x400102, - 0x000424, 0x000424, 0x003224, 0x00292C, - 0x00636C, 0x277386, 0x000007, 0x02B164, - 0x000464, 0x000464, 0x00008D, 0x000A64, - 0x280D02, 0x10008D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x00008D, 0x38B904, - 0x000007, 0x03296C, 0x30010A, 0x0002F5, - 0x010042, 0x08000A, 0x000904, 0x270286, - 0x000007, 0x00212C, 0x28050A, 0x00316C, - 0x00046C, 0x00046C, 0x28450A, 0x001124, - 0x006B64, 0x100102, 0x00008D, 0x01096C, - 0x280D0A, 0x10010D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x004124, 0x000424, - 0x000424, 0x003224, 0x300102, 0x032944, - 0x27FA86, 0x000007, 0x300002, 0x0004F5, - 0x010042, 0x08000A, 0x000315, 0x010D04, - 0x284086, 0x003124, 0x000464, 0x300102, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x284A86, 0x000007, 0x284402, 0x003124, - 0x300502, 0x003924, 0x300583, 0x000883, - 0x0005F5, 0x010042, 0x28040A, 0x00008D, - 0x008124, 0x280D02, 0x00008D, 0x008124, - 0x281502, 0x10018D, 0x000820, 0x0002F5, - 0x010040, 0x220007, 0x001025, 0x000575, - 0x030042, 0x09004A, 0x10000A, 0x0A0904, - 0x121104, 0x000007, 0x001020, 0x050860, - 0x050040, 0x0006FD, 0x018042, 0x09004A, - 0x10000A, 0x0000A5, 0x0A0904, 0x121104, - 0x000007, 0x000820, 0x019060, 0x010040, - 0x0002F5, 0x010042, 0x08000A, 0x000904, - 0x29CA86, 0x000007, 0x244206, 0x000007, - 0x000606, 0x000007, 0x0002F5, 0x010042, - 0x08000A, 0x000904, 0x2A1A86, 0x000007, - 0x000100, 0x080B20, 0x138B60, 0x1B8B60, - 0x238B60, 0x2B8B60, 0x338B60, 0x3B8B60, - 0x438B60, 0x4B8B60, 0x538B60, 0x5B8B60, - 0x638B60, 0x6B8B60, 0x738B60, 0x7B8B60, - 0x038F60, 0x0B8F60, 0x138F60, 0x1B8F60, - 0x238F60, 0x2B8F60, 0x338F60, 0x3B8F60, - 0x438F60, 0x4B8F60, 0x538F60, 0x5B8F60, - 0x638F60, 0x6B8F60, 0x738F60, 0x7B8F60, - 0x038A60, 0x000606, 0x018040, 0x00008D, - 0x000A64, 0x280D02, 0x000A24, 0x00027D, - 0x018042, 0x10000A, 0x001224, 0x0003FD, - 0x018042, 0x08000A, 0x000904, 0x2C0A86, - 0x000007, 0x00018D, 0x000A24, 0x000464, - 0x000464, 0x080102, 0x000924, 0x000424, - 0x000424, 0x100102, 0x02000D, 0x009144, - 0x2C6186, 0x000007, 0x0001FD, 0x018042, - 0x08000A, 0x000A44, 0x2C4386, 0x018042, - 0x0A000D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00027D, 0x001020, 0x000606, - 0x018040, 0x0002F5, 0x010042, 0x08000A, - 0x000904, 0x2CB286, 0x000007, 0x00037D, - 0x018042, 0x08000A, 0x000904, 0x2CE286, - 0x000007, 0x000075, 0x002E7D, 0x010042, - 0x0B804A, 0x000020, 0x000904, 0x000686, - 0x010040, 0x31844A, 0x30048B, 0x000883, - 0x00008D, 0x000810, 0x28143A, 0x00008D, - 0x000810, 0x280C3A, 0x000675, 0x010042, - 0x08000A, 0x003815, 0x010924, 0x280502, - 0x0B000D, 0x000820, 0x0002F5, 0x010040, - 0x000606, 0x220007, 0x000464, 0x000464, - 0x000606, 0x000007, 0x000134, 0x007F8D, - 0x00093C, 0x281D12, 0x282512, 0x001F32, - 0x0E0007, 0x00010D, 0x00037D, 0x000820, - 0x018040, 0x05D2F4, 0x000007, 0x080007, - 0x00037D, 0x018042, 0x08000A, 0x000904, - 0x2E8A86, 0x000007, 0x000606, 0x000007, - 0x000007, 0x000012, 0x100007, 0x320007, - 0x600007, 0x460007, 0x100080, 0x48001A, - 0x004904, 0x2EF186, 0x000007, 0x001210, - 0x58003A, 0x000145, 0x5C5D04, 0x000007, - 0x000080, 0x48001A, 0x004904, 0x2F4186, - 0x000007, 0x001210, 0x50003A, 0x005904, - 0x2F9886, 0x000045, 0x0000C5, 0x7FFFF5, - 0x7FFF7D, 0x07D524, 0x004224, 0x500102, - 0x200502, 0x000082, 0x40001A, 0x004104, - 0x2FC986, 0x000007, 0x003865, 0x40001A, - 0x004020, 0x00104D, 0x04C184, 0x31AB86, - 0x000040, 0x040007, 0x000165, 0x000145, - 0x004020, 0x000040, 0x000765, 0x080080, - 0x40001A, 0x004104, 0x305986, 0x000007, - 0x001210, 0x40003A, 0x004104, 0x30B286, - 0x00004D, 0x0000CD, 0x004810, 0x20043A, - 0x000882, 0x40001A, 0x004104, 0x30C186, - 0x000007, 0x004820, 0x005904, 0x319886, - 0x000040, 0x0007E5, 0x200480, 0x2816A0, - 0x3216E0, 0x3A16E0, 0x4216E0, 0x021260, - 0x000040, 0x000032, 0x400075, 0x00007D, - 0x07D574, 0x200512, 0x000082, 0x40001A, - 0x004104, 0x317186, 0x000007, 0x038A06, - 0x640007, 0x0000E5, 0x000020, 0x000040, - 0x000A65, 0x000020, 0x020040, 0x020040, - 0x000040, 0x000165, 0x000042, 0x70000A, - 0x007104, 0x323286, 0x000007, 0x060007, - 0x019A06, 0x640007, 0x050000, 0x007020, - 0x000040, 0x038A06, 0x640007, 0x000007, - 0x00306D, 0x028860, 0x029060, 0x08000A, - 0x028860, 0x008040, 0x100012, 0x00100D, - 0x009184, 0x32D186, 0x000E0D, 0x009184, - 0x33E186, 0x000007, 0x300007, 0x001020, - 0x003B6D, 0x008040, 0x000080, 0x08001A, - 0x000904, 0x32F186, 0x000007, 0x001220, - 0x000DED, 0x008040, 0x008042, 0x10000A, - 0x40000D, 0x109544, 0x000007, 0x001020, - 0x000DED, 0x008040, 0x008042, 0x20040A, - 0x000082, 0x08001A, 0x000904, 0x338186, - 0x000007, 0x003B6D, 0x008042, 0x08000A, - 0x000E15, 0x010984, 0x342B86, 0x600007, - 0x08001A, 0x000C15, 0x010984, 0x341386, - 0x000020, 0x1A0007, 0x0002ED, 0x008040, - 0x620007, 0x00306D, 0x028042, 0x0A804A, - 0x000820, 0x0A804A, 0x000606, 0x10804A, - 0x000007, 0x282512, 0x001F32, 0x05D2F4, - 0x54D104, 0x00735C, 0x000786, 0x000007, - 0x0C0007, 0x0A0007, 0x1C0007, 0x003465, - 0x020040, 0x004820, 0x025060, 0x40000A, - 0x024060, 0x000040, 0x454944, 0x000007, - 0x004020, 0x003AE5, 0x000040, 0x0028E5, - 0x000042, 0x48000A, 0x004904, 0x39F886, - 0x002C65, 0x000042, 0x40000A, 0x0000D5, - 0x454104, 0x000007, 0x000655, 0x054504, - 0x368286, 0x0001D5, 0x054504, 0x368086, - 0x002B65, 0x000042, 0x003AE5, 0x50004A, - 0x40000A, 0x45C3D4, 0x000007, 0x454504, - 0x000007, 0x0000CD, 0x444944, 0x000007, - 0x454504, 0x000007, 0x00014D, 0x554944, - 0x000007, 0x045144, 0x367986, 0x002C65, - 0x000042, 0x48000A, 0x4CD104, 0x000007, - 0x04C144, 0x368386, 0x000007, 0x160007, - 0x002CE5, 0x040042, 0x40000A, 0x004020, - 0x000040, 0x002965, 0x000042, 0x40000A, - 0x004104, 0x36F086, 0x000007, 0x002402, - 0x383206, 0x005C02, 0x0025E5, 0x000042, - 0x40000A, 0x004274, 0x002AE5, 0x000042, - 0x40000A, 0x004274, 0x500112, 0x0029E5, - 0x000042, 0x40000A, 0x004234, 0x454104, - 0x000007, 0x004020, 0x000040, 0x003EE5, - 0x000020, 0x000040, 0x002DE5, 0x400152, - 0x50000A, 0x045144, 0x37DA86, 0x0000C5, - 0x003EE5, 0x004020, 0x000040, 0x002BE5, - 0x000042, 0x40000A, 0x404254, 0x000007, - 0x002AE5, 0x004020, 0x000040, 0x500132, - 0x040134, 0x005674, 0x0029E5, 0x020042, - 0x42000A, 0x000042, 0x50000A, 0x05417C, - 0x0028E5, 0x000042, 0x48000A, 0x0000C5, - 0x4CC144, 0x38A086, 0x0026E5, 0x0027E5, - 0x020042, 0x40004A, 0x50000A, 0x00423C, - 0x00567C, 0x0028E5, 0x004820, 0x000040, - 0x281D12, 0x282512, 0x001F72, 0x002965, - 0x000042, 0x40000A, 0x004104, 0x393A86, - 0x0E0007, 0x160007, 0x1E0007, 0x003EE5, - 0x000042, 0x40000A, 0x004104, 0x397886, - 0x002D65, 0x000042, 0x28340A, 0x003465, - 0x020042, 0x42004A, 0x004020, 0x4A004A, - 0x50004A, 0x05D2F4, 0x54D104, 0x00735C, - 0x39E186, 0x000007, 0x000606, 0x080007, - 0x0C0007, 0x080007, 0x0A0007, 0x0001E5, - 0x020045, 0x004020, 0x000060, 0x000365, - 0x000040, 0x002E65, 0x001A20, 0x0A1A60, - 0x000040, 0x003465, 0x020042, 0x42004A, - 0x004020, 0x4A004A, 0x000606, 0x50004A, - 0x0017FD, 0x018042, 0x08000A, 0x000904, - 0x225A86, 0x000007, 0x00107D, 0x018042, - 0x0011FD, 0x33804A, 0x19804A, 0x20000A, - 0x000095, 0x2A1144, 0x01A144, 0x3B9086, - 0x00040D, 0x00B184, 0x3B9186, 0x0018FD, - 0x018042, 0x0010FD, 0x09804A, 0x38000A, - 0x000095, 0x010924, 0x003A64, 0x3B8186, - 0x000007, 0x003904, 0x3B9286, 0x000007, - 0x3B9A06, 0x00000D, 0x00008D, 0x000820, - 0x00387D, 0x018040, 0x700002, 0x00117D, - 0x018042, 0x00197D, 0x29804A, 0x30000A, - 0x380002, 0x003124, 0x000424, 0x000424, - 0x002A24, 0x280502, 0x00068D, 0x000810, - 0x28143A, 0x00750D, 0x00B124, 0x002264, - 0x3D0386, 0x284402, 0x000810, 0x280C3A, - 0x0B800D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00758D, 0x00B124, 0x100102, - 0x012144, 0x3E4986, 0x001810, 0x10003A, - 0x00387D, 0x018042, 0x08000A, 0x000904, - 0x3E4886, 0x030000, 0x3E4A06, 0x0000BD, - 0x00008D, 0x023164, 0x000A64, 0x280D02, - 0x0B808D, 0x000820, 0x0002FD, 0x018040, - 0x200007, 0x00387D, 0x018042, 0x08000A, - 0x000904, 0x3E3286, 0x030000, 0x0002FD, - 0x018042, 0x08000A, 0x000904, 0x3D8286, - 0x000007, 0x002810, 0x28043A, 0x00750D, - 0x030924, 0x002264, 0x280D02, 0x02316C, - 0x28450A, 0x0B810D, 0x000820, 0x0002FD, - 0x018040, 0x200007, 0x00008D, 0x000A24, - 0x3E4A06, 0x100102, 0x001810, 0x10003A, - 0x0000BD, 0x003810, 0x30043A, 0x00187D, - 0x018042, 0x0018FD, 0x09804A, 0x20000A, - 0x0000AD, 0x028924, 0x07212C, 0x001010, - 0x300583, 0x300D8B, 0x3014BB, 0x301C83, - 0x002083, 0x00137D, 0x038042, 0x33844A, - 0x33ACCB, 0x33B4CB, 0x33BCCB, 0x33C4CB, - 0x33CCCB, 0x33D4CB, 0x305C8B, 0x006083, - 0x001E0D, 0x0005FD, 0x018042, 0x20000A, - 0x020924, 0x00068D, 0x00A96C, 0x00009D, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x3F6A86, 0x000007, 0x280502, 0x280D0A, - 0x284402, 0x001810, 0x28143A, 0x0C008D, - 0x000820, 0x0002FD, 0x018040, 0x220007, - 0x003904, 0x225886, 0x001E0D, 0x00057D, - 0x018042, 0x20000A, 0x020924, 0x0000A5, - 0x0002FD, 0x018042, 0x08000A, 0x000904, - 0x402A86, 0x000007, 0x280502, 0x280C02, - 0x002010, 0x28143A, 0x0C010D, 0x000820, - 0x0002FD, 0x018040, 0x225A06, 0x220007, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000, - 0x000000, 0x000000, 0x000000, 0x000000 -}; diff -urN linux-2.4.26/drivers/sound/ad1848.c linux-2.4.27/drivers/sound/ad1848.c --- linux-2.4.26/drivers/sound/ad1848.c 2003-06-13 07:51:36.000000000 -0700 +++ linux-2.4.27/drivers/sound/ad1848.c 2004-08-07 16:26:05.549383553 -0700 @@ -636,6 +636,7 @@ devc->supported_devices = MODE3_MIXER_DEVICES; break; case MD_4232: + case MD_4235: case MD_4236: devc->supported_devices = MODE3_MIXER_DEVICES; break; diff -urN linux-2.4.26/drivers/sound/cmpci.c linux-2.4.27/drivers/sound/cmpci.c --- linux-2.4.26/drivers/sound/cmpci.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/sound/cmpci.c 2004-08-07 16:26:05.552383676 -0700 @@ -3595,7 +3595,7 @@ MODULE_DESCRIPTION("CM8x38 Audio Driver"); MODULE_LICENSE("GPL"); -static void __devinit cm_remove(struct pci_dev *dev) +static void __devexit cm_remove(struct pci_dev *dev) { struct cm_state *s = pci_get_drvdata(dev); @@ -3643,10 +3643,10 @@ MODULE_DEVICE_TABLE(pci, id_table); static struct pci_driver cm_driver = { - name: "cmpci", - id_table: id_table, - probe: cm_probe, - remove: cm_remove + .name = "cmpci", + .id_table = id_table, + .probe = cm_probe, + .remove = __devexit_p(cm_remove) }; static int __init init_cmpci(void) diff -urN linux-2.4.26/drivers/sound/i810_audio.c linux-2.4.27/drivers/sound/i810_audio.c --- linux-2.4.26/drivers/sound/i810_audio.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/sound/i810_audio.c 2004-08-07 16:26:05.556383841 -0700 @@ -99,54 +99,14 @@ #include #include #include +#include #include #include -#ifndef PCI_DEVICE_ID_INTEL_82801 -#define PCI_DEVICE_ID_INTEL_82801 0x2415 -#endif -#ifndef PCI_DEVICE_ID_INTEL_82901 -#define PCI_DEVICE_ID_INTEL_82901 0x2425 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH2 -#define PCI_DEVICE_ID_INTEL_ICH2 0x2445 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH3 -#define PCI_DEVICE_ID_INTEL_ICH3 0x2485 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH4 -#define PCI_DEVICE_ID_INTEL_ICH4 0x24c5 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH5 -#define PCI_DEVICE_ID_INTEL_ICH5 0x24d5 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ICH6_3 -#define PCI_DEVICE_ID_INTEL_ICH6_3 0x266e -#endif -#ifndef PCI_DEVICE_ID_INTEL_440MX -#define PCI_DEVICE_ID_INTEL_440MX 0x7195 -#endif -#ifndef PCI_DEVICE_ID_INTEL_ESB_5 -#define PCI_DEVICE_ID_INTEL_ESB_5 0x25a6 -#endif -#ifndef PCI_DEVICE_ID_SI_7012 -#define PCI_DEVICE_ID_SI_7012 0x7012 -#endif -#ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO 0x01b1 -#endif -#ifndef PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO 0x006a -#endif -#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO -#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da -#endif -#ifndef PCI_DEVICE_ID_AMD_768_AUDIO -#define PCI_DEVICE_ID_AMD_768_AUDIO 0x7445 -#endif -#ifndef PCI_DEVICE_ID_AMD_8111_AC97 -#define PCI_DEVICE_ID_AMD_8111_AC97 0x746d -#endif +#define DRIVER_VERSION "1.01" + +#define MODULOP2(a, b) ((a) & ((b) - 1)) +#define MASKP2(a, b) ((a) & ~((b) - 1)) static int ftsodell; static int strict_clocking; @@ -209,6 +169,7 @@ #define ENUM_ENGINE(PRE,DIG) \ enum { \ + PRE##_BASE = 0x##DIG##0, /* Base Address */ \ PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ @@ -256,8 +217,6 @@ #define INT_GPI (1<<0) #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI) -#define DRIVER_VERSION "0.24" - /* magic numbers to protect our data structures */ #define I810_CARD_MAGIC 0x5072696E /* "Prin" */ #define I810_STATE_MAGIC 0x63657373 /* "cess" */ @@ -323,19 +282,19 @@ }; static struct pci_device_id i810_pci_tbl [] = { - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH2, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH4, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH4}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH5, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH5}, {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012}, @@ -345,13 +304,13 @@ PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE}, {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE}, - {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_768_AUDIO, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768}, - {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_AC97, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_AUDIO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD8111}, {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH4}, - {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_3, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH4}, {0,} @@ -491,8 +450,12 @@ /* extract register offset from codec struct */ #define IO_REG_OFF(codec) (((struct i810_card *) codec->private_data)->ac97_id_map[codec->id]) +#define GET_CIV(port) MODULOP2(inb((port) + OFF_CIV), SG_LEN) +#define GET_LVI(port) MODULOP2(inb((port) + OFF_LVI), SG_LEN) + /* set LVI from CIV */ -#define CIV_TO_LVI(port, off) outb((inb(port+OFF_CIV)+off) & 31, port+OFF_LVI) +#define CIV_TO_LVI(port, off) \ + outb(MODULOP2(GET_CIV((port)) + (off), SG_LEN), (port) + OFF_LVI) static struct i810_card *devs = NULL; @@ -762,7 +725,7 @@ port_picb = port + OFF_PICB; do { - civ = inb(port+OFF_CIV) & 31; + civ = GET_CIV(port); offset = inw(port_picb); /* Must have a delay here! */ if(offset == 0) @@ -782,7 +745,7 @@ * that we won't have to worry about the chip still being * out of sync with reality ;-) */ - } while (civ != (inb(port+OFF_CIV) & 31) || offset != inw(port_picb)); + } while (civ != GET_CIV(port) || offset != inw(port_picb)); return (((civ + 1) * dmabuf->fragsize - (bytes * offset)) % dmabuf->dmasize); @@ -992,6 +955,7 @@ dmabuf->numfrag = SG_LEN; dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag; dmabuf->fragsamples = dmabuf->fragsize >> 1; + dmabuf->fragshift = ffs(dmabuf->fragsize) - 1; dmabuf->userfragsize = dmabuf->ossfragsize; dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize; @@ -999,16 +963,12 @@ if(dmabuf->ossmaxfrags == 4) { fragint = 8; - dmabuf->fragshift = 2; } else if (dmabuf->ossmaxfrags == 8) { fragint = 4; - dmabuf->fragshift = 3; } else if (dmabuf->ossmaxfrags == 16) { fragint = 2; - dmabuf->fragshift = 4; } else { fragint = 1; - dmabuf->fragshift = 5; } /* * Now set up the ring @@ -1072,41 +1032,41 @@ { struct dmabuf *dmabuf = &state->dmabuf; int x, port; - + int trigger; + int count, fragsize; + void (*start)(struct i810_state *); + + count = dmabuf->count; port = state->card->iobase; - if(rec) + if (rec) { port += dmabuf->read_channel->port; - else + trigger = PCM_ENABLE_INPUT; + start = __start_adc; + count = dmabuf->dmasize - count; + } else { port += dmabuf->write_channel->port; + trigger = PCM_ENABLE_OUTPUT; + start = __start_dac; + } + + /* Do not process partial fragments. */ + fragsize = dmabuf->fragsize; + if (count < fragsize) + return; - /* if we are currently stopped, then our CIV is actually set to our - * *last* sg segment and we are ready to wrap to the next. However, - * if we set our LVI to the last sg segment, then it won't wrap to - * the next sg segment, it won't even get a start. So, instead, when - * we are stopped, we set both the LVI value and also we increment - * the CIV value to the next sg segment to be played so that when - * we call start_{dac,adc}, things will operate properly - */ if (!dmabuf->enable && dmabuf->ready) { - if(rec && dmabuf->count < dmabuf->dmasize && - (dmabuf->trigger & PCM_ENABLE_INPUT)) - { - CIV_TO_LVI(port, 1); - __start_adc(state); - while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ; - } else if (!rec && dmabuf->count && - (dmabuf->trigger & PCM_ENABLE_OUTPUT)) - { - CIV_TO_LVI(port, 1); - __start_dac(state); - while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ; - } + if (!(dmabuf->trigger & trigger)) + return; + + start(state); + while (!(inb(port + OFF_CR) & ((1<<4) | (1<<2)))) + ; } - /* swptr - 1 is the tail of our transfer */ - x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize; - x /= dmabuf->fragsize; - outb(x, port+OFF_LVI); + /* MASKP2(swptr, fragsize) - 1 is the tail of our transfer */ + x = MODULOP2(MASKP2(dmabuf->swptr, fragsize) - 1, dmabuf->dmasize); + x >>= dmabuf->fragshift; + outb(x, port + OFF_LVI); } static void i810_update_lvi(struct i810_state *state, int rec) @@ -1126,13 +1086,17 @@ { struct dmabuf *dmabuf = &state->dmabuf; unsigned hwptr; + unsigned fragmask, dmamask; int diff; - /* error handling and process wake up for DAC */ + fragmask = MASKP2(~0, dmabuf->fragsize); + dmamask = MODULOP2(~0, dmabuf->dmasize); + + /* error handling and process wake up for ADC */ if (dmabuf->enable == ADC_RUNNING) { /* update hardware pointer */ - hwptr = i810_get_dma_addr(state, 1); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + hwptr = i810_get_dma_addr(state, 1) & fragmask; + diff = (hwptr - dmabuf->hwptr) & dmamask; #if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); #endif @@ -1144,20 +1108,20 @@ /* this is normal for the end of a read */ /* only give an error if we went past the */ /* last valid sg entry */ - if((inb(state->card->iobase + PI_CIV) & 31) != - (inb(state->card->iobase + PI_LVI) & 31)) { + if (GET_CIV(state->card->iobase + PI_BASE) != + GET_LVI(state->card->iobase + PI_BASE)) { printk(KERN_WARNING "i810_audio: DMA overrun on read\n"); dmabuf->error++; } } - if (dmabuf->count > dmabuf->userfragsize) + if (diff) wake_up(&dmabuf->wait); } /* error handling and process wake up for DAC */ if (dmabuf->enable == DAC_RUNNING) { /* update hardware pointer */ - hwptr = i810_get_dma_addr(state, 0); - diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + hwptr = i810_get_dma_addr(state, 0) & fragmask; + diff = (hwptr - dmabuf->hwptr) & dmamask; #if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP) printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); #endif @@ -1169,18 +1133,18 @@ /* this is normal for the end of a write */ /* only give an error if we went past the */ /* last valid sg entry */ - if((inb(state->card->iobase + PO_CIV) & 31) != - (inb(state->card->iobase + PO_LVI) & 31)) { + if (GET_CIV(state->card->iobase + PO_BASE) != + GET_LVI(state->card->iobase + PO_BASE)) { printk(KERN_WARNING "i810_audio: DMA overrun on write\n"); printk("i810_audio: CIV %d, LVI %d, hwptr %x, " "count %d\n", - inb(state->card->iobase + PO_CIV) & 31, - inb(state->card->iobase + PO_LVI) & 31, + GET_CIV(state->card->iobase + PO_BASE), + GET_LVI(state->card->iobase + PO_BASE), dmabuf->hwptr, dmabuf->count); dmabuf->error++; } } - if (dmabuf->count < (dmabuf->dmasize-dmabuf->userfragsize)) + if (diff) wake_up(&dmabuf->wait); } } @@ -1197,7 +1161,6 @@ dmabuf->swptr = dmabuf->hwptr; } free = dmabuf->dmasize - dmabuf->count; - free -= (dmabuf->hwptr % dmabuf->fragsize); if(free < 0) return(0); return(free); @@ -1215,12 +1178,27 @@ dmabuf->swptr = dmabuf->hwptr; } avail = dmabuf->count; - avail -= (dmabuf->hwptr % dmabuf->fragsize); if(avail < 0) return(0); return(avail); } +static inline void fill_partial_frag(struct dmabuf *dmabuf) +{ + unsigned fragsize; + unsigned swptr, len; + + fragsize = dmabuf->fragsize; + swptr = dmabuf->swptr; + len = fragsize - MODULOP2(dmabuf->swptr, fragsize); + if (len == fragsize) + return; + + memset(dmabuf->rawbuf + swptr, '\0', len); + dmabuf->swptr = MODULOP2(swptr + len, dmabuf->dmasize); + dmabuf->count += len; +} + static int drain_dac(struct i810_state *state, int signals_allowed) { DECLARE_WAITQUEUE(wait, current); @@ -1235,30 +1213,28 @@ stop_dac(state); return 0; } + + spin_lock_irqsave(&state->card->lock, flags); + + fill_partial_frag(dmabuf); + + /* + * This will make sure that our LVI is correct, that our + * pointer is updated, and that the DAC is running. We + * have to force the setting of dmabuf->trigger to avoid + * any possible deadlocks. + */ + dmabuf->trigger = PCM_ENABLE_OUTPUT; + __i810_update_lvi(state, 0); + + spin_unlock_irqrestore(&state->card->lock, flags); + add_wait_queue(&dmabuf->wait, &wait); for (;;) { spin_lock_irqsave(&state->card->lock, flags); i810_update_ptr(state); count = dmabuf->count; - spin_unlock_irqrestore(&state->card->lock, flags); - - if (count <= 0) - break; - - /* - * This will make sure that our LVI is correct, that our - * pointer is updated, and that the DAC is running. We - * have to force the setting of dmabuf->trigger to avoid - * any possible deadlocks. - */ - if(!dmabuf->enable) { - dmabuf->trigger = PCM_ENABLE_OUTPUT; - i810_update_lvi(state,0); - } - if (signal_pending(current) && signals_allowed) { - break; - } /* It seems that we have to set the current state to * TASK_INTERRUPTIBLE every time to make the process @@ -1269,7 +1245,17 @@ * instead of actually sleeping and waiting for an * interrupt to wake us up! */ - set_current_state(TASK_INTERRUPTIBLE); + __set_current_state(signals_allowed ? + TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current) && signals_allowed) { + break; + } + /* * set the timeout to significantly longer than it *should* * take for the DAC to drain the DMA buffer @@ -1350,11 +1336,10 @@ if(status & DMA_INT_DCH) printk("DCH -"); #endif - if(dmabuf->enable & DAC_RUNNING) - count = dmabuf->count; - else - count = dmabuf->dmasize - dmabuf->count; - if(count > 0) { + count = dmabuf->count; + if(dmabuf->enable & ADC_RUNNING) + count = dmabuf->dmasize - count; + if (count >= (int)dmabuf->fragsize) { outb(inb(port+OFF_CR) | 1, port+OFF_CR); #ifdef DEBUG_INTERRUPTS printk(" CONTINUE "); @@ -1417,6 +1402,7 @@ unsigned long flags; unsigned int swptr; int cnt; + int pending; DECLARE_WAITQUEUE(waita, current); #ifdef DEBUG2 @@ -1442,6 +1428,8 @@ return -EFAULT; ret = 0; + pending = 0; + add_wait_queue(&dmabuf->wait, &waita); while (count > 0) { set_current_state(TASK_INTERRUPTIBLE); @@ -1455,8 +1443,8 @@ } continue; } - swptr = dmabuf->swptr; cnt = i810_get_available_read_data(state); + swptr = dmabuf->swptr; // this is to make the copy_to_user simpler below if(cnt > (dmabuf->dmasize - swptr)) cnt = dmabuf->dmasize - swptr; @@ -1464,15 +1452,6 @@ if (cnt > count) cnt = count; - /* Lop off the last two bits to force the code to always - * write in full samples. This keeps software that sets - * O_NONBLOCK but doesn't check the return value of the - * write call from getting things out of state where they - * think a full 4 byte sample was written when really only - * a portion was, resulting in odd sound and stereo - * hysteresis. - */ - cnt &= ~0x3; if (cnt <= 0) { unsigned long tmo; /* @@ -1526,7 +1505,7 @@ goto done; } - swptr = (swptr + cnt) % dmabuf->dmasize; + swptr = MODULOP2(swptr + cnt, dmabuf->dmasize); spin_lock_irqsave(&card->lock, flags); @@ -1535,7 +1514,7 @@ continue; } dmabuf->swptr = swptr; - dmabuf->count -= cnt; + pending = dmabuf->count -= cnt; spin_unlock_irqrestore(&card->lock, flags); count -= cnt; @@ -1543,7 +1522,9 @@ ret += cnt; } done: - i810_update_lvi(state,1); + pending = dmabuf->dmasize - pending; + if (dmabuf->enable || pending >= dmabuf->userfragsize) + i810_update_lvi(state, 1); set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &waita); @@ -1560,7 +1541,8 @@ ssize_t ret; unsigned long flags; unsigned int swptr = 0; - int cnt, x; + int pending; + int cnt; DECLARE_WAITQUEUE(waita, current); #ifdef DEBUG2 @@ -1585,6 +1567,8 @@ return -EFAULT; ret = 0; + pending = 0; + add_wait_queue(&dmabuf->wait, &waita); while (count > 0) { set_current_state(TASK_INTERRUPTIBLE); @@ -1599,8 +1583,8 @@ continue; } - swptr = dmabuf->swptr; cnt = i810_get_free_write_space(state); + swptr = dmabuf->swptr; /* Bound the maximum size to how much we can copy to the * dma buffer before we hit the end. If we have more to * copy then it will get done in a second pass of this @@ -1615,15 +1599,6 @@ #endif if (cnt > count) cnt = count; - /* Lop off the last two bits to force the code to always - * write in full samples. This keeps software that sets - * O_NONBLOCK but doesn't check the return value of the - * write call from getting things out of state where they - * think a full 4 byte sample was written when really only - * a portion was, resulting in odd sound and stereo - * hysteresis. - */ - cnt &= ~0x3; if (cnt <= 0) { unsigned long tmo; // There is data waiting to be played @@ -1668,7 +1643,7 @@ goto ret; } - swptr = (swptr + cnt) % dmabuf->dmasize; + swptr = MODULOP2(swptr + cnt, dmabuf->dmasize); spin_lock_irqsave(&state->card->lock, flags); if (PM_SUSPENDED(card)) { @@ -1677,19 +1652,16 @@ } dmabuf->swptr = swptr; - dmabuf->count += cnt; + pending = dmabuf->count += cnt; count -= cnt; buffer += cnt; ret += cnt; spin_unlock_irqrestore(&state->card->lock, flags); } - if (swptr % dmabuf->fragsize) { - x = dmabuf->fragsize - (swptr % dmabuf->fragsize); - memset(dmabuf->rawbuf + swptr, '\0', x); - } ret: - i810_update_lvi(state,0); + if (dmabuf->enable || pending >= dmabuf->userfragsize) + i810_update_lvi(state, 0); set_current_state(TASK_RUNNING); remove_wait_queue(&dmabuf->wait, &waita); @@ -1938,8 +1910,8 @@ } /* ICH and ICH0 only support 2 channels */ - if ( state->card->pci_id == PCI_DEVICE_ID_INTEL_82801 - || state->card->pci_id == PCI_DEVICE_ID_INTEL_82901) + if ( state->card->pci_id == PCI_DEVICE_ID_INTEL_82801AA_5 + || state->card->pci_id == PCI_DEVICE_ID_INTEL_82801AB_5) return put_user(2, (int *)arg); /* Multi-channel support was added with ICH2. Bits in */ @@ -2179,6 +2151,13 @@ #if defined(DEBUG) || defined(DEBUG_MMAP) printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val); #endif + /* silently ignore invalid PCM_ENABLE_xxx bits, + * like the other drivers do + */ + if (!(file->f_mode & FMODE_READ )) + val &= ~PCM_ENABLE_INPUT; + if (!(file->f_mode & FMODE_WRITE )) + val &= ~PCM_ENABLE_OUTPUT; if((file->f_mode & FMODE_READ) && !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) { stop_adc(state); } @@ -2186,7 +2165,7 @@ stop_dac(state); } dmabuf->trigger = val; - if((file->f_mode & FMODE_WRITE) && (val & PCM_ENABLE_OUTPUT) && !(dmabuf->enable & DAC_RUNNING)) { + if((val & PCM_ENABLE_OUTPUT) && !(dmabuf->enable & DAC_RUNNING)) { if (!dmabuf->write_channel) { dmabuf->ready = 0; dmabuf->write_channel = state->card->alloc_pcm_channel(state->card); @@ -2202,12 +2181,12 @@ dmabuf->swptr = dmabuf->hwptr; dmabuf->count = i810_get_free_write_space(state); dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize; - __i810_update_lvi(state, 0); spin_unlock_irqrestore(&state->card->lock, flags); - } else - start_dac(state); + } + i810_update_lvi(state, 0); + start_dac(state); } - if((file->f_mode & FMODE_READ) && (val & PCM_ENABLE_INPUT) && !(dmabuf->enable & ADC_RUNNING)) { + if((val & PCM_ENABLE_INPUT) && !(dmabuf->enable & ADC_RUNNING)) { if (!dmabuf->read_channel) { dmabuf->ready = 0; dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card); @@ -2471,7 +2450,7 @@ if(file->f_mode & FMODE_READ) { if((dmabuf->read_channel = card->alloc_rec_pcm_channel(card)) == NULL) { kfree (card->states[i]); - card->states[i] = NULL;; + card->states[i] = NULL; return -EBUSY; } dmabuf->trigger |= PCM_ENABLE_INPUT; @@ -2483,7 +2462,7 @@ if(file->f_mode & FMODE_READ) card->free_pcm_channel(card,dmabuf->read_channel->num); kfree (card->states[i]); - card->states[i] = NULL;; + card->states[i] = NULL; return -EBUSY; } /* Initialize to 8kHz? What if we don't support 8kHz? */ @@ -2747,6 +2726,26 @@ return i; } +static int is_new_ich(u16 pci_id) +{ + switch (pci_id) { + case PCI_DEVICE_ID_INTEL_82801DB_5: + case PCI_DEVICE_ID_INTEL_82801EB_5: + case PCI_DEVICE_ID_INTEL_ESB_5: + case PCI_DEVICE_ID_INTEL_ICH6_18: + return 1; + default: + break; + } + + return 0; +} + +static inline int ich_use_mmio(struct i810_card *card) +{ + return is_new_ich(card->pci_id) && card->use_mmio; +} + /** * i810_ac97_power_up_bus - bring up AC97 link * @card : ICH audio device to power up @@ -2796,9 +2795,7 @@ */ /* see i810_ac97_init for the next 7 lines (jsaw) */ inw(card->ac97base); - if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH5 || - card->pci_id == PCI_DEVICE_ID_INTEL_ESB_5 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH6_3) - && (card->use_mmio)) { + if (ich_use_mmio(card)) { primary_codec_id = (int) readl(card->iobase_mmio + SDM) & 0x3; printk(KERN_INFO "i810_audio: Primary codec has ID %d\n", primary_codec_id); @@ -2819,7 +2816,7 @@ return 1; } -static int __init i810_ac97_init(struct i810_card *card) +static int __devinit i810_ac97_init(struct i810_card *card) { int num_ac97 = 0; int ac97_id; @@ -2867,9 +2864,7 @@ possible IO channels. Bit 0:1 of SDM then holds the last codec ID spoken to. */ - if ((card->pci_id == PCI_DEVICE_ID_INTEL_ICH4 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH5 || - card->pci_id == PCI_DEVICE_ID_INTEL_ESB_5 || card->pci_id == PCI_DEVICE_ID_INTEL_ICH6_3) - && (card->use_mmio)) { + if (ich_use_mmio(card)) { ac97_id = (int) readl(card->iobase_mmio + SDM) & 0x3; printk(KERN_INFO "i810_audio: Connection %d with codec id %d\n", num_ac97, ac97_id); @@ -3024,7 +3019,7 @@ return num_ac97; } -static void __init i810_configure_clocking (void) +static void __devinit i810_configure_clocking (void) { struct i810_card *card; struct i810_state *state; @@ -3065,15 +3060,14 @@ goto config_out; } dmabuf->count = dmabuf->dmasize; - CIV_TO_LVI(card->iobase+dmabuf->write_channel->port, 31); - save_flags(flags); - cli(); + CIV_TO_LVI(card->iobase+dmabuf->write_channel->port, -1); + local_irq_save(flags); start_dac(state); offset = i810_get_dma_addr(state, 0); mdelay(50); new_offset = i810_get_dma_addr(state, 0); stop_dac(state); - restore_flags(flags); + local_irq_restore(flags); i = new_offset - offset; #ifdef DEBUG_INTERRUPTS printk("i810_audio: %d bytes in 50 milliseconds\n", i); @@ -3097,7 +3091,7 @@ /* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ -static int __init i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +static int __devinit i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { struct i810_card *card; diff -urN linux-2.4.26/drivers/sound/kahlua.c linux-2.4.27/drivers/sound/kahlua.c --- linux-2.4.26/drivers/sound/kahlua.c 2003-06-13 07:51:36.000000000 -0700 +++ linux-2.4.27/drivers/sound/kahlua.c 2004-08-07 16:26:05.557383882 -0700 @@ -37,6 +37,7 @@ #include #include #include +#include #include "sound_config.h" diff -urN linux-2.4.26/drivers/sound/mpu401.c linux-2.4.27/drivers/sound/mpu401.c --- linux-2.4.26/drivers/sound/mpu401.c 2003-06-13 07:51:36.000000000 -0700 +++ linux-2.4.27/drivers/sound/mpu401.c 2004-08-07 16:26:05.558383923 -0700 @@ -1513,14 +1513,16 @@ static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg) { int midi_dev = sound_timer_devs[dev]->devlink; + int *p = (int *)arg; switch (command) { case SNDCTL_TMR_SOURCE: { int parm; - - parm = *(int *) arg; + + if (get_user(parm, p)) + return -EFAULT; parm &= timer_caps; if (parm != 0) @@ -1532,7 +1534,9 @@ else if (timer_mode & TMR_MODE_SMPTE) mpu_cmd(midi_dev, 0x3d, 0); /* Use SMPTE sync */ } - return (*(int *) arg = timer_mode); + if (put_user(timer_mode, p)) + return -EFAULT; + return timer_mode; } break; @@ -1557,10 +1561,13 @@ { int val; - val = *(int *) arg; + if (get_user(val, p)) + return -EFAULT; if (val) set_timebase(midi_dev, val); - return (*(int *) arg = curr_timebase); + if (put_user(curr_timebase, p)) + return -EFAULT; + return curr_timebase; } break; @@ -1569,7 +1576,8 @@ int val; int ret; - val = *(int *) arg; + if (get_user(val, p)) + return -EFAULT; if (val) { @@ -1584,7 +1592,9 @@ } curr_tempo = val; } - return (*(int *) arg = curr_tempo); + if (put_user(curr_tempo, p)) + return -EFAULT; + return curr_tempo; } break; @@ -1592,18 +1602,25 @@ { int val; - val = *(int *) arg; + if (get_user(val, p)) + return -EFAULT; if (val != 0) /* Can't change */ return -EINVAL; - return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60); + val = (curr_tempo * curr_timebase + 30) / 60; + if (put_user(val, p)) + return -EFAULT; + return val; } break; case SNDCTL_SEQ_GETTIME: - return (*(int *) arg = curr_ticks); + if (put_user(curr_ticks, p)) + return -EFAULT; + return curr_ticks; case SNDCTL_TMR_METRONOME: - metronome_mode = *(int *) arg; + if (get_user(metronome_mode, p)) + return -EFAULT; setup_metronome(midi_dev); return 0; diff -urN linux-2.4.26/drivers/sound/msnd.c linux-2.4.27/drivers/sound/msnd.c --- linux-2.4.26/drivers/sound/msnd.c 2001-09-30 12:26:08.000000000 -0700 +++ linux-2.4.27/drivers/sound/msnd.c 2004-08-07 16:26:05.559383964 -0700 @@ -155,13 +155,10 @@ f->len = f->tail = f->head = 0; } -int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user) +int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len) { int count = 0; - if (f->len == f->n) - return 0; - while ((count < len) && (f->len != f->n)) { int nwritten; @@ -177,11 +174,7 @@ nwritten = len - count; } - if (user) { - if (copy_from_user(f->data + f->tail, buf, nwritten)) - return -EFAULT; - } else - isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten); + isa_memcpy_fromio(f->data + f->tail, (unsigned long) buf, nwritten); count += nwritten; buf += nwritten; @@ -193,13 +186,10 @@ return count; } -int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user) +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len) { int count = 0; - if (f->len == 0) - return f->len; - while ((count < len) && (f->len > 0)) { int nread; @@ -215,11 +205,7 @@ nread = len - count; } - if (user) { - if (copy_to_user(buf, f->data + f->head, nread)) - return -EFAULT; - } else - isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread); + isa_memcpy_toio((unsigned long) buf, f->data + f->head, nread); count += nread; buf += nread; diff -urN linux-2.4.26/drivers/sound/msnd.h linux-2.4.27/drivers/sound/msnd.h --- linux-2.4.26/drivers/sound/msnd.h 2000-07-12 21:58:43.000000000 -0700 +++ linux-2.4.27/drivers/sound/msnd.h 2004-08-07 16:26:05.559383964 -0700 @@ -266,8 +266,8 @@ void msnd_fifo_free(msnd_fifo *f); int msnd_fifo_alloc(msnd_fifo *f, size_t n); void msnd_fifo_make_empty(msnd_fifo *f); -int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len, int user); -int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len, int user); +int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len); +int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len); int msnd_wait_TXDE(multisound_dev_t *dev); int msnd_wait_HC0(multisound_dev_t *dev); diff -urN linux-2.4.26/drivers/sound/msnd_pinnacle.c linux-2.4.27/drivers/sound/msnd_pinnacle.c --- linux-2.4.26/drivers/sound/msnd_pinnacle.c 2002-08-02 17:39:44.000000000 -0700 +++ linux-2.4.27/drivers/sound/msnd_pinnacle.c 2004-08-07 16:26:05.561384046 -0700 @@ -804,7 +804,7 @@ static __inline__ int pack_DARQ_to_DARF(register int bank) { - register int size, n, timeout = 3; + register int size, timeout = 3; register WORD wTmp; LPDAQD DAQD; @@ -825,13 +825,10 @@ /* Read data from the head (unprotected bank 1 access okay since this is only called inside an interrupt) */ outb(HPBLKSEL_1, dev.io + HP_BLKS); - if ((n = msnd_fifo_write( + msnd_fifo_write( &dev.DARF, (char *)(dev.base + bank * DAR_BUFF_SIZE), - size, 0)) <= 0) { - outb(HPBLKSEL_0, dev.io + HP_BLKS); - return n; - } + size); outb(HPBLKSEL_0, dev.io + HP_BLKS); return 1; @@ -853,21 +850,16 @@ if (protect) { /* Critical section: protect fifo in non-interrupt */ spin_lock_irqsave(&dev.lock, flags); - if ((n = msnd_fifo_read( + n = msnd_fifo_read( &dev.DAPF, (char *)(dev.base + bank_num * DAP_BUFF_SIZE), - DAP_BUFF_SIZE, 0)) < 0) { - spin_unlock_irqrestore(&dev.lock, flags); - return n; - } + DAP_BUFF_SIZE); spin_unlock_irqrestore(&dev.lock, flags); } else { - if ((n = msnd_fifo_read( + n = msnd_fifo_read( &dev.DAPF, (char *)(dev.base + bank_num * DAP_BUFF_SIZE), - DAP_BUFF_SIZE, 0)) < 0) { - return n; - } + DAP_BUFF_SIZE); } if (!n) break; @@ -894,30 +886,43 @@ static int dsp_read(char *buf, size_t len) { int count = len; + char *page = (char *)__get_free_page(PAGE_SIZE); + + if (!page) + return -ENOMEM; while (count > 0) { - int n; + int n, k; unsigned long flags; + k = PAGE_SIZE; + if (k > count) + k = count; + /* Critical section: protect fifo in non-interrupt */ spin_lock_irqsave(&dev.lock, flags); - if ((n = msnd_fifo_read(&dev.DARF, buf, count, 1)) < 0) { - printk(KERN_WARNING LOGNAME ": FIFO read error\n"); - spin_unlock_irqrestore(&dev.lock, flags); - return n; - } + n = msnd_fifo_read(&dev.DARF, page, k); spin_unlock_irqrestore(&dev.lock, flags); + if (copy_to_user(buf, page, n)) { + free_page((unsigned long)page); + return -EFAULT; + } buf += n; count -= n; + if (n == k && count) + continue; + if (!test_bit(F_READING, &dev.flags) && dev.mode & FMODE_READ) { dev.last_recbank = -1; if (chk_send_dsp_cmd(&dev, HDEX_RECORD_START) == 0) set_bit(F_READING, &dev.flags); } - if (dev.rec_ndelay) + if (dev.rec_ndelay) { + free_page((unsigned long)page); return count == len ? -EAGAIN : len - count; + } if (count > 0) { set_bit(F_READBLOCK, &dev.flags); @@ -926,41 +931,57 @@ get_rec_delay_jiffies(DAR_BUFF_SIZE))) clear_bit(F_READING, &dev.flags); clear_bit(F_READBLOCK, &dev.flags); - if (signal_pending(current)) + if (signal_pending(current)) { + free_page((unsigned long)page); return -EINTR; + } } } - + free_page((unsigned long)page); return len - count; } static int dsp_write(const char *buf, size_t len) { int count = len; + char *page = (char *)__get_free_page(GFP_KERNEL); + + if (!page) + return -ENOMEM; while (count > 0) { - int n; + int n, k; unsigned long flags; + k = PAGE_SIZE; + if (k > count) + k = count; + + if (copy_from_user(page, buf, k)) { + free_page((unsigned long)page); + return -EFAULT; + } + /* Critical section: protect fifo in non-interrupt */ spin_lock_irqsave(&dev.lock, flags); - if ((n = msnd_fifo_write(&dev.DAPF, buf, count, 1)) < 0) { - printk(KERN_WARNING LOGNAME ": FIFO write error\n"); - spin_unlock_irqrestore(&dev.lock, flags); - return n; - } + n = msnd_fifo_write(&dev.DAPF, page, k); spin_unlock_irqrestore(&dev.lock, flags); buf += n; count -= n; + if (count && n == k) + continue; + if (!test_bit(F_WRITING, &dev.flags) && (dev.mode & FMODE_WRITE)) { dev.last_playbank = -1; if (pack_DAPF_to_DAPQ(1) > 0) set_bit(F_WRITING, &dev.flags); } - if (dev.play_ndelay) + if (dev.play_ndelay) { + free_page((unsigned long)page); return count == len ? -EAGAIN : len - count; + } if (count > 0) { set_bit(F_WRITEBLOCK, &dev.flags); @@ -968,11 +989,14 @@ &dev.writeblock, get_play_delay_jiffies(DAP_BUFF_SIZE)); clear_bit(F_WRITEBLOCK, &dev.flags); - if (signal_pending(current)) + if (signal_pending(current)) { + free_page((unsigned long)page); return -EINTR; + } } } + free_page((unsigned long)page); return len - count; } diff -urN linux-2.4.26/drivers/sound/pss.c linux-2.4.27/drivers/sound/pss.c --- linux-2.4.26/drivers/sound/pss.c 2002-11-28 15:53:14.000000000 -0800 +++ linux-2.4.27/drivers/sound/pss.c 2004-08-07 16:26:05.562384087 -0700 @@ -450,20 +450,36 @@ } } -static void arg_to_volume_mono(unsigned int volume, int *aleft) +static int set_volume_mono(caddr_t p, int *aleft) { int left; + unsigned volume; + if (get_user(volume, (unsigned *)p)) + return -EFAULT; - left = volume & 0x00ff; + left = volume & 0xff; if (left > 100) left = 100; *aleft = left; + return 0; } -static void arg_to_volume_stereo(unsigned int volume, int *aleft, int *aright) +static int set_volume_stereo(caddr_t p, int *aleft, int *aright) { - arg_to_volume_mono(volume, aleft); - arg_to_volume_mono(volume >> 8, aright); + int left, right; + unsigned volume; + if (get_user(volume, (unsigned *)p)) + return -EFAULT; + + left = volume & 0xff; + if (left > 100) + left = 100; + right = (volume >> 8) & 0xff; + if (right > 100) + right = 100; + *aleft = left; + *aright = right; + return 0; } static int ret_vol_mono(int left) @@ -510,33 +526,38 @@ return call_ad_mixer(devc, cmd, arg); else { - if (*(int *)arg != 0) + int v; + if (get_user(v, (int *)arg)) + return -EFAULT; + if (v != 0) return -EINVAL; return 0; } case SOUND_MIXER_VOLUME: - arg_to_volume_stereo(*(unsigned int *)arg, &devc->mixer.volume_l, - &devc->mixer.volume_r); + if (set_volume_stereo(arg, + &devc->mixer.volume_l, + &devc->mixer.volume_r)) + return -EFAULT; set_master_volume(devc, devc->mixer.volume_l, devc->mixer.volume_r); return ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r); case SOUND_MIXER_BASS: - arg_to_volume_mono(*(unsigned int *)arg, - &devc->mixer.bass); + if (set_volume_mono(arg, &devc->mixer.bass)) + return -EFAULT; set_bass(devc, devc->mixer.bass); return ret_vol_mono(devc->mixer.bass); case SOUND_MIXER_TREBLE: - arg_to_volume_mono(*(unsigned int *)arg, - &devc->mixer.treble); + if (set_volume_mono(arg, &devc->mixer.treble)) + return -EFAULT; set_treble(devc, devc->mixer.treble); return ret_vol_mono(devc->mixer.treble); case SOUND_MIXER_SYNTH: - arg_to_volume_mono(*(unsigned int *)arg, - &devc->mixer.synth); + if (set_volume_mono(arg, &devc->mixer.synth)) + return -EFAULT; set_synth_volume(devc, devc->mixer.synth); return ret_vol_mono(devc->mixer.synth); @@ -546,54 +567,67 @@ } else { + int val, and_mask = 0, or_mask = 0; /* * Return parameters */ switch (cmdf) { - case SOUND_MIXER_DEVMASK: if (call_ad_mixer(devc, cmd, arg) == -EINVAL) - *(int *)arg = 0; /* no mixer devices */ - return (*(int *)arg |= SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH); + break; + and_mask = ~0; + or_mask = SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_SYNTH; + break; case SOUND_MIXER_STEREODEVS: if (call_ad_mixer(devc, cmd, arg) == -EINVAL) - *(int *)arg = 0; /* no stereo devices */ - return (*(int *)arg |= SOUND_MASK_VOLUME); + break; + and_mask = ~0; + or_mask = SOUND_MASK_VOLUME; + break; case SOUND_MIXER_RECMASK: if (devc->ad_mixer_dev != NO_WSS_MIXER) return call_ad_mixer(devc, cmd, arg); - else - return (*(int *)arg = 0); /* no record devices */ + break; case SOUND_MIXER_CAPS: if (devc->ad_mixer_dev != NO_WSS_MIXER) return call_ad_mixer(devc, cmd, arg); - else - return (*(int *)arg = SOUND_CAP_EXCL_INPUT); + or_mask = SOUND_CAP_EXCL_INPUT; + break; case SOUND_MIXER_RECSRC: if (devc->ad_mixer_dev != NO_WSS_MIXER) return call_ad_mixer(devc, cmd, arg); - else - return (*(int *)arg = 0); /* no record source */ + break; case SOUND_MIXER_VOLUME: - return (*(int *)arg = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r)); + or_mask = ret_vol_stereo(devc->mixer.volume_l, devc->mixer.volume_r); + break; case SOUND_MIXER_BASS: - return (*(int *)arg = ret_vol_mono(devc->mixer.bass)); + or_mask = ret_vol_mono(devc->mixer.bass); + break; case SOUND_MIXER_TREBLE: - return (*(int *)arg = ret_vol_mono(devc->mixer.treble)); + or_mask = ret_vol_mono(devc->mixer.treble); + break; case SOUND_MIXER_SYNTH: - return (*(int *)arg = ret_vol_mono(devc->mixer.synth)); + or_mask = ret_vol_mono(devc->mixer.synth); + break; default: return -EINVAL; } + if (get_user(val, (int *)arg)) + return -EFAULT; + val &= and_mask; + val |= or_mask; + if (put_user(val, (int *)arg)) + return -EFAULT; + return val; } } diff -urN linux-2.4.26/drivers/usb/audio.c linux-2.4.27/drivers/usb/audio.c --- linux-2.4.26/drivers/usb/audio.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/audio.c 2004-08-07 16:26:05.622386553 -0700 @@ -2141,6 +2141,8 @@ if (cmd == SOUND_MIXER_INFO) { mixer_info info; + + memset(&info, 0, sizeof(info)); strncpy(info.id, "USB_AUDIO", sizeof(info.id)); strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); info.modify_counter = ms->modcnt; @@ -2150,6 +2152,8 @@ } if (cmd == SOUND_OLD_MIXER_INFO) { _old_mixer_info info; + + memset(&info, 0, sizeof(info)); strncpy(info.id, "USB_AUDIO", sizeof(info.id)); strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); if (copy_to_user((void *)arg, &info, sizeof(info))) diff -urN linux-2.4.26/drivers/usb/auermain.c linux-2.4.27/drivers/usb/auermain.c --- linux-2.4.26/drivers/usb/auermain.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/usb/auermain.c 2004-08-07 16:26:05.623386594 -0700 @@ -55,7 +55,7 @@ /*-------------------------------------------------------------------*/ /* Version Information */ -#define DRIVER_VERSION "1.2.6" +#define DRIVER_VERSION "1.2.7" #define DRIVER_AUTHOR "Wolfgang Mües " #define DRIVER_DESC "Auerswald PBX/System Telephone usb driver" @@ -110,8 +110,8 @@ case 0: case -ETIMEDOUT: case -EOVERFLOW: + case -EFBIG: case -EAGAIN: - case -EPIPE: case -EPROTO: case -EILSEQ: case -ENOSR: diff -urN linux-2.4.26/drivers/usb/brlvger.c linux-2.4.27/drivers/usb/brlvger.c --- linux-2.4.26/drivers/usb/brlvger.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/brlvger.c 2004-08-07 16:26:05.681388977 -0700 @@ -596,6 +596,9 @@ off = *pos; + if (off < 0) + return -EINVAL; + if(off > priv->plength) return -ESPIPE;; @@ -743,6 +746,7 @@ case BRLVGER_GET_INFO: { struct brlvger_info vi; + memset(&vi, 0, sizeof(vi)); strncpy(vi.driver_version, DRIVER_VERSION, sizeof(vi.driver_version)); vi.driver_version[sizeof(vi.driver_version)-1] = 0; diff -urN linux-2.4.26/drivers/usb/devio.c linux-2.4.27/drivers/usb/devio.c --- linux-2.4.26/drivers/usb/devio.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/devio.c 2004-08-07 16:26:05.682389018 -0700 @@ -80,7 +80,7 @@ struct dev_state *ps = (struct dev_state *)file->private_data; ssize_t ret = 0; unsigned len; - loff_t pos; + loff_t pos, last; int i; pos = *ppos; @@ -102,37 +102,38 @@ goto err; } - *ppos += len; + pos += len; buf += len; nbytes -= len; ret += len; } - pos = sizeof(struct usb_device_descriptor); + last = sizeof(struct usb_device_descriptor); for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) { struct usb_config_descriptor *config = (struct usb_config_descriptor *)ps->dev->rawdescriptors[i]; unsigned int length = le16_to_cpu(config->wTotalLength); - if (*ppos < pos + length) { - len = length - (*ppos - pos); + if (pos < last + length) { + len = length - (pos - last); if (len > nbytes) len = nbytes; if (copy_to_user(buf, - ps->dev->rawdescriptors[i] + (*ppos - pos), len)) { + ps->dev->rawdescriptors[i] + (pos - last), len)) { ret = -EFAULT; goto err; } - *ppos += len; + pos += len; buf += len; nbytes -= len; ret += len; } - pos += length; + last += length; } + *ppos = pos; err: up_read(&ps->devsem); diff -urN linux-2.4.26/drivers/usb/drivers.c linux-2.4.27/drivers/usb/drivers.c --- linux-2.4.26/drivers/usb/drivers.c 2000-04-26 15:22:55.000000000 -0700 +++ linux-2.4.27/drivers/usb/drivers.c 2004-08-07 16:26:05.683389059 -0700 @@ -52,9 +52,10 @@ struct list_head *tmp = usb_driver_list.next; char *page, *start, *end; ssize_t ret = 0; - unsigned int pos, len; + loff_t n = *ppos; + unsigned int pos = n, len; - if (*ppos < 0) + if (pos != n) return -EINVAL; if (nbytes <= 0) return 0; @@ -64,7 +65,6 @@ return -ENOMEM; start = page; end = page + (PAGE_SIZE - 100); - pos = *ppos; for (; tmp != &usb_driver_list; tmp = tmp->next) { struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); int minor = driver->fops ? driver->minor : -1; @@ -88,7 +88,7 @@ if (copy_to_user(buf, page + pos, len)) ret = -EFAULT; else - *ppos += len; + *ppos = pos + len; } free_page((unsigned long)page); return ret; diff -urN linux-2.4.26/drivers/usb/gadget/Config.in linux-2.4.27/drivers/usb/gadget/Config.in --- linux-2.4.26/drivers/usb/gadget/Config.in 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/Config.in 2004-08-07 16:26:05.683389059 -0700 @@ -53,6 +53,9 @@ dep_tristate ' Gadget Zero (DEVELOPMENT)' CONFIG_USB_ZERO $CONFIG_USB_GADGET_CONTROLLER dep_tristate ' Ethernet Gadget (EXPERIMENTAL)' CONFIG_USB_ETH $CONFIG_USB_GADGET_CONTROLLER $CONFIG_NET + if [ "$CONFIG_USB_ETH" = "y" -o "$CONFIG_USB_ETH" = "m" ] ; then + bool ' RNDIS support (EXPERIMENTAL)' CONFIG_USB_ETH_RNDIS + fi dep_tristate ' File-backed Storage Gadget (DEVELOPMENT)' CONFIG_USB_FILE_STORAGE $CONFIG_USB_GADGET_CONTROLLER dep_mbool ' File-backed Storage Gadget test mode' CONFIG_USB_FILE_STORAGE_TEST $CONFIG_USB_FILE_STORAGE diff -urN linux-2.4.26/drivers/usb/gadget/Makefile linux-2.4.27/drivers/usb/gadget/Makefile --- linux-2.4.26/drivers/usb/gadget/Makefile 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/Makefile 2004-08-07 16:26:05.684389100 -0700 @@ -16,13 +16,17 @@ controller-$(CONFIG_USB_GOKU) += goku_udc.o # ... and only one of these, too; kbuild/kconfig don't help though. -g_zero-objs := zero.o usbstring.o config.o +g_zero-objs := zero.o usbstring.o config.o epautoconf.o obj-$(CONFIG_USB_ZERO) += g_zero.o -g_ether-objs := ether.o usbstring.o config.o +g_ether-objs := ether.o usbstring.o config.o epautoconf.o +ifeq ($(CONFIG_USB_ETH_RNDIS),y) + g_ether-objs += rndis.o +endif obj-$(CONFIG_USB_ETH) += g_ether.o -g_file_storage-objs := file_storage.o usbstring.o +g_file_storage-objs := file_storage.o usbstring.o config.o \ + epautoconf.o obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o export-objs := $(controller-y) $(controller-m) diff -urN linux-2.4.26/drivers/usb/gadget/config.c linux-2.4.27/drivers/usb/gadget/config.c --- linux-2.4.26/drivers/usb/gadget/config.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/config.c 2004-08-07 16:26:05.684389100 -0700 @@ -51,7 +51,7 @@ for (; 0 != *src; src++) { unsigned len = (*src)->bLength; - if (len > buflen); + if (len > buflen) return -EINVAL; memcpy(dest, *src, len); buflen -= len; diff -urN linux-2.4.26/drivers/usb/gadget/epautoconf.c linux-2.4.27/drivers/usb/gadget/epautoconf.c --- linux-2.4.26/drivers/usb/gadget/epautoconf.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/usb/gadget/epautoconf.c 2004-08-07 16:26:05.685389141 -0700 @@ -0,0 +1,308 @@ +/* + * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers + * + * Copyright (C) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gadget_chips.h" + + +/* we must assign addresses for configurable endpoints (like net2280) */ +static __initdata unsigned epnum; + +// #define MANY_ENDPOINTS +#ifdef MANY_ENDPOINTS +/* more than 15 configurable endpoints */ +static __initdata unsigned in_epnum; +#endif + + +/* + * This should work with endpoints from controller drivers sharing the + * same endpoint naming convention. By example: + * + * - ep1, ep2, ... address is fixed, not direction or type + * - ep1in, ep2out, ... address and direction are fixed, not type + * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction + * - ep1in-bulk, ep2out-iso, ... all three are fixed + * - ep-* ... no functionality restrictions + * + * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal. + * Less common restrictions are implied by gadget_is_*(). + */ +static int __init +ep_matches ( + struct usb_gadget *gadget, + struct usb_ep *ep, + struct usb_endpoint_descriptor *desc +) +{ + u8 type; + const char *tmp; + u16 max; + + /* endpoint already claimed? */ + if (0 != ep->driver_data) + return 0; + + /* only support ep0 for portable CONTROL traffic */ + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + if (USB_ENDPOINT_XFER_CONTROL == type) + return 0; + + /* some other naming convention */ + if ('e' != ep->name[0]) + return 0; + + /* type-restriction: "-iso", "-bulk", or "-int". + * direction-restriction: "in", "out". + */ + if ('-' != ep->name[2]) { + tmp = strrchr (ep->name, '-'); + if (tmp) { + switch (type) { + case USB_ENDPOINT_XFER_INT: + /* bulk endpoints handle interrupt transfers, + * except the toggle-quirky iso-synch kind + */ + if ('s' == tmp[2]) // == "-iso" + return 0; + /* for now, avoid PXA "interrupt-in"; + * it's documented as never using DATA1. + */ + if (gadget_is_pxa (gadget) + && 'i' == tmp [1]) + return 0; + break; + case USB_ENDPOINT_XFER_BULK: + if ('b' != tmp[1]) // != "-bulk" + return 0; + break; + case USB_ENDPOINT_XFER_ISOC: + if ('s' != tmp[2]) // != "-iso" + return 0; + } + } else { + tmp = ep->name + strlen (ep->name); + } + + /* direction-restriction: "..in-..", "out-.." */ + tmp--; + if (!isdigit (*tmp)) { + if (desc->bEndpointAddress & USB_DIR_IN) { + if ('n' != *tmp) + return 0; + } else { + if ('t' != *tmp) + return 0; + } + } + } + + /* endpoint maxpacket size is an input parameter, except for bulk + * where it's an output parameter representing the full speed limit. + * the usb spec fixes high speed bulk maxpacket at 512 bytes. + */ + max = 0x7ff & le16_to_cpup (&desc->wMaxPacketSize); + switch (type) { + case USB_ENDPOINT_XFER_INT: + /* INT: limit 64 bytes full speed, 1024 high speed */ + if (!gadget->is_dualspeed && max > 64) + return 0; + /* FALLTHROUGH */ + + case USB_ENDPOINT_XFER_ISOC: + /* ISO: limit 1023 bytes full speed, 1024 high speed */ + if (ep->maxpacket < max) + return 0; + if (!gadget->is_dualspeed && max > 1023) + return 0; + + /* BOTH: "high bandwidth" works only at high speed */ + if ((desc->wMaxPacketSize & __constant_cpu_to_le16(3<<11))) { + if (!gadget->is_dualspeed) + return 0; + /* configure your hardware with enough buffering!! */ + } + break; + } + + /* MATCH!! */ + + /* report address */ + if (isdigit (ep->name [2])) { + u8 num = simple_strtol (&ep->name [2], NULL, 10); + desc->bEndpointAddress |= num; +#ifdef MANY_ENDPOINTS + } else if (desc->bEndpointAddress & USB_DIR_IN) { + if (++in_epnum > 15) + return 0; + desc->bEndpointAddress = USB_DIR_IN | in_epnum; +#endif + } else { + if (++epnum > 15) + return 0; + desc->bEndpointAddress |= epnum; + } + + /* report (variable) full speed bulk maxpacket */ + if (USB_ENDPOINT_XFER_BULK == type) { + int size = ep->maxpacket; + + /* min() doesn't work on bitfields with gcc-3.5 */ + if (size > 64) + size = 64; + desc->wMaxPacketSize = cpu_to_le16(size); + } + return 1; +} + +static struct usb_ep * __init +find_ep (struct usb_gadget *gadget, const char *name) +{ + struct usb_ep *ep; + + list_for_each_entry (ep, &gadget->ep_list, ep_list) { + if (0 == strcmp (ep->name, name)) + return ep; + } + return 0; +} + +/** + * usb_ep_autoconfig - choose an endpoint matching the descriptor + * @gadget: The device to which the endpoint must belong. + * @desc: Endpoint descriptor, with endpoint direction and transfer mode + * initialized. For periodic transfers, the maximum packet + * size must also be initialized. This is modified on success. + * + * By choosing an endpoint to use with the specified descriptor, this + * routine simplifies writing gadget drivers that work with multiple + * USB device controllers. The endpoint would be passed later to + * usb_ep_enable(), along with some descriptor. + * + * That second descriptor won't always be the same as the first one. + * For example, isochronous endpoints can be autoconfigured for high + * bandwidth, and then used in several lower bandwidth altsettings. + * Also, high and full speed descriptors will be different. + * + * Be sure to examine and test the results of autoconfiguration on your + * hardware. This code may not make the best choices about how to use the + * USB controller, and it can't know all the restrictions that may apply. + * Some combinations of driver and hardware won't be able to autoconfigure. + * + * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value + * is initialized as if the endpoint were used at full speed. To prevent + * the endpoint from being returned by a later autoconfig call, claim it + * by assigning ep->driver_data to some non-null value. + * + * On failure, this returns a null endpoint descriptor. + */ +struct usb_ep * __init usb_ep_autoconfig ( + struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc +) +{ + struct usb_ep *ep; + u8 type; + + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* First, apply chip-specific "best usage" knowledge. + * This might make a good usb_gadget_ops hook ... + */ + if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep (gadget, "ep-e"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + ep = find_ep (gadget, "ep-f"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + + } else if (gadget_is_goku (gadget)) { + if (USB_ENDPOINT_XFER_INT == type) { + /* single buffering is enough */ + ep = find_ep (gadget, "ep3-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + } else if (USB_ENDPOINT_XFER_BULK == type + && (USB_DIR_IN & desc->bEndpointAddress)) { + /* DMA may be available */ + ep = find_ep (gadget, "ep2-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + } + + } else if (gadget_is_sh (gadget) && USB_ENDPOINT_XFER_INT == type) { + /* single buffering is enough; maybe 8 byte fifo is too */ + ep = find_ep (gadget, "ep3in-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + + } else if (gadget_is_mq11xx (gadget) && USB_ENDPOINT_XFER_INT == type) { + ep = find_ep (gadget, "ep1-bulk"); + if (ep && ep_matches (gadget, ep, desc)) + return ep; + } + + /* Second, look at endpoints until an unclaimed one looks usable */ + list_for_each_entry (ep, &gadget->ep_list, ep_list) { + if (ep_matches (gadget, ep, desc)) + return ep; + } + + /* Fail */ + return 0; +} + +/** + * usb_ep_autoconfig_reset - reset endpoint autoconfig state + * @gadget: device for which autoconfig state will be reset + * + * Use this for devices where one configuration may need to assign + * endpoint resources very differently from the next one. It clears + * state such as ep->driver_data and the record of assigned endpoints + * used by usb_ep_autoconfig(). + */ +void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget) +{ + struct usb_ep *ep; + + list_for_each_entry (ep, &gadget->ep_list, ep_list) { + ep->driver_data = 0; + } +#ifdef MANY_ENDPOINTS + in_epnum = 0; +#endif + epnum = 0; +} + diff -urN linux-2.4.26/drivers/usb/gadget/ether.c linux-2.4.27/drivers/usb/gadget/ether.c --- linux-2.4.26/drivers/usb/gadget/ether.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/ether.c 2004-08-07 16:26:05.691389388 -0700 @@ -1,7 +1,8 @@ /* * ether.c -- Ethernet gadget driver, with CDC and non-CDC options * - * Copyright (C) 2003 David Brownell + * Copyright (C) 2003-2004 David Brownell + * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger * * 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 @@ -23,8 +24,8 @@ // #define VERBOSE #include -#include #include +#include #include #include #include @@ -37,6 +38,8 @@ #include #include #include +#include +#include #include #include @@ -53,6 +56,8 @@ #include #include +#include "gadget_chips.h" + /*-------------------------------------------------------------------------*/ /* @@ -60,39 +65,54 @@ * * CDC Ethernet is the standard USB solution for sending Ethernet frames * using USB. Real hardware tends to use the same framing protocol but look - * different for control features. And Microsoft pushes their own approach - * (RNDIS) instead of the standard. + * different for control features. This driver strongly prefers to use + * this USB-IF standard as its open-systems interoperability solution; + * most host side USB stacks (except from Microsoft) support it. * * There's some hardware that can't talk CDC. We make that hardware * implement a "minimalist" vendor-agnostic CDC core: same framing, but * link-level setup only requires activating the configuration. + * Linux supports it, but other host operating systems may not. + * (This is a subset of CDC Ethernet.) + * + * A third option is also in use. Rather than CDC Ethernet, or something + * simpler, Microsoft pushes their own approach: RNDIS. The published + * RNDIS specs are ambiguous and appear to be incomplete, and are also + * needlessly complex. */ #define DRIVER_DESC "Ethernet Gadget" -#define DRIVER_VERSION "Bastille Day 2003" +#define DRIVER_VERSION "St Patrick's Day 2004" static const char shortname [] = "ether"; static const char driver_desc [] = DRIVER_DESC; -#define MIN_PACKET sizeof(struct ethhdr) -#define MAX_PACKET ETH_DATA_LEN /* biggest packet we'll rx/tx */ #define RX_EXTRA 20 /* guard against rx overflows */ -/* FIXME allow high speed jumbograms */ - -/*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_ETH_RNDIS +#include "rndis.h" +#else +#define rndis_init() 0 +#define rndis_exit() do{}while(0) +#endif +/* 2.6-compat */ #ifndef container_of -#define container_of list_entry +#define container_of list_entry #endif -/* 2.5 modified and renamed these */ -#ifndef INIT_WORK -#define work_struct tq_struct -#define INIT_WORK INIT_TQUEUE +#include +#define work_struct tq_struct +#define INIT_WORK INIT_TQUEUE #define schedule_work schedule_task #define flush_scheduled_work flush_scheduled_tasks -#endif + +static void random_ether_addr (u8 *addr) +{ + get_random_bytes (addr, ETH_ALEN); + addr [0] &= 0xfe; // clear multicast bit + addr [0] |= 0x02; // set local assignment bit (IEEE802) +} /*-------------------------------------------------------------------------*/ @@ -112,219 +132,147 @@ atomic_t tx_qlen; struct work_struct work; + unsigned zlp:1; + unsigned cdc:1; + unsigned rndis:1; + unsigned suspended:1; + u16 cdc_filter; unsigned long todo; #define WORK_RX_MEMORY 0 + int rndis_config; + u8 host_mac [ETH_ALEN]; }; +/* This version autoconfigures as much as possible at run-time. + * + * It also ASSUMES a self-powered device, without remote wakeup, + * although remote wakeup support would make sense. + */ +static const char *EP_IN_NAME; +static const char *EP_OUT_NAME; +static const char *EP_STATUS_NAME; + /*-------------------------------------------------------------------------*/ -/* Thanks to NetChip Technologies for donating this product ID. - * - * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! +/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ -#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ -#define DRIVER_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ -/*-------------------------------------------------------------------------*/ +/* Thanks to NetChip Technologies for donating this product ID. + * It's for devices with only CDC Ethernet configurations. + */ +#define CDC_VENDOR_NUM 0x0525 /* NetChip */ +#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ -/* - * hardware-specific configuration, controlled by which device - * controller driver was configured. - * - * CHIP ... hardware identifier - * DRIVER_VERSION_NUM ... alerts the host side driver to differences - * EP_*_NAME ... which endpoints do we use for which purpose? - * EP_*_NUM ... numbers for them (often limited by hardware) - * WAKEUP ... if hardware supports remote wakeup AND we will issue the - * usb_gadget_wakeup() call to initiate it, USB_CONFIG_ATT_WAKEUP - * - * hw_optimize(gadget) ... for any hardware tweaks we want to kick in - * before we enable our endpoints +/* For hardware that can't talk CDC, we use the same vendor ID that + * ARM Linux has used for ethernet-over-usb, both with sa1100 and + * with pxa250. We're protocol-compatible, if the host-side drivers + * use the endpoint descriptors. bcdDevice (version) is nonzero, so + * drivers that need to hard-wire endpoint numbers have a hook. * - * add other defines for other portability issues, like hardware that - * for some reason doesn't handle full speed bulk maxpacket of 64. + * The protocol is a minimal subset of CDC Ether, which works on any bulk + * hardware that's not deeply broken ... even on hardware that can't talk + * RNDIS (like SA-1100, with no interrupt endpoint, or anything that + * doesn't handle control-OUT). */ +#define SIMPLE_VENDOR_NUM 0x049f +#define SIMPLE_PRODUCT_NUM 0x505a -#define DEV_CONFIG_VALUE 3 /* some hardware cares */ - -/* #undef on hardware that can't implement CDC */ -#define DEV_CONFIG_CDC +/* For hardware that can talk RNDIS and either of the above protocols, + * use this ID ... the windows INF files will know it. Unless it's + * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose + * the non-RNDIS configuration. + */ +#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ +#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ -/* undef on bus-powered hardware, and #define MAX_USB_POWER */ -#define SELFPOWER -/* - * NetChip 2280, PCI based. - * - * use DMA with fat fifos for all data traffic, PIO for the status channel - * where its 64 byte maxpacket ceiling is no issue. - * - * performance note: only PIO needs per-usb-packet IRQs (ep0, ep-e, ep-f) - * otherwise IRQs are per-Ethernet-packet unless TX_DELAY and chaining help. +/* Some systems will want different product identifers published in the + * device descriptor, either numbers or strings or both. These string + * parameters are in UTF-8 (superset of ASCII's 7 bit characters). */ -#ifdef CONFIG_USB_GADGET_NET2280 -#define CHIP "net2280" -#define DEFAULT_QLEN 4 /* has dma chaining */ -#define DRIVER_VERSION_NUM 0x0111 -static const char EP_OUT_NAME [] = "ep-a"; -#define EP_OUT_NUM 1 -static const char EP_IN_NAME [] = "ep-b"; -#define EP_IN_NUM 2 -static const char EP_STATUS_NAME [] = "ep-f"; -#define EP_STATUS_NUM 3 -/* supports remote wakeup, but this driver doesn't */ -extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode); +static ushort __initdata idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); -static inline void hw_optimize (struct usb_gadget *gadget) -{ - /* we can have bigger ep-a/ep-b fifos (2KB each, 4 USB packets - * for highspeed bulk) because we're not using ep-c/ep-d. - */ - net2280_set_fifo_mode (gadget, 1); -} -#endif +static ushort __initdata idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); -/* - * PXA-2xx UDC: widely used in second gen Linux-capable ARM PDAs - * and other products. The IXP-42x UDC is register-compatible. - * - * multiple interfaces (or altsettings) aren't usable. so this hardware - * can't implement CDC, which needs both capabilities. - */ -#ifdef CONFIG_USB_GADGET_PXA2XX -#undef DEV_CONFIG_CDC -#define CHIP "pxa2xx" -#define DRIVER_VERSION_NUM 0x0113 -static const char EP_OUT_NAME [] = "ep2out-bulk"; -#define EP_OUT_NUM 2 -static const char EP_IN_NAME [] = "ep1in-bulk"; -#define EP_IN_NUM 1 -/* supports remote wakeup, but this driver doesn't */ +static ushort __initdata bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); -/* no hw optimizations to apply */ -#define hw_optimize(g) do {} while (0) -#endif +static char *__initdata iManufacturer; +MODULE_PARM(iManufacturer, "s"); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); -/* - * SA-1100 UDC: widely used in first gen Linux-capable PDAs. - * - * can't have a notification endpoint, since there are only the two - * bulk-capable ones. the CDC spec allows that. - */ -#ifdef CONFIG_USB_GADGET_SA1100 -#define CHIP "sa1100" -#define DRIVER_VERSION_NUM 0x0115 -static const char EP_OUT_NAME [] = "ep1out-bulk"; -#define EP_OUT_NUM 1 -static const char EP_IN_NAME [] = "ep2in-bulk"; -#define EP_IN_NUM 2 -// EP_STATUS_NUM is undefined -/* doesn't support remote wakeup? */ +static char *__initdata iProduct; +MODULE_PARM(iProduct, "s"); +MODULE_PARM_DESC(iProduct, "USB Product string"); -/* no hw optimizations to apply */ -#define hw_optimize(g) do {} while (0) -#endif +/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ +static char *__initdata dev_addr; +MODULE_PARM(dev_addr, "s"); +MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); -/* - * Toshiba TC86C001 ("Goku-S") UDC - * - * This has three semi-configurable full speed bulk/interrupt endpoints. - */ -#ifdef CONFIG_USB_GADGET_GOKU -#define CHIP "goku" -#define DRIVER_VERSION_NUM 0x0116 -static const char EP_OUT_NAME [] = "ep1-bulk"; -#define EP_OUT_NUM 1 -static const char EP_IN_NAME [] = "ep2-bulk"; -#define EP_IN_NUM 2 -static const char EP_STATUS_NAME [] = "ep3-bulk"; -#define EP_STATUS_NUM 3 -/* doesn't support remote wakeup */ +/* this address is invisible to ifconfig */ +static char *__initdata host_addr; +MODULE_PARM(host_addr, "s"); +MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); + + +/*-------------------------------------------------------------------------*/ -#define hw_optimize(g) do {} while (0) +/* Include CDC support if we could run on CDC-capable hardware. */ + +#ifdef CONFIG_USB_GADGET_NET2280 +#define DEV_CONFIG_CDC #endif -/* - * SuperH UDC: UDC built-in to some Renesas SH processors. - * - * This has three semi-configurable full speed bulk/interrupt endpoints. - * - * Only one configuration and interface is supported. So this hardware - * can't implement CDC. - */ -#ifdef CONFIG_USB_GADGET_SUPERH -#undef DEV_CONFIG_CDC -#define CHIP "superh" -#define DRIVER_VERSION_NUM 0x0117 -static const char EP_OUT_NAME[] = "ep1out-bulk"; -#define EP_OUT_NUM 1 -static const char EP_IN_NAME[] = "ep2in-bulk"; -#define EP_IN_NUM 2 +#ifdef CONFIG_USB_GADGET_DUMMY_HCD +#define DEV_CONFIG_CDC +#endif -#define hw_optimize(g) do {} while (0) +#ifdef CONFIG_USB_GADGET_GOKU +#define DEV_CONFIG_CDC #endif -/*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_GADGET_MQ11XX +#define DEV_CONFIG_CDC +#endif -#ifndef CHIP -# error Configure some USB peripheral controller driver! +#ifdef CONFIG_USB_GADGET_OMAP +#define DEV_CONFIG_CDC #endif -/* We normally expect hardware that can talk CDC. That involves - * using multiple interfaces and altsettings, and maybe a status - * interrupt. Driver binding to be done according to USB-IF class, - * though you can use different VENDOR and PRODUCT numbers if you - * want (and they're officially assigned). - * - * For hardware that can't talk CDC, we use the same vendor ID that - * ARM Linux has used for ethernet-over-usb, both with sa1100 and - * with pxa250. We're protocol-compatible, if the host-side drivers - * use the endpoint descriptors. DRIVER_VERSION_NUM is nonzero, so - * drivers that need to hard-wire endpoint numbers have a hook. + +/* For CDC-incapable hardware, choose the simple cdc subset. + * Anything that talks bulk (without notable bugs) can do this. */ -#ifdef DEV_CONFIG_CDC -#define DEV_CONFIG_CLASS USB_CLASS_COMM -#else -#define DEV_CONFIG_CLASS USB_CLASS_VENDOR_SPEC -#undef EP_STATUS_NUM -#undef DRIVER_VENDOR_NUM -#undef DRIVER_PRODUCT_NUM -#define DRIVER_VENDOR_NUM 0x049f -#define DRIVER_PRODUCT_NUM 0x505a -#endif /* CONFIG_CDC_ETHER */ - -/* power usage is config specific. - * hardware that supports remote wakeup defaults to disabling it. - */ - -#ifndef MAX_USB_POWER -#ifdef SELFPOWER -/* some hosts are confused by 0mA */ -#define MAX_USB_POWER 2 /* mA */ -#else -/* bus powered */ -#error Define your bus power consumption! +#ifdef CONFIG_USB_GADGET_PXA2XX +#define DEV_CONFIG_SUBSET +#endif + +#ifdef CONFIG_USB_GADGET_SH +#define DEV_CONFIG_SUBSET #endif -#endif /* MAX_USB_POWER */ -#ifndef WAKEUP -/* default: this driver won't do remote wakeup */ -#define WAKEUP 0 -/* else value must be USB_CONFIG_ATT_WAKEUP */ +#ifdef CONFIG_USB_GADGET_SA1100 +/* use non-CDC for backwards compatibility */ +#define DEV_CONFIG_SUBSET #endif + /*-------------------------------------------------------------------------*/ -#ifndef DEFAULT_QLEN #define DEFAULT_QLEN 2 /* double buffering by default */ -#endif #ifdef CONFIG_USB_GADGET_DUALSPEED static unsigned qmult = 5; -MODULE_PARM (qmult, "i"); +module_param (qmult, uint, 0); /* for dual-speed hardware, use deeper queues at highspeed */ @@ -332,10 +280,14 @@ (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) /* also defer IRQs on highspeed TX */ -#define TX_DELAY DEFAULT_QLEN +#define TX_DELAY qmult + +#define BITRATE(g) ((g->speed == USB_SPEED_HIGH) ? 4800000 : 120000) #else /* full speed (low speed doesn't do bulk) */ #define qlen(gadget) DEFAULT_QLEN + +#define BITRATE(g) (12000) #endif @@ -376,8 +328,13 @@ /* * DESCRIPTORS ... most are static, but strings and (full) configuration - * descriptors are built on demand. Notice how most of the cdc descriptors - * aren't needed in the "minimalist" mode. + * descriptors are built on demand. For now we do either full CDC, or + * our simple subset, with RNDIS as an optional second configuration. + * + * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But + * the class descriptors match a modem (they're ignored; it's really just + * Ethernet functionality), they don't need the NOP altsetting, and the + * status transfer endpoint isn't optional. */ #define STRING_MANUFACTURER 1 @@ -385,12 +342,24 @@ #define STRING_ETHADDR 3 #define STRING_DATA 4 #define STRING_CONTROL 5 +#define STRING_RNDIS_CONTROL 6 +#define STRING_CDC 7 +#define STRING_SUBSET 8 +#define STRING_RNDIS 9 #define USB_BUFSIZ 256 /* holds our biggest descriptor */ /* - * This device advertises one configuration. + * This device advertises one configuration, eth_config, unless RNDIS + * is enabled (rndis_config) on hardware supporting at least two configs. + * + * NOTE: Controllers like superh_udc should probably be able to use + * an RNDIS-only configuration. */ + +#define DEV_CONFIG_VALUE 1 /* cdc or subset */ +#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ + static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, @@ -398,13 +367,12 @@ .bcdUSB = __constant_cpu_to_le16 (0x0200), - .bDeviceClass = DEV_CONFIG_CLASS, + .bDeviceClass = USB_CLASS_COMM, .bDeviceSubClass = 0, .bDeviceProtocol = 0, - .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM), - .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM), - .bcdDevice = __constant_cpu_to_le16 (DRIVER_VERSION_NUM), + .idVendor = __constant_cpu_to_le16 (CDC_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16 (CDC_PRODUCT_NUM), .iManufacturer = STRING_MANUFACTURER, .iProduct = STRING_PRODUCT, .bNumConfigurations = 1, @@ -416,44 +384,70 @@ .bDescriptorType = USB_DT_CONFIG, /* compute wTotalLength on the fly */ -#ifdef DEV_CONFIG_CDC .bNumInterfaces = 2, -#else - .bNumInterfaces = 1, -#endif .bConfigurationValue = DEV_CONFIG_VALUE, - .iConfiguration = STRING_PRODUCT, - .bmAttributes = USB_CONFIG_ATT_ONE | WAKEUP, - .bMaxPower = (MAX_USB_POWER + 1) / 2, + .iConfiguration = STRING_CDC, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, }; -#ifdef DEV_CONFIG_CDC +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_config_descriptor +rndis_config = { + .bLength = sizeof rndis_config, + .bDescriptorType = USB_DT_CONFIG, + + /* compute wTotalLength on the fly */ + .bNumInterfaces = 2, + .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, + .iConfiguration = STRING_RNDIS, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, +}; +#endif /* - * Compared to the "minimalist" non-CDC model, the CDC model adds - * three class descriptors, two interface descrioptors, and a status + * Compared to the simple CDC subset, the full CDC Ethernet model adds + * three class descriptors, two interface descriptors, optional status * endpoint. Both have a "data" interface and two bulk endpoints. * There are also differences in how control requests are handled. + * + * RNDIS shares a lot with CDC-Ethernet, since it's a variant of + * the CDC-ACM (modem) spec. */ -/* master comm interface optionally has a status notification endpoint */ - -static const struct usb_interface_descriptor +#ifdef DEV_CONFIG_CDC +static struct usb_interface_descriptor control_intf = { .bLength = sizeof control_intf, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, -#ifdef EP_STATUS_NUM + /* status endpoint is optional; this may be patched later */ .bNumEndpoints = 1, -#else - .bNumEndpoints = 0, -#endif .bInterfaceClass = USB_CLASS_COMM, .bInterfaceSubClass = 6, /* ethernet control model */ .bInterfaceProtocol = 0, .iInterface = STRING_CONTROL, }; +#endif + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_interface_descriptor +rndis_control_intf = { + .bLength = sizeof rndis_control_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_COMM, + .bInterfaceSubClass = 2, /* abstract control model */ + .bInterfaceProtocol = 0xff, /* vendor specific */ + .iInterface = STRING_RNDIS_CONTROL, +}; +#endif + +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) /* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ struct header_desc { @@ -466,13 +460,13 @@ static const struct header_desc header_desc = { .bLength = sizeof header_desc, - .bDescriptorType = 0x24, + .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = 0, .bcdCDC = __constant_cpu_to_le16 (0x0110), }; -/* "Union Functional Descriptor" from CDC spec 5.2.3.X */ +/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ struct union_desc { u8 bLength; u8 bDescriptorType; @@ -485,13 +479,58 @@ static const struct union_desc union_desc = { .bLength = sizeof union_desc, - .bDescriptorType = 0x24, + .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = 6, .bMasterInterface0 = 0, /* index of control interface */ .bSlaveInterface0 = 1, /* index of DATA interface */ }; +#endif /* CDC || RNDIS */ + +#ifdef CONFIG_USB_ETH_RNDIS + +/* "Call Management Descriptor" from CDC spec 5.2.3.3 */ +struct call_mgmt_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + + u8 bmCapabilities; + u8 bDataInterface; +} __attribute__ ((packed)); + +static const struct call_mgmt_descriptor call_mgmt_descriptor = { + .bLength = sizeof call_mgmt_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = 0x01, + + .bmCapabilities = 0x00, + .bDataInterface = 0x01, +}; + + +/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.4 */ +struct acm_descriptor { + u8 bLength; + u8 bDescriptorType; + u8 bDescriptorSubType; + + u8 bmCapabilities; +} __attribute__ ((packed)); + +static struct acm_descriptor acm_descriptor = { + .bLength = sizeof acm_descriptor, + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = 0x02, + + .bmCapabilities = 0X00, +}; + +#endif + +#ifdef DEV_CONFIG_CDC + /* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ struct ether_desc { u8 bLength; @@ -507,41 +546,53 @@ static const struct ether_desc ether_desc = { .bLength = sizeof ether_desc, - .bDescriptorType = 0x24, + .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = 0x0f, /* this descriptor actually adds value, surprise! */ .iMACAddress = STRING_ETHADDR, .bmEthernetStatistics = __constant_cpu_to_le32 (0), /* no statistics */ - .wMaxSegmentSize = __constant_cpu_to_le16 (MAX_PACKET + ETH_HLEN), + .wMaxSegmentSize = __constant_cpu_to_le16 (ETH_FRAME_LEN), .wNumberMCFilters = __constant_cpu_to_le16 (0), .bNumberPowerFilters = 0, }; -#ifdef EP_STATUS_NUM +#endif -/* include the status endpoint if we can, even though it's optional. +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + +/* include the status endpoint if we can, even where it's optional. + * use small wMaxPacketSize, since many "interrupt" endpoints have + * very small fifos and it's no big deal if CDC_NOTIFY_SPEED_CHANGE + * takes two packets. also default to a big transfer interval, to + * waste less bandwidth. * - * some drivers (like current Linux cdc-ether!) "need" it to exist even + * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even * if they ignore the connect/disconnect notifications that real aether * can provide. more advanced cdc configurations might want to support * encapsulated commands (vendor-specific, using control-OUT). + * + * RNDIS requires the status endpoint, since it uses that encapsulation + * mechanism for its funky RPC scheme. */ -#define LOG2_STATUS_INTERVAL_MSEC 6 -#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ -static const struct usb_endpoint_descriptor +#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ +#define STATUS_BYTECOUNT 8 /* 8 byte header + data */ + +static struct usb_endpoint_descriptor fs_status_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_STATUS_NUM | USB_DIR_IN, + .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, }; #endif +#ifdef DEV_CONFIG_CDC + /* the default data interface has no endpoints ... */ static const struct usb_interface_descriptor @@ -555,10 +606,9 @@ .bInterfaceClass = USB_CLASS_CDC_DATA, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, - .iInterface = STRING_DATA, }; -/* ... but the "real" data interface has two full speed bulk endpoints */ +/* ... but the "real" data interface has two bulk endpoints */ static const struct usb_interface_descriptor data_intf = { @@ -573,16 +623,39 @@ .bInterfaceProtocol = 0, .iInterface = STRING_DATA, }; -#else + +#endif + +#ifdef CONFIG_USB_ETH_RNDIS + +/* RNDIS doesn't activate by changing to the "real" altsetting */ + +static const struct usb_interface_descriptor +rndis_data_intf = { + .bLength = sizeof rndis_data_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_CDC_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = STRING_DATA, +}; + +#endif + +#ifdef DEV_CONFIG_SUBSET /* - * "Minimalist" non-CDC option is a simple vendor-neutral model that most + * "Simple" CDC-subset option is a simple vendor-neutral model that most * full speed controllers can handle: one interface, two bulk endpoints. */ static const struct usb_interface_descriptor -data_intf = { - .bLength = sizeof data_intf, +subset_data_intf = { + .bLength = sizeof subset_data_intf, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = 0, @@ -594,47 +667,73 @@ .iInterface = STRING_DATA, }; -#endif /* DEV_CONFIG_CDC */ +#endif /* SUBSET */ -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_source_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_IN_NUM | USB_DIR_IN, + .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (64), }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_sink_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_OUT_NUM, + .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (64), }; -static const struct usb_descriptor_header *fs_function [] = { +static const struct usb_descriptor_header *fs_eth_function [10] = { #ifdef DEV_CONFIG_CDC /* "cdc" mode descriptors */ (struct usb_descriptor_header *) &control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &union_desc, (struct usb_descriptor_header *) ðer_desc, -#ifdef EP_STATUS_NUM + /* NOTE: status endpoint may need to be removed */ (struct usb_descriptor_header *) &fs_status_desc, -#endif + /* data interface, with altsetting */ (struct usb_descriptor_header *) &data_nop_intf, -#endif /* DEV_CONFIG_CDC */ - /* minimalist core */ (struct usb_descriptor_header *) &data_intf, (struct usb_descriptor_header *) &fs_source_desc, (struct usb_descriptor_header *) &fs_sink_desc, 0, +#endif /* DEV_CONFIG_CDC */ +}; + +static inline void __init fs_subset_descriptors(void) +{ +#ifdef DEV_CONFIG_SUBSET + fs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf; + fs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc; + fs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc; + fs_eth_function[3] = 0; +#else + fs_eth_function[0] = 0; +#endif +} + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *fs_rndis_function [] = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) &fs_status_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &fs_source_desc, + (struct usb_descriptor_header *) &fs_sink_desc, + 0, }; +#endif #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -643,39 +742,34 @@ * descriptors, unless they only run at full speed. */ -#ifdef EP_STATUS_NUM -static const struct usb_endpoint_descriptor +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) +static struct usb_endpoint_descriptor hs_status_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_STATUS_NUM | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), - .bInterval = LOG2_STATUS_INTERVAL_MSEC + 3, + .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, }; -#endif +#endif /* DEV_CONFIG_CDC */ -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor hs_source_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_IN_NUM | USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16 (512), - .bInterval = 1, }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor hs_sink_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_OUT_NUM, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16 (512), - .bInterval = 1, }; static struct usb_qualifier_descriptor @@ -684,29 +778,57 @@ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = __constant_cpu_to_le16 (0x0200), - .bDeviceClass = DEV_CONFIG_CLASS, + .bDeviceClass = USB_CLASS_COMM, .bNumConfigurations = 1, }; -static const struct usb_descriptor_header *hs_function [] = { +static const struct usb_descriptor_header *hs_eth_function [10] = { #ifdef DEV_CONFIG_CDC /* "cdc" mode descriptors */ (struct usb_descriptor_header *) &control_intf, (struct usb_descriptor_header *) &header_desc, (struct usb_descriptor_header *) &union_desc, (struct usb_descriptor_header *) ðer_desc, -#ifdef EP_STATUS_NUM + /* NOTE: status endpoint may need to be removed */ (struct usb_descriptor_header *) &hs_status_desc, -#endif + /* data interface, with altsetting */ (struct usb_descriptor_header *) &data_nop_intf, -#endif /* DEV_CONFIG_CDC */ - /* minimalist core */ (struct usb_descriptor_header *) &data_intf, (struct usb_descriptor_header *) &hs_source_desc, (struct usb_descriptor_header *) &hs_sink_desc, 0, +#endif /* DEV_CONFIG_CDC */ +}; + +static inline void __init hs_subset_descriptors(void) +{ +#ifdef DEV_CONFIG_SUBSET + hs_eth_function[0] = (struct usb_descriptor_header *) &subset_data_intf; + hs_eth_function[1] = (struct usb_descriptor_header *) &fs_source_desc; + hs_eth_function[2] = (struct usb_descriptor_header *) &fs_sink_desc; + hs_eth_function[3] = 0; +#else + hs_eth_function[0] = 0; +#endif +} + +#ifdef CONFIG_USB_ETH_RNDIS +static const struct usb_descriptor_header *hs_rndis_function [] = { + /* control interface matches ACM, not Ethernet */ + (struct usb_descriptor_header *) &rndis_control_intf, + (struct usb_descriptor_header *) &header_desc, + (struct usb_descriptor_header *) &call_mgmt_descriptor, + (struct usb_descriptor_header *) &acm_descriptor, + (struct usb_descriptor_header *) &union_desc, + (struct usb_descriptor_header *) &hs_status_desc, + /* data interface has no altsetting */ + (struct usb_descriptor_header *) &rndis_data_intf, + (struct usb_descriptor_header *) &hs_source_desc, + (struct usb_descriptor_header *) &hs_sink_desc, + 0, }; +#endif /* maxpacket and other transfer characteristics vary by speed. */ @@ -717,12 +839,19 @@ /* if there's no high speed support, maxpacket doesn't change. */ #define ep_desc(g,hs,fs) fs +static inline void __init hs_subset_descriptors(void) +{ +} + #endif /* !CONFIG_USB_GADGET_DUALSPEED */ /*-------------------------------------------------------------------------*/ /* descriptors that are built on-demand */ +static char manufacturer [40]; +static char product_desc [40] = DRIVER_DESC; + #ifdef DEV_CONFIG_CDC /* address that the host will use ... usually assigned at random */ static char ethaddr [2 * ETH_ALEN + 1]; @@ -730,13 +859,21 @@ /* static strings, in iso 8859/1 */ static struct usb_string strings [] = { - { STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE "/" CHIP, }, - { STRING_PRODUCT, driver_desc, }, + { STRING_MANUFACTURER, manufacturer, }, + { STRING_PRODUCT, product_desc, }, + { STRING_DATA, "Ethernet Data", }, #ifdef DEV_CONFIG_CDC + { STRING_CDC, "CDC Ethernet", }, { STRING_ETHADDR, ethaddr, }, { STRING_CONTROL, "CDC Communications Control", }, #endif - { STRING_DATA, "Ethernet Data", }, +#ifdef DEV_CONFIG_SUBSET + { STRING_SUBSET, "CDC Ethernet Subset", }, +#endif +#ifdef CONFIG_USB_ETH_RNDIS + { STRING_RNDIS, "RNDIS", }, + { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, +#endif { } /* end of list */ }; @@ -753,20 +890,34 @@ config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) { int len; - const struct usb_descriptor_header **function = fs_function; #ifdef CONFIG_USB_GADGET_DUALSPEED int hs = (speed == USB_SPEED_HIGH); if (type == USB_DT_OTHER_SPEED_CONFIG) hs = !hs; - if (hs) - function = hs_function; +#define which_config(t) (hs ? & t ## _config : & t ## _config) +#define which_fn(t) (hs ? & hs_ ## t ## _function : & fs_ ## t ## _function) +#else +#define which_config(t) (& t ## _config) +#define which_fn(t) (& fs_ ## t ## _function) #endif - /* a single configuration must always be index 0 */ - if (index > 0) + if (index >= device_desc.bNumConfigurations) return -EINVAL; - len = usb_gadget_config_buf (ð_config, buf, USB_BUFSIZ, function); + +#ifdef CONFIG_USB_ETH_RNDIS + /* list the RNDIS config first, to make Microsoft's drivers + * happy. DOCSIS 1.0 needs this too. + */ + if (device_desc.bNumConfigurations == 2 && index == 0) + len = usb_gadget_config_buf (which_config (rndis), buf, + USB_BUFSIZ, (const struct usb_descriptor_header **) + which_fn (rndis)); + else +#endif + len = usb_gadget_config_buf (which_config (eth), buf, + USB_BUFSIZ, (const struct usb_descriptor_header **) + which_fn (eth)); if (len < 0) return len; ((struct usb_config_descriptor *) buf)->bDescriptorType = type; @@ -778,6 +929,91 @@ static void eth_start (struct eth_dev *dev, int gfp_flags); static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags); +#ifdef DEV_CONFIG_CDC +static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep) +{ + const struct usb_endpoint_descriptor *d; + + /* With CDC, the host isn't allowed to use these two data + * endpoints in the default altsetting for the interface. + * so we don't activate them yet. Reset from SET_INTERFACE. + * + * Strictly speaking RNDIS should work the same: activation is + * a side effect of setting a packet filter. Deactivation is + * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. + */ + + /* one endpoint writes data back IN to the host */ + if (strcmp (ep->name, EP_IN_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); + ep->driver_data = dev; + dev->in_ep = ep; + dev->in = d; + + /* one endpoint just reads OUT packets */ + } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); + ep->driver_data = dev; + dev->out_ep = ep; + dev->out = d; + + /* optional status/notification endpoint */ + } else if (EP_STATUS_NAME && + strcmp (ep->name, EP_STATUS_NAME) == 0) { + int result; + + d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc); + result = usb_ep_enable (ep, d); + if (result < 0) + return result; + + ep->driver_data = dev; + dev->status_ep = ep; + dev->status = d; + } + return 0; +} +#endif + +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) +static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep) +{ + int result; + const struct usb_endpoint_descriptor *d; + + /* CDC subset is simpler: if the device is there, + * it's live with rx and tx endpoints. + * + * Do this as a shortcut for RNDIS too. + */ + + /* one endpoint writes data back IN to the host */ + if (strcmp (ep->name, EP_IN_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); + result = usb_ep_enable (ep, d); + if (result < 0) + return result; + + ep->driver_data = dev; + dev->in_ep = ep; + dev->in = d; + + /* one endpoint just reads OUT packets */ + } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { + d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); + result = usb_ep_enable (ep, d); + if (result < 0) + return result; + + ep->driver_data = dev; + dev->out_ep = ep; + dev->out = d; + } + + return 0; +} +#endif + static int set_ether_config (struct eth_dev *dev, int gfp_flags) { @@ -786,34 +1022,17 @@ struct usb_gadget *gadget = dev->gadget; gadget_for_each_ep (ep, gadget) { - const struct usb_endpoint_descriptor *d; - #ifdef DEV_CONFIG_CDC - /* With CDC, the host isn't allowed to use these two data - * endpoints in the default altsetting for the interface. - * so we don't activate them yet. - */ + if (!dev->rndis && dev->cdc) { + result = ether_alt_ep_setup (dev, ep); + if (result == 0) + continue; + } +#endif - /* one endpoint writes data back IN to the host */ - if (strcmp (ep->name, EP_IN_NAME) == 0) { - d = ep_desc (gadget, &hs_source_desc, &fs_source_desc); - ep->driver_data = dev; - dev->in_ep = ep; - dev->in = d; - continue; - - /* one endpoint just reads OUT packets */ - } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { - d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc); - ep->driver_data = dev; - dev->out_ep = ep; - dev->out = d; - continue; - } - -#ifdef EP_STATUS_NUM - /* optional status/notification endpoint */ - else if (strcmp (ep->name, EP_STATUS_NAME) == 0) { +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) { + const struct usb_endpoint_descriptor *d; d = ep_desc (gadget, &hs_status_desc, &fs_status_desc); result = usb_ep_enable (ep, d); if (result == 0) { @@ -822,43 +1041,17 @@ dev->status = d; continue; } - } + } else #endif -#else /* !CONFIG_CDC_ETHER */ - - /* non-CDC is simpler: if the device is there, - * it's live with rx and tx endpoints. - */ - /* one endpoint writes data back IN to the host */ - if (strcmp (ep->name, EP_IN_NAME) == 0) { - d = ep_desc (gadget, &hs_source_desc, &fs_source_desc); - result = usb_ep_enable (ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->in_ep = ep; - dev->in = d; + { +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) + result = ether_ep_setup (dev, ep); + if (result == 0) continue; - } - - /* one endpoint just reads OUT packets */ - } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { - d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc); - result = usb_ep_enable (ep, d); - if (result == 0) { - ep->driver_data = dev; - dev->out_ep = ep; - dev->out = d; - continue; - } +#endif } -#endif /* !CONFIG_CDC_ETHER */ - - /* ignore any other endpoints */ - else - continue; - /* stop on error */ ERROR (dev, "can't enable %s, result %d\n", ep->name, result); break; @@ -869,23 +1062,41 @@ if (result == 0) result = alloc_requests (dev, qlen (gadget), gfp_flags); -#ifndef DEV_CONFIG_CDC - if (result == 0) { + /* on error, disable any endpoints */ + if (result < 0) { +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->status_ep) + (void) usb_ep_disable (dev->status_ep); +#endif + dev->status_ep = 0; + dev->status = 0; +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->rndis || !dev->cdc) { + if (dev->in_ep) + (void) usb_ep_disable (dev->in_ep); + if (dev->out_ep) + (void) usb_ep_disable (dev->out_ep); + } +#endif + dev->in_ep = 0; + dev->in = 0; + dev->out_ep = 0; + dev->out = 0; + } else + + /* activate non-CDC configs right away + * this isn't strictly according to the RNDIS spec + */ +#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) + if (dev->rndis || !dev->cdc) { netif_carrier_on (dev->net); if (netif_running (dev->net)) { spin_unlock (&dev->lock); eth_start (dev, GFP_ATOMIC); spin_lock (&dev->lock); } - } else { - (void) usb_ep_disable (dev->in_ep); - dev->in_ep = 0; - dev->in = 0; - (void) usb_ep_disable (dev->out_ep); - dev->out_ep = 0; - dev->out = 0; } -#endif /* !CONFIG_CDC_ETHER */ +#endif if (result == 0) DEBUG (dev, "qlen %d\n", qlen (gadget)); @@ -930,12 +1141,10 @@ dev->out_ep = 0; } -#ifdef EP_STATUS_NUM if (dev->status_ep) { usb_ep_disable (dev->status_ep); dev->status_ep = 0; } -#endif dev->config = 0; } @@ -951,20 +1160,29 @@ if (number == dev->config) return 0; -#ifdef CONFIG_USB_GADGET_SA1100 - if (dev->config && atomic_read (&dev->tx_qlen) != 0) { + if (gadget_is_sa1100 (gadget) + && dev->config + && atomic_read (&dev->tx_qlen) != 0) { /* tx fifo is full, but we can't clear it...*/ INFO (dev, "can't change configurations\n"); return -ESPIPE; } -#endif eth_reset_config (dev); - hw_optimize (gadget); + + /* default: pass all packets, no multicast filtering */ + dev->cdc_filter = 0x000f; switch (number) { case DEV_CONFIG_VALUE: + dev->rndis = 0; + result = set_ether_config (dev, gfp_flags); + break; +#ifdef CONFIG_USB_ETH_RNDIS + case DEV_RNDIS_CONFIG_VALUE: + dev->rndis = 1; result = set_ether_config (dev, gfp_flags); break; +#endif default: result = -EINVAL; /* FALL THROUGH */ @@ -986,21 +1204,29 @@ } dev->config = number; - INFO (dev, "%s speed config #%d: %s\n", speed, number, - driver_desc); + INFO (dev, "%s speed config #%d: %s, using %s\n", + speed, number, driver_desc, + dev->rndis + ? "RNDIS" + : (dev->cdc + ? "CDC Ethernet" + : "CDC Ethernet Subset")); } return result; } /*-------------------------------------------------------------------------*/ -#ifdef EP_STATUS_NUM - -/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications */ +/* section 3.8.2 table 11 of the CDC spec lists Ethernet notifications + * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS + * and RNDIS also defines its own bit-incompatible notifications + */ #define CDC_NOTIFY_NETWORK_CONNECTION 0x00 /* required; 6.3.1 */ #define CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 /* optional; 6.3.2 */ #define CDC_NOTIFY_SPEED_CHANGE 0x2a /* required; 6.3.8 */ +#ifdef DEV_CONFIG_CDC + struct cdc_notification { u8 bmRequestType; u8 bNotificationType; @@ -1115,19 +1341,58 @@ /* see section 3.8.2 table 10 of the CDC spec for more ethernet * requests, mostly for filters (multicast, pm) and statistics + * section 3.6.2.1 table 4 has ACM requests; RNDIS requires the + * encapsulated command mechanism. */ -#define CDC_SEND_ENCAPSULATED_REQUEST 0x00 /* optional */ -#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 /* optional */ -#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */ +#define CDC_SEND_ENCAPSULATED_COMMAND 0x00 /* optional */ +#define CDC_GET_ENCAPSULATED_RESPONSE 0x01 /* optional */ +#define CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 /* optional */ +#define CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 /* optional */ +#define CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 /* optional */ +#define CDC_SET_ETHERNET_PACKET_FILTER 0x43 /* required */ +#define CDC_GET_ETHERNET_STATISTIC 0x44 /* optional */ + +/* table 62; bits in cdc_filter */ +#define CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) +#define CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ +#define CDC_PACKET_TYPE_DIRECTED (1 << 2) +#define CDC_PACKET_TYPE_BROADCAST (1 << 3) +#define CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ + +#ifdef CONFIG_USB_ETH_RNDIS + +static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DEBUG (dev, "rndis response complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + /* done sending after CDC_GET_ENCAPSULATED_RESPONSE */ +} + +static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) +{ + struct eth_dev *dev = ep->driver_data; + int status; + + /* received RNDIS command from CDC_SEND_ENCAPSULATED_COMMAND */ + spin_lock(&dev->lock); + status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); + if (status < 0) + ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status); + spin_unlock(&dev->lock); +} + +#endif /* RNDIS */ /* * The setup() callback implements all the ep0 functionality that's not * handled lower down. CDC has a number of less-common features: * * - two interfaces: control, and ethernet data - * - data interface has two altsettings: default, and active + * - Ethernet data interface has two altsettings: default, and active * - class-specific descriptors for the control interface - * - a mandatory class-specific control request + * - class-specific control requests */ static int eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) @@ -1187,17 +1452,6 @@ value = eth_set_config (dev, ctrl->wValue, GFP_ATOMIC); spin_unlock (&dev->lock); break; -#ifdef CONFIG_USB_GADGET_PXA2XX - /* PXA UDC prevents us from using SET_INTERFACE in normal ways. - * And it hides GET_CONFIGURATION and GET_INTERFACE too. - */ - case USB_REQ_SET_INTERFACE: - spin_lock (&dev->lock); - value = eth_set_config (dev, DEV_CONFIG_VALUE, GFP_ATOMIC); - spin_unlock (&dev->lock); - break; - -#else /* hardware that that stays out of our way */ case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) break; @@ -1210,17 +1464,28 @@ || !dev->config || ctrl->wIndex > 1) break; + if (!dev->cdc && ctrl->wIndex != 0) + break; spin_lock (&dev->lock); + + /* PXA hardware partially handles SET_INTERFACE; + * we need to kluge around that interference. + */ + if (gadget_is_pxa (gadget)) { + value = eth_set_config (dev, DEV_CONFIG_VALUE, + GFP_ATOMIC); + goto done_set_intf; + } + +#ifdef DEV_CONFIG_CDC switch (ctrl->wIndex) { case 0: /* control/master intf */ if (ctrl->wValue != 0) break; -#ifdef EP_STATUS_NUM if (dev->status_ep) { usb_ep_disable (dev->status_ep); usb_ep_enable (dev->status_ep, dev->status); } -#endif value = 0; break; case 1: /* data intf */ @@ -1237,9 +1502,8 @@ usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->out_ep, dev->out); netif_carrier_on (dev->net); -#ifdef EP_STATUS_NUM - issue_start_status (dev); -#endif + if (dev->status_ep) + issue_start_status (dev); if (netif_running (dev->net)) { spin_unlock (&dev->lock); eth_start (dev, GFP_ATOMIC); @@ -1252,6 +1516,14 @@ value = 0; break; } +#else + /* FIXME this is wrong, as is the assumption that + * all non-PXA hardware talks real CDC ... + */ + dev_warn (&gadget->dev, "set_interface ignored!\n"); +#endif /* DEV_CONFIG_CDC */ + +done_set_intf: spin_unlock (&dev->lock); break; case USB_REQ_GET_INTERFACE: @@ -1259,15 +1531,16 @@ || !dev->config || ctrl->wIndex > 1) break; + if (!(dev->cdc || dev->rndis) && ctrl->wIndex != 0) + break; - /* if carrier is on, data interface is active. */ - *(u8 *)req->buf = - ((ctrl->wIndex == 1) && netif_carrier_ok (dev->net)) - ? 1 - : 0, + /* for CDC, iff carrier is on, data interface is active. */ + if (dev->rndis || ctrl->wIndex != 1) + *(u8 *)req->buf = 0; + else + *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; value = min (ctrl->wLength, (u16) 1); break; -#endif #ifdef DEV_CONFIG_CDC case CDC_SET_ETHERNET_PACKET_FILTER: @@ -1275,17 +1548,62 @@ * wValue = packet filter bitmap */ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + || !dev->cdc + || dev->rndis || ctrl->wLength != 0 || ctrl->wIndex > 1) + break; DEBUG (dev, "NOP packet filter %04x\n", ctrl->wValue); /* NOTE: table 62 has 5 filter bits to reduce traffic, * and we "must" support multicast and promiscuous. - * this NOP implements a bad filter... + * this NOP implements a bad filter (always promisc) */ + dev->cdc_filter = ctrl->wValue; value = 0; break; #endif /* DEV_CONFIG_CDC */ +#ifdef CONFIG_USB_ETH_RNDIS + /* RNDIS uses the CDC command encapsulation mechanism to implement + * an RPC scheme, with much getting/setting of attributes by OID. + */ + case CDC_SEND_ENCAPSULATED_COMMAND: + if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) + || !dev->rndis + || ctrl->wLength > USB_BUFSIZ + || ctrl->wValue + || rndis_control_intf.bInterfaceNumber + != ctrl->wIndex) + break; + /* read the request, then process it */ + value = ctrl->wLength; + req->complete = rndis_command_complete; + /* later, rndis_control_ack () sends a notification */ + break; + + case CDC_GET_ENCAPSULATED_RESPONSE: + if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) + == ctrl->bRequestType + && dev->rndis + // && ctrl->wLength >= 0x0400 + && !ctrl->wValue + && rndis_control_intf.bInterfaceNumber + == ctrl->wIndex) { + u8 *buf; + + /* return the result */ + buf = rndis_get_next_response (dev->rndis_config, + &value); + if (buf) { + memcpy (req->buf, buf, value); + req->complete = rndis_response_complete; + rndis_free_response(dev->rndis_config, buf); + } + /* else stalls ... spec says to avoid that */ + } + break; +#endif /* RNDIS */ + default: VDEBUG (dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", @@ -1320,6 +1638,8 @@ eth_reset_config (dev); spin_unlock_irqrestore (&dev->lock, flags); + /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ + /* next we may get setup() calls to enumerate new connections; * or an unbind() during shutdown (including removing module). */ @@ -1333,7 +1653,9 @@ { struct eth_dev *dev = (struct eth_dev *) net->priv; - if (new_mtu <= MIN_PACKET || new_mtu > MAX_PACKET) + // FIXME if rndis, don't change while link's live + + if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) return -ERANGE; /* no zero-length packet read wanted after mtu-sized packets */ if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0) @@ -1347,12 +1669,12 @@ return &((struct eth_dev *) net->priv)->stats; } -static int eth_ethtool_ioctl (struct net_device *net, void *useraddr) +static int eth_ethtool_ioctl (struct net_device *net, void __user *useraddr) { struct eth_dev *dev = (struct eth_dev *) net->priv; u32 cmd; - if (get_user (cmd, (u32 *)useraddr)) + if (get_user (cmd, (u32 __user *)useraddr)) return -EFAULT; switch (cmd) { @@ -1363,7 +1685,8 @@ info.cmd = ETHTOOL_GDRVINFO; strncpy (info.driver, shortname, sizeof info.driver); strncpy (info.version, DRIVER_VERSION, sizeof info.version); - strncpy (info.fw_version, CHIP, sizeof info.fw_version); + strncpy (info.fw_version, dev->gadget->name, + sizeof info.fw_version); strncpy (info.bus_info, dev->gadget->dev.bus_id, sizeof info.bus_info); if (copy_to_user (useraddr, &info, sizeof (info))) @@ -1389,7 +1712,7 @@ { switch (cmd) { case SIOCETHTOOL: - return eth_ethtool_ioctl (net, (void *)rq->ifr_data); + return eth_ethtool_ioctl(net, rq->ifr_data); default: return -EOPNOTSUPP; } @@ -1414,7 +1737,27 @@ int retval = -ENOMEM; size_t size; + /* Padding up to RX_EXTRA handles minor disagreements with host. + * Normally we use the USB "terminate on short read" convention; + * so allow up to (N*maxpacket)-1, since that memory is normally + * already allocated. Major loss of synch means -EOVERFLOW; any + * obviously corrupted packets will automatically be discarded. + * + * RNDIS uses internal framing, and explicitly allows senders to + * pad to end-of-packet. That's potentially nice for speed, + * but means receivers can't recover synch on their own. + */ size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); + size += dev->out_ep->maxpacket - 1; +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) + size += sizeof (struct rndis_packet_msg_type); +#endif + size -= size % dev->out_ep->maxpacket; +#ifdef CONFIG_USB_ETH_RNDIS + if (!dev->rndis) +#endif + size--; if ((skb = alloc_skb (size, gfp_flags)) == 0) { DEBUG (dev, "no rx skb\n"); @@ -1451,8 +1794,12 @@ /* normal completion */ case 0: skb_put (skb, req->actual); - if (MIN_PACKET > skb->len - || skb->len > (MAX_PACKET + ETH_HLEN)) { +#ifdef CONFIG_USB_ETH_RNDIS + /* we know MaxPacketsPerTransfer == 1 here */ + if (dev->rndis) + rndis_rm_hdr (req->buf, &(skb->len)); +#endif + if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) { dev->stats.rx_errors++; dev->stats.rx_length_errors++; DEBUG (dev, "rx length %d\n", skb->len); @@ -1640,6 +1987,11 @@ struct usb_request *req = 0; unsigned long flags; + /* FIXME check dev->cdc_filter to decide whether to send this, + * instead of acting as if CDC_PACKET_TYPE_PROMISCUOUS were + * always set. RNDIS has the same kind of outgoing filter. + */ + spin_lock_irqsave (&dev->lock, flags); req = container_of (dev->tx_reqs.next, struct usb_request, list); list_del (&req->list); @@ -1649,21 +2001,35 @@ /* no buffer copies needed, unless the network stack did it * or the hardware can't use skb buffers. + * or there's not enough space for any RNDIS headers we need */ +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) { + struct sk_buff *skb_rndis; + + skb_rndis = skb_realloc_headroom (skb, + sizeof (struct rndis_packet_msg_type)); + if (!skb_rndis) + goto drop; + + dev_kfree_skb_any (skb); + skb = skb_rndis; + rndis_add_hdr (skb); + length = skb->len; + } +#endif req->buf = skb->data; req->context = skb; req->complete = tx_complete; -#ifdef CONFIG_USB_GADGET_SA1100 - /* don't demand zlp (req->zero) support from all hardware */ - if ((length % dev->in_ep->maxpacket) == 0) - length++; -#else /* use zlp framing on tx for strict CDC-Ether conformance, * though any robust network rx path ignores extra padding. + * and some hardware doesn't like to write zlps. */ req->zero = 1; -#endif + if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) + length++; + req->length = length; #ifdef CONFIG_USB_GADGET_DUALSPEED @@ -1684,6 +2050,9 @@ } if (retval) { +#ifdef CONFIG_USB_ETH_RNDIS +drop: +#endif dev->stats.tx_dropped++; dev_kfree_skb_any (skb); spin_lock_irqsave (&dev->lock, flags); @@ -1695,6 +2064,81 @@ return 0; } +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_ETH_RNDIS + +static void rndis_send_media_state (struct eth_dev *dev, int connect) +{ + if (!dev) + return; + + if (connect) { + if (rndis_signal_connect (dev->rndis_config)) + return; + } else { + if (rndis_signal_disconnect (dev->rndis_config)) + return; + } +} + +static void rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DEBUG (dev, "rndis control ack complete --> %d, %d/%d\n", + req->status, req->actual, req->length); + + usb_ep_free_buffer(ep, req->buf, req->dma, 8); + usb_ep_free_request(ep, req); +} + +static int rndis_control_ack (struct net_device *net) +{ + struct eth_dev *dev = (struct eth_dev *) net->priv; + u32 length; + struct usb_request *resp; + + /* in case RNDIS calls this after disconnect */ + if (!dev->status_ep) { + DEBUG (dev, "status ENODEV\n"); + return -ENODEV; + } + + /* Allocate memory for notification ie. ACK */ + resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC); + if (!resp) { + DEBUG (dev, "status ENOMEM\n"); + return -ENOMEM; + } + + resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8, + &resp->dma, GFP_ATOMIC); + if (!resp->buf) { + DEBUG (dev, "status buf ENOMEM\n"); + usb_ep_free_request (dev->status_ep, resp); + return -ENOMEM; + } + + /* Send RNDIS RESPONSE_AVAILABLE notification; + * CDC_NOTIFY_RESPONSE_AVAILABLE should work too + */ + resp->length = 8; + resp->complete = rndis_control_ack_complete; + + *((u32 *) resp->buf) = __constant_cpu_to_le32 (1); + *((u32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); + + length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); + if (length < 0) { + resp->status = 0; + rndis_control_ack_complete (dev->status_ep, resp); + } + + return 0; +} + +#endif /* RNDIS */ + static void eth_start (struct eth_dev *dev, int gfp_flags) { DEBUG (dev, "%s\n", __FUNCTION__); @@ -1705,6 +2149,14 @@ /* and open the tx floodgates */ atomic_set (&dev->tx_qlen, 0); netif_wake_queue (dev->net); +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) { + rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, + BITRATE(dev->gadget)); + rndis_send_media_state (dev, 1); + } +#endif } static int eth_open (struct net_device *net) @@ -1739,11 +2191,19 @@ usb_ep_enable (dev->in_ep, dev->in); usb_ep_enable (dev->out_ep, dev->out); } -#ifdef EP_STATUS_NUM - usb_ep_disable (dev->status_ep); - usb_ep_enable (dev->status_ep, dev->status); -#endif + if (dev->status_ep) { + usb_ep_disable (dev->status_ep); + usb_ep_enable (dev->status_ep, dev->status); + } + } + +#ifdef CONFIG_USB_ETH_RNDIS + if (dev->rndis) { + rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, 0); + rndis_send_media_state (dev, 0); } +#endif return 0; } @@ -1756,6 +2216,10 @@ struct eth_dev *dev = get_gadget_data (gadget); DEBUG (dev, "unbind\n"); +#ifdef CONFIG_USB_ETH_RNDIS + rndis_deregister (dev->rndis_config); + rndis_exit (); +#endif /* we've already been disconnected ... no i/o is active */ if (dev->req) { @@ -1767,37 +2231,218 @@ } unregister_netdev (dev->net); - dev_put (dev->net); + free_netdev(dev->net); /* assuming we used keventd, it must quiesce too */ flush_scheduled_work (); set_gadget_data (gadget, 0); } -static int +static u8 __init nibble (unsigned char c) +{ + if (likely (isdigit (c))) + return c - '0'; + c = toupper (c); + if (likely (isxdigit (c))) + return 10 + c - 'A'; + return 0; +} + +static void __init get_ether_addr (const char *str, u8 *dev_addr) +{ + if (str) { + unsigned i; + + for (i = 0; i < 6; i++) { + unsigned char num; + + if((*str == '.') || (*str == ':')) + str++; + num = nibble(*str++) << 4; + num |= (nibble(*str++)); + dev_addr [i] = num; + } + if (is_valid_ether_addr (dev_addr)) + return; + } + random_ether_addr(dev_addr); +} + +static int __init eth_bind (struct usb_gadget *gadget) { struct eth_dev *dev; struct net_device *net; + u8 cdc = 1, zlp = 1, rndis = 1; + struct usb_ep *ep; int status = -ENOMEM; -#ifdef DEV_CONFIG_CDC - u8 node_id [ETH_ALEN]; - /* just one upstream link at a time */ - if (ethaddr [0] != 0) + /* these flags are only ever cleared; compiler take note */ +#ifndef DEV_CONFIG_CDC + cdc = 0; +#endif +#ifndef CONFIG_USB_ETH_RNDIS + rndis = 0; +#endif + + /* Because most host side USB stacks handle CDC Ethernet, that + * standard protocol is _strongly_ preferred for interop purposes. + * (By everyone except Microsoft.) + */ + if (gadget_is_net2280 (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201); + } else if (gadget_is_dummy (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0202); + } else if (gadget_is_pxa (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203); + /* pxa doesn't support altsettings */ + cdc = 0; + } else if (gadget_is_sh(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204); + /* sh doesn't support multiple interfaces or configs */ + cdc = 0; + rndis = 0; + } else if (gadget_is_sa1100 (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205); + /* hardware can't write zlps */ + zlp = 0; + /* sa1100 CAN do CDC, without status endpoint ... we use + * non-CDC to be compatible with ARM Linux-2.4 "usb-eth". + */ + cdc = 0; + } else if (gadget_is_goku (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206); + } else if (gadget_is_mq11xx (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207); + } else if (gadget_is_omap (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); + } else { + /* can't assume CDC works. don't want to default to + * anything less functional on CDC-capable hardware, + * so we fail in this case. + */ + printk (KERN_ERR "%s: controller '%s' not recognized\n", + shortname, gadget->name); + return -ENODEV; + } + snprintf (manufacturer, sizeof manufacturer, + UTS_SYSNAME " " UTS_RELEASE "/%s", + gadget->name); + + /* If there's an RNDIS configuration, that's what Windows wants to + * be using ... so use these product IDs here and in the "linux.inf" + * needed to install MSFT drivers. Current Linux kernels will use + * the second configuration if it's CDC Ethernet, and need some help + * to choose the right configuration otherwise. + */ + if (rndis) { + device_desc.idVendor = + __constant_cpu_to_le16(RNDIS_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); + snprintf (product_desc, sizeof product_desc, + "RNDIS/%s", driver_desc); + + /* CDC subset ... recognized by Linux since 2.4.10, but Windows + * drivers aren't widely available. + */ + } else if (!cdc) { + device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; + device_desc.idVendor = + __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); + device_desc.idProduct = + __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); + } + + + /* support optional vendor/distro customization */ + if (idVendor) { + if (!idProduct) { + printk (KERN_ERR "%s: idVendor needs idProduct!\n", + shortname); + return -ENODEV; + } + device_desc.idVendor = cpu_to_le16(idVendor); + device_desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + device_desc.bcdDevice = cpu_to_le16(bcdDevice); + } + if (iManufacturer) + strncpy (manufacturer, iManufacturer, sizeof manufacturer); + if (iProduct) + strncpy (product_desc, iProduct, sizeof product_desc); + + /* all we really need is bulk IN/OUT */ + usb_ep_autoconfig_reset (gadget); + ep = usb_ep_autoconfig (gadget, &fs_source_desc); + if (!ep) { +autoconf_fail: + printk (KERN_ERR "%s: can't autoconfigure on %s\n", + shortname, gadget->name); return -ENODEV; + } + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim */ + + ep = usb_ep_autoconfig (gadget, &fs_sink_desc); + if (!ep) + goto autoconf_fail; + EP_OUT_NAME = ep->name; + ep->driver_data = ep; /* claim */ + +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + /* CDC Ethernet control interface doesn't require a status endpoint. + * Since some hosts expect one, try to allocate one anyway. + */ + if (cdc || rndis) { + ep = usb_ep_autoconfig (gadget, &fs_status_desc); + if (ep) { + EP_STATUS_NAME = ep->name; + ep->driver_data = ep; /* claim */ + } else if (rndis) { + printk (KERN_ERR "%s: can't run RNDIS on %s\n", + shortname, gadget->name); + return -ENODEV; + } else if (cdc) { + control_intf.bNumEndpoints = 0; + /* FIXME remove endpoint from descriptor list */ + } + } #endif - device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + /* one config: cdc, else minimal subset */ + if (!cdc) { + eth_config.bNumInterfaces = 1; + eth_config.iConfiguration = STRING_SUBSET; + fs_subset_descriptors(); + hs_subset_descriptors(); + } + + /* For now RNDIS is always a second config */ + if (rndis) + device_desc.bNumConfigurations = 2; + #ifdef CONFIG_USB_GADGET_DUALSPEED + if (rndis) + dev_qualifier.bNumConfigurations = 2; + else if (!cdc) + dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; + /* assumes ep0 uses the same value for both speeds ... */ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; + + /* and that all endpoints are dual-speed */ + hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; + hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; +#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) + if (EP_STATUS_NAME) + hs_status_desc.bEndpointAddress = + fs_status_desc.bEndpointAddress; #endif +#endif /* DUALSPEED */ -#ifdef SELFPOWERED - eth_config.bmAttributes |= USB_CONFIG_ATT_SELFPOWERED; + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; usb_gadget_set_selfpowered (gadget); -#endif net = alloc_etherdev (sizeof *dev); if (!net) @@ -1812,25 +2457,32 @@ dev->net = net; SET_MODULE_OWNER (net); strcpy (net->name, "usb%d"); + dev->cdc = cdc; + dev->zlp = zlp; - /* one random address for the gadget device ... both of these could - * reasonably come from an id prom or a module parameter. + /* Module params for these addresses should come from ID proms. + * The host side address is used with CDC and RNDIS, and commonly + * ends up in a persistent config database. */ - get_random_bytes (net->dev_addr, ETH_ALEN); - net->dev_addr [0] &= 0xfe; // clear multicast bit - net->dev_addr [0] |= 0x02; // set local assignment bit (IEEE802) - + get_ether_addr(dev_addr, net->dev_addr); + if (cdc || rndis) { + get_ether_addr(host_addr, dev->host_mac); #ifdef DEV_CONFIG_CDC - /* ... another address for the host, on the other end of the - * link, gets exported through CDC (see CDC spec table 41) - */ - get_random_bytes (node_id, sizeof node_id); - node_id [0] &= 0xfe; // clear multicast bit - node_id [0] |= 0x02; // set local assignment bit (IEEE802) - snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", - node_id [0], node_id [1], node_id [2], - node_id [3], node_id [4], node_id [5]); + snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", + dev->host_mac [0], dev->host_mac [1], + dev->host_mac [2], dev->host_mac [3], + dev->host_mac [4], dev->host_mac [5]); #endif + } + + if (rndis) { + status = rndis_init(); + if (status < 0) { + printk (KERN_ERR "%s: can't init RNDIS, %d\n", + shortname, status); + goto fail; + } + } net->change_mtu = eth_change_mtu; net->get_stats = eth_get_stats; @@ -1867,16 +2519,60 @@ // SET_NETDEV_DEV (dev->net, &gadget->dev); status = register_netdev (dev->net); - if (status == 0) { + if (status < 0) + goto fail1; - INFO (dev, "%s, " CHIP ", version: " DRIVER_VERSION "\n", - driver_desc); -#ifdef DEV_CONFIG_CDC - INFO (dev, "CDC host enet %s\n", ethaddr); -#endif - return status; + INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); + INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, + EP_OUT_NAME, EP_IN_NAME, + EP_STATUS_NAME ? " STATUS " : "", + EP_STATUS_NAME ? EP_STATUS_NAME : "" + ); + INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + net->dev_addr [0], net->dev_addr [1], + net->dev_addr [2], net->dev_addr [3], + net->dev_addr [4], net->dev_addr [5]); + + if (cdc || rndis) + INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->host_mac [0], dev->host_mac [1], + dev->host_mac [2], dev->host_mac [3], + dev->host_mac [4], dev->host_mac [5]); + +#ifdef CONFIG_USB_ETH_RNDIS + if (rndis) { + u32 vendorID = 0; + + /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ + + dev->rndis_config = rndis_register (rndis_control_ack); + if (dev->rndis_config < 0) { +fail0: + unregister_netdev (dev->net); + status = -ENODEV; + goto fail; + } + + /* these set up a lot of the OIDs that RNDIS needs */ + rndis_set_host_mac (dev->rndis_config, dev->host_mac); + if (rndis_set_param_dev (dev->rndis_config, dev->net, + &dev->stats)) + goto fail0; + if (rndis_set_param_vendor (dev->rndis_config, vendorID, + manufacturer)) + goto fail0; + if (rndis_set_param_medium (dev->rndis_config, + NDIS_MEDIUM_802_3, + 0)) + goto fail0; + INFO (dev, "RNDIS ready\n"); } - pr_debug("%s: register_netdev failed, %d\n", shortname, status); +#endif + + return status; + +fail1: + pr_debug ("%s: register_netdev failed, %d\n", shortname, status); fail: eth_unbind (gadget); return status; @@ -1884,6 +2580,26 @@ /*-------------------------------------------------------------------------*/ +static void +eth_suspend (struct usb_gadget *gadget) +{ + struct eth_dev *dev = get_gadget_data (gadget); + + DEBUG (dev, "suspend\n"); + dev->suspended = 1; +} + +static void +eth_resume (struct usb_gadget *gadget) +{ + struct eth_dev *dev = get_gadget_data (gadget); + + DEBUG (dev, "resume\n"); + dev->suspended = 0; +} + +/*-------------------------------------------------------------------------*/ + static struct usb_gadget_driver eth_driver = { #ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH, @@ -1897,6 +2613,9 @@ .setup = eth_setup, .disconnect = eth_disconnect, + .suspend = eth_suspend, + .resume = eth_resume, + .driver = { .name = (char *) shortname, // .shutdown = ... @@ -1906,7 +2625,7 @@ }; MODULE_DESCRIPTION (DRIVER_DESC); -MODULE_AUTHOR ("David Brownell"); +MODULE_AUTHOR ("David Brownell, Benedikt Spanger"); MODULE_LICENSE ("GPL"); diff -urN linux-2.4.26/drivers/usb/gadget/file_storage.c linux-2.4.27/drivers/usb/gadget/file_storage.c --- linux-2.4.26/drivers/usb/gadget/file_storage.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/usb/gadget/file_storage.c 2004-08-07 16:26:05.695389552 -0700 @@ -1,7 +1,7 @@ /* * file_storage.c -- File-backed USB Storage Gadget, for USB development * - * Copyright (C) 2003 Alan Stern + * Copyright (C) 2003, 2004 Alan Stern * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -229,12 +229,14 @@ #include #include +#include "gadget_chips.h" + /*-------------------------------------------------------------------------*/ #define DRIVER_DESC "File-backed Storage Gadget" #define DRIVER_NAME "g_file_storage" -#define DRIVER_VERSION "14 January 2004" +#define DRIVER_VERSION "05 June 2004" static const char longname[] = DRIVER_DESC; static const char shortname[] = DRIVER_NAME; @@ -251,165 +253,11 @@ #define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget -/*-------------------------------------------------------------------------*/ - -/* - * Hardware-specific configuration, controlled by which device - * controller driver was configured. - * - * CHIP ... hardware identifier - * DRIVER_VERSION_NUM ... alerts the host side driver to differences - * EP_*_NAME ... which endpoints do we use for which purpose? - * EP_*_NUM ... numbers for them (often limited by hardware) - * FS_BULK_IN_MAXPACKET ... maxpacket value for full-speed bulk-in ep - * FS_BULK_OUT_MAXPACKET ... maxpacket value for full-speed bulk-out ep - * HIGHSPEED ... define if ep0 and descriptors need high speed support - * MAX_USB_POWER ... define if we use other than 100 mA bus current - * SELFPOWER ... if we can run on bus power, zero - * NO_BULK_STALL ... bulk endpoint halts don't work well so avoid them - */ - - -/* - * NetChip 2280, PCI based. - * - * This has half a dozen configurable endpoints, four with dedicated - * DMA channels to manage their FIFOs. It supports high speed. - * Those endpoints can be arranged in any desired configuration. - */ -#ifdef CONFIG_USB_GADGET_NET2280 -#define CHIP "net2280" -#define DRIVER_VERSION_NUM 0x0211 -static const char EP_BULK_IN_NAME[] = "ep-a"; -#define EP_BULK_IN_NUM 1 -#define FS_BULK_IN_MAXPACKET 64 -static const char EP_BULK_OUT_NAME[] = "ep-b"; -#define EP_BULK_OUT_NUM 2 -#define FS_BULK_OUT_MAXPACKET 64 -static const char EP_INTR_IN_NAME[] = "ep-e"; -#define EP_INTR_IN_NUM 5 -#define HIGHSPEED -#endif - - -/* - * Dummy_hcd, software-based loopback controller. - * - * This imitates the abilities of the NetChip 2280, so we will use - * the same configuration. - */ -#ifdef CONFIG_USB_GADGET_DUMMY -#define CHIP "dummy" -#define DRIVER_VERSION_NUM 0x0212 -static const char EP_BULK_IN_NAME[] = "ep-a"; -#define EP_BULK_IN_NUM 1 -#define FS_BULK_IN_MAXPACKET 64 -static const char EP_BULK_OUT_NAME[] = "ep-b"; -#define EP_BULK_OUT_NUM 2 -#define FS_BULK_OUT_MAXPACKET 64 -static const char EP_INTR_IN_NAME[] = "ep-e"; -#define EP_INTR_IN_NUM 5 -#define HIGHSPEED -#endif - - -/* - * PXA-2xx UDC: widely used in second gen Linux-capable PDAs. - * - * This has fifteen fixed-function full speed endpoints, and it - * can support all USB transfer types. - * - * These supports three or four configurations, with fixed numbers. - * The hardware interprets SET_INTERFACE, net effect is that you - * can't use altsettings or reset the interfaces independently. - * So stick to a single interface. - */ -#ifdef CONFIG_USB_GADGET_PXA2XX -#define CHIP "pxa2xx" -#define DRIVER_VERSION_NUM 0x0213 -static const char EP_BULK_IN_NAME[] = "ep1in-bulk"; -#define EP_BULK_IN_NUM 1 -#define FS_BULK_IN_MAXPACKET 64 -static const char EP_BULK_OUT_NAME[] = "ep2out-bulk"; -#define EP_BULK_OUT_NUM 2 -#define FS_BULK_OUT_MAXPACKET 64 -static const char EP_INTR_IN_NAME[] = "ep6in-bulk"; -#define EP_INTR_IN_NUM 6 -#endif - - /* - * SuperH UDC: UDC built-in to some Renesas SH processors. - * - * This has three fixed-function full speed bulk/interrupt endpoints. - * - * Only one configuration and interface is supported (SET_CONFIGURATION - * and SET_INTERFACE are handled completely by the hardware). - */ -#ifdef CONFIG_USB_GADGET_SUPERH -#define CHIP "superh" -#define DRIVER_VERSION_NUM 0x0215 -static const char EP_BULK_IN_NAME[] = "ep2in-bulk"; -#define EP_BULK_IN_NUM 2 -#define FS_BULK_IN_MAXPACKET 64 -static const char EP_BULK_OUT_NAME[] = "ep1out-bulk"; -#define EP_BULK_OUT_NUM 1 -#define FS_BULK_OUT_MAXPACKET 64 -static const char EP_INTR_IN_NAME[] = "ep3in-bulk"; -#define EP_INTR_IN_NUM 3 -#define NO_BULK_STALL -#endif - - -/* - * Toshiba TC86C001 ("Goku-S") UDC - * - * This has three semi-configurable full speed bulk/interrupt endpoints. - */ -#ifdef CONFIG_USB_GADGET_GOKU -#define CHIP "goku" -#define DRIVER_VERSION_NUM 0x0216 -static const char EP_BULK_OUT_NAME [] = "ep1-bulk"; -#define EP_BULK_OUT_NUM 1 -#define FS_BULK_IN_MAXPACKET 64 -static const char EP_BULK_IN_NAME [] = "ep2-bulk"; -#define EP_BULK_IN_NUM 2 -#define FS_BULK_OUT_MAXPACKET 64 -static const char EP_INTR_IN_NAME [] = "ep3-bulk"; -#define EP_INTR_IN_NUM 3 -#endif - - -/*-------------------------------------------------------------------------*/ - -#ifndef CHIP -# error Configure some USB peripheral controller driver! -#endif - -/* Power usage is config specific. - * Hardware that supports remote wakeup defaults to disabling it. + * This driver assumes self-powered hardware and has no way for users to + * trigger remote wakeup. It uses autoconfiguration to select endpoints + * and endpoint addresses. */ -#ifndef SELFPOWER -/* default: say we're self-powered */ -#define SELFPOWER USB_CONFIG_ATT_SELFPOWER -/* else: - * - SELFPOWER value must be zero - * - MAX_USB_POWER may be nonzero. - */ -#endif - -#ifndef MAX_USB_POWER -/* Any hub supports this steady state bus power consumption */ -#define MAX_USB_POWER 100 /* mA */ -#endif - -/* We don't support remote wake-up */ - -#ifdef NO_BULK_STALL -#define CAN_STALL 0 -#else -#define CAN_STALL 1 -#endif /*-------------------------------------------------------------------------*/ @@ -478,14 +326,15 @@ static char *file[MAX_LUNS] = {NULL, }; static int ro[MAX_LUNS] = {0, }; static unsigned int luns = 0; + // Default values static char *transport = "BBB"; static char *protocol = "SCSI"; static int removable = 0; static unsigned short vendor = DRIVER_VENDOR_ID; static unsigned short product = DRIVER_PRODUCT_ID; -static unsigned short release = DRIVER_VERSION_NUM; +static unsigned short release = 0xffff; // Use controller chip type static unsigned int buflen = 16384; -static int stall = CAN_STALL; +static int stall = 1; static struct { unsigned int nluns; @@ -990,7 +839,7 @@ /* The next three values can be overridden by module parameters */ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), - .bcdDevice = __constant_cpu_to_le16(DRIVER_VERSION_NUM), + .bcdDevice = __constant_cpu_to_le16(0xffff), .iManufacturer = STRING_MANUFACTURER, .iProduct = STRING_PRODUCT, @@ -1003,11 +852,11 @@ .bLength = sizeof config_desc, .bDescriptorType = USB_DT_CONFIG, - /* wTotalLength adjusted during bind() */ + /* wTotalLength computed by usb_gadget_config_buf() */ .bNumInterfaces = 1, .bConfigurationValue = CONFIG_VALUE, - .bmAttributes = USB_CONFIG_ATT_ONE | SELFPOWER, - .bMaxPower = (MAX_USB_POWER + 1) / 2, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 1, // self-powered }; /* There is only one interface. */ @@ -1017,47 +866,56 @@ .bLength = sizeof intf_desc, .bDescriptorType = USB_DT_INTERFACE, - .bNumEndpoints = 2, // Adjusted during bind() + .bNumEndpoints = 2, // Adjusted during fsg_bind() .bInterfaceClass = USB_CLASS_MASS_STORAGE, - .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during bind() - .bInterfaceProtocol = USB_PR_BULK, // Adjusted during bind() + .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() + .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() }; /* Three full-speed endpoint descriptors: bulk-in, bulk-out, * and interrupt-in. */ -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_BULK_IN_NUM | USB_DIR_IN, + .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(FS_BULK_IN_MAXPACKET), + /* wMaxPacketSize set by autoconfiguration */ }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_BULK_OUT_NUM, + .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16(FS_BULK_OUT_MAXPACKET), + /* wMaxPacketSize set by autoconfiguration */ }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_INTR_IN_NUM | USB_DIR_IN, + .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16(2), .bInterval = 32, // frames -> 32 ms }; -#ifdef HIGHSPEED +static const struct usb_descriptor_header *fs_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &fs_bulk_in_desc, + (struct usb_descriptor_header *) &fs_bulk_out_desc, + (struct usb_descriptor_header *) &fs_intr_in_desc, + NULL, +}; + + +#ifdef CONFIG_USB_GADGET_DUALSPEED /* * USB 2.0 devices need to expose both high speed and full speed @@ -1067,47 +925,55 @@ * and a "device qualifier" ... plus more construction options * for the config descriptor. */ -static const struct usb_endpoint_descriptor +static struct usb_qualifier_descriptor +dev_qualifier = { + .bLength = sizeof dev_qualifier, + .bDescriptorType = USB_DT_DEVICE_QUALIFIER, + + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + + .bNumConfigurations = 1, +}; + +static struct usb_endpoint_descriptor hs_bulk_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_BULK_IN_NUM | USB_DIR_IN, + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512), }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor hs_bulk_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_BULK_OUT_NUM, + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512), .bInterval = 1, // NAK every 1 uframe }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor hs_intr_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_INTR_IN_NUM | USB_DIR_IN, + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ .bmAttributes = USB_ENDPOINT_XFER_INT, .wMaxPacketSize = __constant_cpu_to_le16(2), .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms }; -static struct usb_qualifier_descriptor -dev_qualifier = { - .bLength = sizeof dev_qualifier, - .bDescriptorType = USB_DT_DEVICE_QUALIFIER, - - .bcdUSB = __constant_cpu_to_le16(0x0200), - .bDeviceClass = USB_CLASS_PER_INTERFACE, - - .bNumConfigurations = 1, +static const struct usb_descriptor_header *hs_function[] = { + (struct usb_descriptor_header *) &intf_desc, + (struct usb_descriptor_header *) &hs_bulk_in_desc, + (struct usb_descriptor_header *) &hs_bulk_out_desc, + (struct usb_descriptor_header *) &hs_intr_in_desc, + NULL, }; /* Maxpacket and other transfer characteristics vary by speed. */ @@ -1115,22 +981,23 @@ #else -/* If there's no high speed support, maxpacket doesn't change. */ +/* If there's no high speed support, always use the full-speed descriptor. */ #define ep_desc(g,fs,hs) fs -#endif /* !HIGHSPEED */ +#endif /* !CONFIG_USB_GADGET_DUALSPEED */ /* The CBI specification limits the serial string to 12 uppercase hexadecimal * characters. */ +static char manufacturer[40]; static char serial[13]; -/* Static strings, in ISO 8859/1 */ +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ static struct usb_string strings[] = { - { STRING_MANUFACTURER, UTS_SYSNAME " " UTS_RELEASE " with " CHIP, }, - { STRING_PRODUCT, longname, }, - { STRING_SERIAL, serial, }, - { } // end of list + {STRING_MANUFACTURER, manufacturer}, + {STRING_PRODUCT, longname}, + {STRING_SERIAL, serial}, + {} }; static struct usb_gadget_strings stringtab = { @@ -1140,61 +1007,33 @@ /* - * Config descriptors are handcrafted. They must agree with the code - * that sets configurations and with code managing interfaces and their - * altsettings. They must also handle different speeds and other-speed - * requests. + * Config descriptors must agree with the code that sets configurations + * and with code managing interfaces and their altsettings. They must + * also handle different speeds and other-speed requests. */ static int populate_config_buf(enum usb_device_speed speed, - u8 *buf0, u8 type, unsigned index) + u8 *buf, u8 type, unsigned index) { - u8 *buf = buf0; -#ifdef HIGHSPEED - int hs; -#endif + int len; + const struct usb_descriptor_header **function; if (index > 0) return -EINVAL; - if (config_desc.wTotalLength > EP0_BUFSIZE) - return -EDOM; - /* Config (or other speed config) */ - memcpy(buf, &config_desc, USB_DT_CONFIG_SIZE); - buf[1] = type; - buf += USB_DT_CONFIG_SIZE; - - /* Interface */ - memcpy(buf, &intf_desc, USB_DT_INTERFACE_SIZE); - buf += USB_DT_INTERFACE_SIZE; - - /* The endpoints in the interface (at that speed) */ -#ifdef HIGHSPEED - hs = (speed == USB_SPEED_HIGH); +#ifdef CONFIG_USB_GADGET_DUALSPEED if (type == USB_DT_OTHER_SPEED_CONFIG) - hs = !hs; - if (hs) { - memcpy(buf, &hs_bulk_in_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - memcpy(buf, &hs_bulk_out_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - if (transport_is_cbi()) { - memcpy(buf, &hs_intr_in_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - } - } else + speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; + if (speed == USB_SPEED_HIGH) + function = hs_function; + else #endif - { - memcpy(buf, &fs_bulk_in_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - memcpy(buf, &fs_bulk_out_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - if (transport_is_cbi()) { - memcpy(buf, &fs_intr_in_desc, USB_DT_ENDPOINT_SIZE); - buf += USB_DT_ENDPOINT_SIZE; - } - } + function = fs_function; - return buf - buf0; + len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); + if (len < 0) + return len; + ((struct usb_config_descriptor *) buf)->bDescriptorType = type; + return len; } @@ -1493,22 +1332,26 @@ value = min(ctrl->wLength, (u16) sizeof device_desc); memcpy(req->buf, &device_desc, value); break; -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED case USB_DT_DEVICE_QUALIFIER: VDBG(fsg, "get device qualifier\n"); + if (!fsg->gadget->is_dualspeed) + break; value = min(ctrl->wLength, (u16) sizeof dev_qualifier); memcpy(req->buf, &dev_qualifier, value); break; case USB_DT_OTHER_SPEED_CONFIG: VDBG(fsg, "get other-speed config descriptor\n"); + if (!fsg->gadget->is_dualspeed) + break; goto get_config; -#endif /* HIGHSPEED */ +#endif case USB_DT_CONFIG: VDBG(fsg, "get configuration descriptor\n"); -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED get_config: -#endif /* HIGHSPEED */ +#endif value = populate_config_buf(fsg->gadget->speed, req->buf, ctrl->wValue >> 8, @@ -2148,7 +1991,7 @@ buf[4] = 31; // Additional length // No special options sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id, - DRIVER_VERSION_NUM); + mod_data.release); return 36; } @@ -2196,7 +2039,7 @@ buf[0] = 0x80 | 0x70; // Valid, current error buf[2] = SK(sd); put_be32(&buf[3], sdinfo); // Sense information - buf[7] = 18 - 7; // Additional sense length + buf[7] = 18 - 8; // Additional sense length buf[12] = ASC(sd); buf[13] = ASCQ(sd); return 18; @@ -2643,7 +2486,7 @@ /* Store and send the Bulk-only CSW */ csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG); csw->Tag = fsg->tag; - csw->Residue = fsg->residue; + csw->Residue = cpu_to_le32(fsg->residue); csw->Status = status; bh->inreq->length = USB_BULK_CS_WRAP_LEN; @@ -3089,7 +2932,7 @@ fsg->data_dir = DATA_DIR_TO_HOST; else fsg->data_dir = DATA_DIR_FROM_HOST; - fsg->data_size = cbw->DataTransferLength; + fsg->data_size = le32_to_cpu(cbw->DataTransferLength); if (fsg->data_size == 0) fsg->data_dir = DATA_DIR_NONE; fsg->lun = cbw->Lun; @@ -3242,6 +3085,7 @@ if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) goto reset; fsg->bulk_out_enabled = 1; + fsg->bulk_out_maxpacket = d->wMaxPacketSize; if (transport_is_cbi()) { d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc); @@ -3717,6 +3561,34 @@ mod_data.protocol_type = USB_SC_SCSI; mod_data.protocol_name = "Transparent SCSI"; + if (gadget_is_sh(fsg->gadget)) + mod_data.can_stall = 0; + + if (mod_data.release == 0xffff) { // Parameter wasn't set + if (gadget_is_net2280(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0221); + else if (gadget_is_dummy(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0222); + else if (gadget_is_pxa(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0223); + else if (gadget_is_sh(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0224); + + /* The sa1100 controller is not supported */ + + else if (gadget_is_goku(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0226); + else if (gadget_is_mq11xx(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0227); + else if (gadget_is_omap(fsg->gadget)) + mod_data.release = __constant_cpu_to_le16(0x0228); + else { + WARN(fsg, "controller '%s' not recognized\n", + fsg->gadget->name); + mod_data.release = __constant_cpu_to_le16(0x0299); + } + } + prot = simple_strtol(mod_data.protocol_parm, NULL, 0); #ifdef CONFIG_USB_FILE_STORAGE_TEST @@ -3729,7 +3601,7 @@ mod_data.transport_type = USB_PR_CBI; mod_data.transport_name = "Control-Bulk-Interrupt"; } else { - INFO(fsg, "invalid transport: %s\n", mod_data.transport_parm); + ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm); return -EINVAL; } @@ -3758,13 +3630,13 @@ mod_data.protocol_type = USB_SC_8070; mod_data.protocol_name = "8070i"; } else { - INFO(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); + ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); return -EINVAL; } mod_data.buflen &= PAGE_CACHE_MASK; if (mod_data.buflen <= 0) { - INFO(fsg, "invalid buflen\n"); + ERROR(fsg, "invalid buflen\n"); return -ETOOSMALL; } #endif /* CONFIG_USB_FILE_STORAGE_TEST */ @@ -3800,7 +3672,7 @@ } } if (i > MAX_LUNS) { - INFO(fsg, "invalid number of LUNs: %d\n", i); + ERROR(fsg, "invalid number of LUNs: %d\n", i); rc = -EINVAL; goto out; } @@ -3827,45 +3699,57 @@ if ((rc = open_backing_file(curlun, file[i])) != 0) goto out; } else if (!mod_data.removable) { - INFO(fsg, "no file given for LUN%d\n", i); + ERROR(fsg, "no file given for LUN%d\n", i); rc = -EINVAL; goto out; } } + /* Find all the endpoints we will use */ + usb_ep_autoconfig_reset(gadget); + ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_out = ep; + + if (transport_is_cbi()) { + ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->intr_in = ep; + } + /* Fix up the descriptors */ device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; -#ifdef HIGHSPEED - dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; // ??? -#endif device_desc.idVendor = cpu_to_le16(mod_data.vendor); device_desc.idProduct = cpu_to_le16(mod_data.product); device_desc.bcdDevice = cpu_to_le16(mod_data.release); i = (transport_is_cbi() ? 3 : 2); // Number of endpoints - config_desc.wTotalLength = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE - + USB_DT_ENDPOINT_SIZE * i; intf_desc.bNumEndpoints = i; intf_desc.bInterfaceSubClass = mod_data.protocol_type; intf_desc.bInterfaceProtocol = mod_data.transport_type; + fs_function[i+1] = NULL; - /* Find all the endpoints we will use */ - gadget_for_each_ep(ep, gadget) { - if (strcmp(ep->name, EP_BULK_IN_NAME) == 0) - fsg->bulk_in = ep; - else if (strcmp(ep->name, EP_BULK_OUT_NAME) == 0) - fsg->bulk_out = ep; - else if (strcmp(ep->name, EP_INTR_IN_NAME) == 0) - fsg->intr_in = ep; - } - if (!fsg->bulk_in || !fsg->bulk_out || - (transport_is_cbi() && !fsg->intr_in)) { - DBG(fsg, "unable to find all endpoints\n"); - rc = -ENOTSUPP; - goto out; - } - fsg->bulk_out_maxpacket = (gadget->speed == USB_SPEED_HIGH ? 512 : - FS_BULK_OUT_MAXPACKET); +#ifdef CONFIG_USB_GADGET_DUALSPEED + hs_function[i+1] = NULL; + + /* Assume ep0 uses the same maxpacket value for both speeds */ + dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; + + /* Assume that all endpoint addresses are the same for both speeds */ + hs_bulk_in_desc.bEndpointAddress = fs_bulk_in_desc.bEndpointAddress; + hs_bulk_out_desc.bEndpointAddress = fs_bulk_out_desc.bEndpointAddress; + hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress; +#endif rc = -ENOMEM; @@ -3894,6 +3778,10 @@ /* This should reflect the actual gadget power source */ usb_gadget_set_selfpowered(gadget); + snprintf(manufacturer, sizeof manufacturer, + UTS_SYSNAME " " UTS_RELEASE " with %s", + gadget->name); + /* On a real device, serial[] would be loaded from permanent * storage. We just encode it from the driver version string. */ for (i = 0; i < sizeof(serial) - 2; i += 2) { @@ -3942,6 +3830,10 @@ DBG(fsg, "I/O thread pid: %d\n", fsg->thread_pid); return 0; +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + rc = -ENOTSUPP; + out: fsg->state = FSG_STATE_TERMINATED; // The thread is dead fsg_unbind(gadget); @@ -3953,7 +3845,7 @@ /*-------------------------------------------------------------------------*/ static struct usb_gadget_driver fsg_driver = { -#ifdef HIGHSPEED +#ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH, #else .speed = USB_SPEED_FULL, diff -urN linux-2.4.26/drivers/usb/gadget/gadget_chips.h linux-2.4.27/drivers/usb/gadget/gadget_chips.h --- linux-2.4.26/drivers/usb/gadget/gadget_chips.h 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/gadget_chips.h 2004-08-07 16:26:05.696389593 -0700 @@ -14,7 +14,13 @@ #define gadget_is_net2280(g) 0 #endif -#ifdef CONFIG_USB_GADGET_PXA +#ifdef CONFIG_USB_GADGET_DUMMY_HCD +#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) +#else +#define gadget_is_dummy(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_PXA2XX #define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) #else #define gadget_is_pxa(g) 0 diff -urN linux-2.4.26/drivers/usb/gadget/ndis.h linux-2.4.27/drivers/usb/gadget/ndis.h --- linux-2.4.26/drivers/usb/gadget/ndis.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/usb/gadget/ndis.h 2004-08-07 16:26:05.697389634 -0700 @@ -0,0 +1,217 @@ +/* + * ndis.h + * + * ntddndis.h modified by Benedikt Spranger + * + * Thanks to the cygwin development team, + * espacially to Casper S. Hornstrup + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAIMED. This includes but is not limited to warranties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifndef _LINUX_NDIS_H +#define _LINUX_NDIS_H + + +#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 +#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A +#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B + +enum NDIS_DEVICE_POWER_STATE { + NdisDeviceStateUnspecified = 0, + NdisDeviceStateD0, + NdisDeviceStateD1, + NdisDeviceStateD2, + NdisDeviceStateD3, + NdisDeviceStateMaximum +}; + +struct NDIS_PM_WAKE_UP_CAPABILITIES { + enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp; + enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp; + enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; +}; + +/* NDIS_PNP_CAPABILITIES.Flags constants */ +#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 +#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 +#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 + +struct NDIS_PNP_CAPABILITIES { + u32 Flags; + struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; +}; + +struct NDIS_PM_PACKET_PATTERN { + u32 Priority; + u32 Reserved; + u32 MaskSize; + u32 PatternOffset; + u32 PatternSize; + u32 PatternFlags; +}; + + +/* Required Object IDs (OIDs) */ +#define OID_GEN_SUPPORTED_LIST 0x00010101 +#define OID_GEN_HARDWARE_STATUS 0x00010102 +#define OID_GEN_MEDIA_SUPPORTED 0x00010103 +#define OID_GEN_MEDIA_IN_USE 0x00010104 +#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 +#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 +#define OID_GEN_LINK_SPEED 0x00010107 +#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 +#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 +#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A +#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B +#define OID_GEN_VENDOR_ID 0x0001010C +#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D +#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E +#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F +#define OID_GEN_DRIVER_VERSION 0x00010110 +#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 +#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 +#define OID_GEN_MAC_OPTIONS 0x00010113 +#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 +#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 +#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 +#define OID_GEN_SUPPORTED_GUIDS 0x00010117 +#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 +#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 +#define OID_GEN_MACHINE_NAME 0x0001021A +#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B +#define OID_GEN_VLAN_ID 0x0001021C + +/* Optional OIDs */ +#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 +#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 + +/* Required statistics OIDs */ +#define OID_GEN_XMIT_OK 0x00020101 +#define OID_GEN_RCV_OK 0x00020102 +#define OID_GEN_XMIT_ERROR 0x00020103 +#define OID_GEN_RCV_ERROR 0x00020104 +#define OID_GEN_RCV_NO_BUFFER 0x00020105 + +/* Optional statistics OIDs */ +#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 +#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 +#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 +#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 +#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 +#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 +#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 +#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 +#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 +#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A +#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B +#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C +#define OID_GEN_RCV_CRC_ERROR 0x0002020D +#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E +#define OID_GEN_GET_TIME_CAPS 0x0002020F +#define OID_GEN_GET_NETCARD_TIME 0x00020210 +#define OID_GEN_NETCARD_LOAD 0x00020211 +#define OID_GEN_DEVICE_PROFILE 0x00020212 +#define OID_GEN_INIT_TIME_MS 0x00020213 +#define OID_GEN_RESET_COUNTS 0x00020214 +#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 +#define OID_GEN_FRIENDLY_NAME 0x00020216 +#define OID_GEN_MINIPORT_INFO 0x00020217 +#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 + +/* IEEE 802.3 (Ethernet) OIDs */ +#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 + +#define OID_802_3_PERMANENT_ADDRESS 0x01010101 +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#define OID_802_3_MULTICAST_LIST 0x01010103 +#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 +#define OID_802_3_MAC_OPTIONS 0x01010105 +#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 +#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 +#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 +#define OID_802_3_XMIT_DEFERRED 0x01020201 +#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 +#define OID_802_3_RCV_OVERRUN 0x01020203 +#define OID_802_3_XMIT_UNDERRUN 0x01020204 +#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 +#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 +#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 + +/* OID_GEN_MINIPORT_INFO constants */ +#define NDIS_MINIPORT_BUS_MASTER 0x00000001 +#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 +#define NDIS_MINIPORT_SG_LIST 0x00000004 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 +#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 +#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 +#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 +#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 +#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 +#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 +#define NDIS_MINIPORT_IS_CO 0x00000400 +#define NDIS_MINIPORT_DESERIALIZE 0x00000800 +#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 +#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 +#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 +#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 +#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 +#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 +#define NDIS_MINIPORT_HIDDEN 0x00040000 +#define NDIS_MINIPORT_SWENUM 0x00080000 +#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 +#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 +#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 +#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 +#define NDIS_MINIPORT_64BITS_DMA 0x01000000 + +#define NDIS_MEDIUM_802_3 0x00000000 +#define NDIS_MEDIUM_802_5 0x00000001 +#define NDIS_MEDIUM_FDDI 0x00000002 +#define NDIS_MEDIUM_WAN 0x00000003 +#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 +#define NDIS_MEDIUM_DIX 0x00000005 +#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 +#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 +#define NDIS_MEDIUM_ATM 0x00000008 +#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 +#define NDIS_MEDIUM_IRDA 0x0000000A +#define NDIS_MEDIUM_BPC 0x0000000B +#define NDIS_MEDIUM_CO_WAN 0x0000000C +#define NDIS_MEDIUM_1394 0x0000000D + +#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 +#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 +#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 +#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 +#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 +#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 +#define NDIS_PACKET_TYPE_SMT 0x00000040 +#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 +#define NDIS_PACKET_TYPE_GROUP 0x00000100 +#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 +#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 +#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 + +#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 +#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 + +#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 +#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 +#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 +#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 +#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 +#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 +#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 +#define NDIS_MAC_OPTION_RESERVED 0x80000000 + +#endif /* _LINUX_NDIS_H */ diff -urN linux-2.4.26/drivers/usb/gadget/rndis.c linux-2.4.27/drivers/usb/gadget/rndis.c --- linux-2.4.26/drivers/usb/gadget/rndis.c 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/usb/gadget/rndis.c 2004-08-07 16:26:05.699389717 -0700 @@ -0,0 +1,1426 @@ +/* + * RNDIS MSG parser + * + * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ + * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This software was originally developed in conformance with + * Microsoft's Remote NDIS Specification License Agreement. + * + * 03/12/2004 Kai-Uwe Bloem + * Fixed message length bug in init_response + * + * 03/25/2004 Kai-Uwe Bloem + * Fixed rndis_rm_hdr length bug. + * + * Copyright (C) 2004 by David Brownell + * updates to merge with Linux 2.6, better match RNDIS spec + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#undef RNDIS_PM +#undef VERBOSE + +#include "rndis.h" + + +/* The driver for your USB chip needs to support ep0 OUT to work with + * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). + * + * Windows hosts need an INF file like Documentation/usb/linux.inf + * and will be happier if you provide the host_addr module parameter. + */ + +#if 0 +#define DEBUG(str,args...) do { \ + if (rndis_debug) \ + printk(KERN_DEBUG str , ## args ); \ + } while (0) +static int rndis_debug = 0; + +module_param (rndis_debug, bool, 0); +MODULE_PARM_DESC (rndis_debug, "enable debugging"); + +#else + +#define rndis_debug 0 +#define DEBUG(str,args...) do{}while(0) +#endif + +#define RNDIS_MAX_CONFIGS 1 + +static struct proc_dir_entry *rndis_connect_dir; +static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; + +static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; + +/* Driver Version */ +static const u32 rndis_driver_version = __constant_cpu_to_le32 (1); + +/* Function Prototypes */ +static int rndis_init_response (int configNr, rndis_init_msg_type *buf); +static int rndis_query_response (int configNr, rndis_query_msg_type *buf); +static int rndis_set_response (int configNr, rndis_set_msg_type *buf); +static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf); +static int rndis_keepalive_response (int configNr, + rndis_keepalive_msg_type *buf); + +static rndis_resp_t *rndis_add_response (int configNr, u32 length); + + +/* NDIS Functions */ +static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) +{ + int retval = -ENOTSUPP; + u32 length = 0; + u32 *tmp; + int i, count; + rndis_query_cmplt_type *resp; + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + switch (OID) { + + /* general oids (table 4-1) */ + + /* mandatory */ + case OID_GEN_SUPPORTED_LIST: + DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); + length = sizeof (oid_supported_list); + count = length / sizeof (u32); + tmp = (u32 *) ((u8 *)resp + 24); + for (i = 0; i < count; i++) + tmp[i] = cpu_to_le32 (oid_supported_list[i]); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_HARDWARE_STATUS: + DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); + length = 4; + /* Bogus question! + * Hardware must be ready to recieve high level protocols. + * BTW: + * reddite ergo quae sunt Caesaris Caesari + * et quae sunt Dei Deo! + */ + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_SUPPORTED: + DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].medium); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_IN_USE: + DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); + length = 4; + /* one medium, one transport... (maybe you do it better) */ + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].medium); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_FRAME_SIZE: + DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].dev->mtu); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_LINK_SPEED: + DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); + length = 4; + if (rndis_per_dev_params [configNr].media_state + == NDIS_MEDIA_STATE_DISCONNECTED) + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + else + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].speed); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_TRANSMIT_BLOCK_SIZE: + DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].dev->mtu); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RECEIVE_BLOCK_SIZE: + DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].dev->mtu); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_VENDOR_ID: + DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].vendorID); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_VENDOR_DESCRIPTION: + DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); + length = strlen (rndis_per_dev_params [configNr].vendorDescr); + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].vendorDescr, length); + retval = 0; + break; + + case OID_GEN_VENDOR_DRIVER_VERSION: + DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); + length = 4; + /* Created as LE */ + *((u32 *) resp + 6) = rndis_driver_version; + retval = 0; + break; + + /* mandatory */ + case OID_GEN_CURRENT_PACKET_FILTER: + DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params[configNr].filter); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MAXIMUM_TOTAL_SIZE: + DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = __constant_cpu_to_le32( + RNDIS_MAX_TOTAL_SIZE); + retval = 0; + break; + + /* mandatory */ + case OID_GEN_MEDIA_CONNECT_STATUS: + DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .media_state); + retval = 0; + break; + + case OID_GEN_PHYSICAL_MEDIUM: + DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + /* The RNDIS specification is incomplete/wrong. Some versions + * of MS-Windows expect OIDs that aren't specified there. Other + * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! + */ + case OID_GEN_MAC_OPTIONS: /* from WinME */ + DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = __constant_cpu_to_le32( + NDIS_MAC_OPTION_RECEIVE_SERIALIZED + | NDIS_MAC_OPTION_FULL_DUPLEX); + retval = 0; + break; + + /* statistics OIDs (table 4-2) */ + + /* mandatory */ + case OID_GEN_XMIT_OK: + DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].stats->tx_packets - + rndis_per_dev_params [configNr].stats->tx_errors - + rndis_per_dev_params [configNr].stats->tx_dropped); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_OK: + DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr].stats->rx_packets - + rndis_per_dev_params [configNr].stats->rx_errors - + rndis_per_dev_params [configNr].stats->rx_dropped); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_XMIT_ERROR: + DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->tx_errors); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_ERROR: + DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_errors); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + /* mandatory */ + case OID_GEN_RCV_NO_BUFFER: + DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_dropped); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + +#ifdef RNDIS_OPTIONAL_STATS + case OID_GEN_DIRECTED_BYTES_XMIT: + DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); + /* + * Aunt Tilly's size of shoes + * minus antarctica count of penguins + * divided by weight of Alpha Centauri + */ + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + (rndis_per_dev_params [configNr] + .stats->tx_packets - + rndis_per_dev_params [configNr] + .stats->tx_errors - + rndis_per_dev_params [configNr] + .stats->tx_dropped) + * 123); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_DIRECTED_FRAMES_XMIT: + DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); + /* dito */ + if (rndis_per_dev_params [configNr].stats) { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + (rndis_per_dev_params [configNr] + .stats->tx_packets - + rndis_per_dev_params [configNr] + .stats->tx_errors - + rndis_per_dev_params [configNr] + .stats->tx_dropped) + / 123); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_MULTICAST_BYTES_XMIT: + DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast*1234); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_MULTICAST_FRAMES_XMIT: + DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_BYTES_XMIT: + DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->tx_packets/42*255); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_FRAMES_XMIT: + DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->tx_packets/42); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_DIRECTED_BYTES_RCV: + DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + case OID_GEN_DIRECTED_FRAMES_RCV: + DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + case OID_GEN_MULTICAST_BYTES_RCV: + DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast * 1111); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_MULTICAST_FRAMES_RCV: + DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->multicast); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_BYTES_RCV: + DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_packets/42*255); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_BROADCAST_FRAMES_RCV: + DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_packets/42); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_RCV_CRC_ERROR: + DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) { + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_crc_errors); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + case OID_GEN_TRANSMIT_QUEUE_LENGTH: + DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; +#endif /* RNDIS_OPTIONAL_STATS */ + + /* ieee802.3 OIDs (table 4-3) */ + + /* mandatory */ + case OID_802_3_PERMANENT_ADDRESS: + DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = ETH_ALEN; + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].host_mac, + length); + retval = 0; + } else { + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_CURRENT_ADDRESS: + DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].dev) { + length = ETH_ALEN; + memcpy ((u8 *) resp + 24, + rndis_per_dev_params [configNr].host_mac, + length); + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_MULTICAST_LIST: + DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + length = 4; + /* Multicast base address only */ + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0xE0000000); + retval = 0; + break; + + /* mandatory */ + case OID_802_3_MAXIMUM_LIST_SIZE: + DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); + length = 4; + /* Multicast base address only */ + *((u32 *) resp + 6) = __constant_cpu_to_le32 (1); + retval = 0; + break; + + case OID_802_3_MAC_OPTIONS: + DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); + break; + + /* ieee802.3 statistics OIDs (table 4-4) */ + + /* mandatory */ + case OID_802_3_RCV_ERROR_ALIGNMENT: + DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); + if (rndis_per_dev_params [configNr].stats) + { + length = 4; + *((u32 *) resp + 6) = cpu_to_le32 ( + rndis_per_dev_params [configNr] + .stats->rx_frame_errors); + retval = 0; + } + break; + + /* mandatory */ + case OID_802_3_XMIT_ONE_COLLISION: + DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + + /* mandatory */ + case OID_802_3_XMIT_MORE_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); + length = 4; + *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); + retval = 0; + break; + +#ifdef RNDIS_OPTIONAL_STATS + case OID_802_3_XMIT_DEFERRED: + DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_MAX_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_RCV_OVERRUN: + DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_UNDERRUN: + DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_HEARTBEAT_FAILURE: + DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_TIMES_CRS_LOST: + DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); + /* TODO */ + break; + + case OID_802_3_XMIT_LATE_COLLISIONS: + DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); + /* TODO */ + break; +#endif /* RNDIS_OPTIONAL_STATS */ + +#ifdef RNDIS_PM + /* power management OIDs (table 4-5) */ + case OID_PNP_CAPABILITIES: + DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); + + /* just PM, and remote wakeup on link status change + * (not magic packet or pattern match) + */ + length = sizeof (struct NDIS_PNP_CAPABILITIES); + memset (resp, 0, length); + { + struct NDIS_PNP_CAPABILITIES *caps = (void *) resp; + + caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE; + caps->WakeUpCapabilities.MinLinkChangeWakeUp + = NdisDeviceStateD3; + + /* FIXME then use usb_gadget_wakeup(), and + * set USB_CONFIG_ATT_WAKEUP in config desc + */ + } + retval = 0; + break; + case OID_PNP_QUERY_POWER: + DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__); + /* sure, handle any power state that maps to USB suspend */ + retval = 0; + break; +#endif + + default: + printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", + __FUNCTION__, OID); + } + + resp->InformationBufferOffset = __constant_cpu_to_le32 (16); + resp->InformationBufferLength = cpu_to_le32 (length); + resp->MessageLength = cpu_to_le32 (24 + length); + r->length = 24 + length; + return retval; +} + +static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, + rndis_resp_t *r) +{ + rndis_set_cmplt_type *resp; + int i, retval = -ENOTSUPP; + struct rndis_params *params; + + if (!r) + return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + if (!resp) + return -ENOMEM; + + DEBUG("set OID %08x value, len %d:\n", OID, buf_len); + for (i = 0; i < buf_len; i += 16) { + DEBUG ("%03d: " + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + "\n", + i, + buf[i], buf [i+1], + buf[i+2], buf[i+3], + buf[i+4], buf [i+5], + buf[i+6], buf[i+7], + buf[i+8], buf [i+9], + buf[i+10], buf[i+11], + buf[i+12], buf [i+13], + buf[i+14], buf[i+15]); + } + + switch (OID) { + case OID_GEN_CURRENT_PACKET_FILTER: + params = &rndis_per_dev_params [configNr]; + retval = 0; + + /* FIXME use these NDIS_PACKET_TYPE_* bitflags to + * filter packets in hard_start_xmit() + * NDIS_PACKET_TYPE_x == CDC_PACKET_TYPE_x for x in: + * PROMISCUOUS, DIRECTED, + * MULTICAST, ALL_MULTICAST, BROADCAST + */ + params->filter = cpu_to_le32p((u32 *)buf); + DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", + __FUNCTION__, params->filter); + + /* this call has a significant side effect: it's + * what makes the packet flow start and stop, like + * activating the CDC Ethernet altsetting. + */ + if (params->filter) { + params->state = RNDIS_DATA_INITIALIZED; + netif_carrier_on(params->dev); + if (netif_running(params->dev)) + netif_wake_queue (params->dev); + } else { + params->state = RNDIS_INITIALIZED; + netif_carrier_off (params->dev); + netif_stop_queue (params->dev); + } + break; + + case OID_802_3_MULTICAST_LIST: + /* I think we can ignore this */ + DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); + retval = 0; + break; +#if 0 + case OID_GEN_RNDIS_CONFIG_PARAMETER: + { + struct rndis_config_parameter *param; + param = (struct rndis_config_parameter *) buf; + DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", + __FUNCTION__, + min(cpu_to_le32(param->ParameterNameLength),80), + buf + param->ParameterNameOffset); + retval = 0; + } + break; +#endif + +#ifdef RNDIS_PM + case OID_PNP_SET_POWER: + DEBUG ("OID_PNP_SET_POWER\n"); + /* sure, handle any power state that maps to USB suspend */ + retval = 0; + break; + + case OID_PNP_ENABLE_WAKE_UP: + /* always-connected ... */ + DEBUG ("OID_PNP_ENABLE_WAKE_UP\n"); + retval = 0; + break; + + // no PM resume patterns supported (specified where?) + // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails +#endif + + default: + printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", + __FUNCTION__, OID, buf_len); + } + + return retval; +} + +/* + * Response Functions + */ + +static int rndis_init_response (int configNr, rndis_init_msg_type *buf) +{ + rndis_init_cmplt_type *resp; + rndis_resp_t *r; + + if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + + r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); + + if (!r) return -ENOMEM; + + resp = (rndis_init_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + resp->MessageType = __constant_cpu_to_le32 ( + REMOTE_NDIS_INITIALIZE_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (52); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION); + resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION); + resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); + resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); + resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); + resp->MaxTransferSize = cpu_to_le32 ( + rndis_per_dev_params [configNr].dev->mtu + + sizeof (struct ethhdr) + + sizeof (struct rndis_packet_msg_type) + + 22); + resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0); + resp->AFListOffset = __constant_cpu_to_le32 (0); + resp->AFListSize = __constant_cpu_to_le32 (0); + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + +static int rndis_query_response (int configNr, rndis_query_msg_type *buf) +{ + rndis_query_cmplt_type *resp; + rndis_resp_t *r; + + // DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); + if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; + + /* + * we need more memory: + * oid_supported_list is the largest answer + */ + r = rndis_add_response (configNr, sizeof (oid_supported_list)); + + if (!r) return -ENOMEM; + resp = (rndis_query_cmplt_type *) r->buf; + + if (!resp) return -ENOMEM; + + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (24); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + + if (gen_ndis_query_resp (configNr, cpu_to_le32 (buf->OID), r)) { + /* OID not supported */ + resp->Status = __constant_cpu_to_le32 ( + RNDIS_STATUS_NOT_SUPPORTED); + resp->InformationBufferLength = __constant_cpu_to_le32 (0); + resp->InformationBufferOffset = __constant_cpu_to_le32 (0); + } else + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + return 0; +} + +static int rndis_set_response (int configNr, rndis_set_msg_type *buf) +{ + u32 BufLength, BufOffset; + rndis_set_cmplt_type *resp; + rndis_resp_t *r; + + r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); + + if (!r) return -ENOMEM; + resp = (rndis_set_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + BufLength = cpu_to_le32 (buf->InformationBufferLength); + BufOffset = cpu_to_le32 (buf->InformationBufferOffset); + +#ifdef VERBOSE + DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength); + DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset); + DEBUG("%s: InfoBuffer: ", __FUNCTION__); + + for (i = 0; i < BufLength; i++) { + DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); + } + + DEBUG ("\n"); +#endif + + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (16); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + if (gen_ndis_set_resp (configNr, cpu_to_le32 (buf->OID), + ((u8 *) buf) + 8 + BufOffset, BufLength, r)) + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); + else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + +static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) +{ + rndis_reset_cmplt_type *resp; + rndis_resp_t *r; + + r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); + + if (!r) return -ENOMEM; + resp = (rndis_reset_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (16); + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + /* resent information */ + resp->AddressingReset = __constant_cpu_to_le32 (1); + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + +static int rndis_keepalive_response (int configNr, + rndis_keepalive_msg_type *buf) +{ + rndis_keepalive_cmplt_type *resp; + rndis_resp_t *r; + + /* host "should" check only in RNDIS_DATA_INITIALIZED state */ + + r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); + resp = (rndis_keepalive_cmplt_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = __constant_cpu_to_le32 ( + REMOTE_NDIS_KEEPALIVE_CMPLT); + resp->MessageLength = __constant_cpu_to_le32 (16); + resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ + resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + + return 0; +} + + +/* + * Device to Host Comunication + */ +static int rndis_indicate_status_msg (int configNr, u32 status) +{ + rndis_indicate_status_msg_type *resp; + rndis_resp_t *r; + + if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) + return -ENOTSUPP; + + r = rndis_add_response (configNr, + sizeof (rndis_indicate_status_msg_type)); + if (!r) return -ENOMEM; + + resp = (rndis_indicate_status_msg_type *) r->buf; + if (!resp) return -ENOMEM; + + resp->MessageType = __constant_cpu_to_le32 ( + REMOTE_NDIS_INDICATE_STATUS_MSG); + resp->MessageLength = __constant_cpu_to_le32 (20); + resp->Status = cpu_to_le32 (status); + resp->StatusBufferLength = __constant_cpu_to_le32 (0); + resp->StatusBufferOffset = __constant_cpu_to_le32 (0); + + if (rndis_per_dev_params [configNr].ack) + rndis_per_dev_params [configNr].ack ( + rndis_per_dev_params [configNr].dev); + return 0; +} + +int rndis_signal_connect (int configNr) +{ + rndis_per_dev_params [configNr].media_state + = NDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg (configNr, + RNDIS_STATUS_MEDIA_CONNECT); +} + +int rndis_signal_disconnect (int configNr) +{ + rndis_per_dev_params [configNr].media_state + = NDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg (configNr, + RNDIS_STATUS_MEDIA_DISCONNECT); +} + +void rndis_set_host_mac (int configNr, const u8 *addr) +{ + rndis_per_dev_params [configNr].host_mac = addr; +} + +/* + * Message Parser + */ +int rndis_msg_parser (u8 configNr, u8 *buf) +{ + u32 MsgType, MsgLength, *tmp; + struct rndis_params *params; + + if (!buf) + return -ENOMEM; + + tmp = (u32 *) buf; + MsgType = cpu_to_le32p(tmp++); + MsgLength = cpu_to_le32p(tmp++); + + if (configNr >= RNDIS_MAX_CONFIGS) + return -ENOTSUPP; + params = &rndis_per_dev_params [configNr]; + + /* For USB: responses may take up to 10 seconds */ + switch (MsgType) + { + case REMOTE_NDIS_INITIALIZE_MSG: + DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", + __FUNCTION__ ); + params->state = RNDIS_INITIALIZED; + return rndis_init_response (configNr, + (rndis_init_msg_type *) buf); + + case REMOTE_NDIS_HALT_MSG: + DEBUG("%s: REMOTE_NDIS_HALT_MSG\n", + __FUNCTION__ ); + params->state = RNDIS_UNINITIALIZED; + if (params->dev) { + netif_carrier_off (params->dev); + netif_stop_queue (params->dev); + } + return 0; + + case REMOTE_NDIS_QUERY_MSG: + return rndis_query_response (configNr, + (rndis_query_msg_type *) buf); + + case REMOTE_NDIS_SET_MSG: + return rndis_set_response (configNr, + (rndis_set_msg_type *) buf); + + case REMOTE_NDIS_RESET_MSG: + DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", + __FUNCTION__ ); + return rndis_reset_response (configNr, + (rndis_reset_msg_type *) buf); + + case REMOTE_NDIS_KEEPALIVE_MSG: + /* For USB: host does this every 5 seconds */ +#ifdef VERBOSE + DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", + __FUNCTION__ ); +#endif + return rndis_keepalive_response (configNr, + (rndis_keepalive_msg_type *) + buf); + + default: + /* At least Windows XP emits some undefined RNDIS messages. + * In one case those messages seemed to relate to the host + * suspending itself. + */ + printk (KERN_WARNING + "%s: unknown RNDIS message 0x%08X len %d\n", + __FUNCTION__ , MsgType, MsgLength); + { + unsigned i; + for (i = 0; i < MsgLength; i += 16) { + DEBUG ("%03d: " + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + " %02x %02x %02x %02x" + "\n", + i, + buf[i], buf [i+1], + buf[i+2], buf[i+3], + buf[i+4], buf [i+5], + buf[i+6], buf[i+7], + buf[i+8], buf [i+9], + buf[i+10], buf[i+11], + buf[i+12], buf [i+13], + buf[i+14], buf[i+15]); + } + } + break; + } + + return -ENOTSUPP; +} + +int rndis_register (int (* rndis_control_ack) (struct net_device *)) +{ + u8 i; + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + if (!rndis_per_dev_params [i].used) { + rndis_per_dev_params [i].used = 1; + rndis_per_dev_params [i].ack = rndis_control_ack; + DEBUG("%s: configNr = %d\n", __FUNCTION__, i); + return i; + } + } + DEBUG("failed\n"); + + return -1; +} + +void rndis_deregister (int configNr) +{ + DEBUG("%s: \n", __FUNCTION__ ); + + if (configNr >= RNDIS_MAX_CONFIGS) return; + rndis_per_dev_params [configNr].used = 0; + + return; +} + +int rndis_set_param_dev (u8 configNr, struct net_device *dev, + struct net_device_stats *stats) +{ + DEBUG("%s:\n", __FUNCTION__ ); + if (!dev || !stats) return -1; + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].dev = dev; + rndis_per_dev_params [configNr].stats = stats; + + return 0; +} + +int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) +{ + DEBUG("%s:\n", __FUNCTION__ ); + if (!vendorDescr) return -1; + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].vendorID = vendorID; + rndis_per_dev_params [configNr].vendorDescr = vendorDescr; + + return 0; +} + +int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) +{ + DEBUG("%s:\n", __FUNCTION__ ); + if (configNr >= RNDIS_MAX_CONFIGS) return -1; + + rndis_per_dev_params [configNr].medium = medium; + rndis_per_dev_params [configNr].speed = speed; + + return 0; +} + +void rndis_add_hdr (struct sk_buff *skb) +{ + if (!skb) return; + skb_push (skb, sizeof (struct rndis_packet_msg_type)); + memset (skb->data, 0, sizeof (struct rndis_packet_msg_type)); + *((u32 *) skb->data) = __constant_cpu_to_le32 (1); + *((u32 *) skb->data + 1) = cpu_to_le32(skb->len); + *((u32 *) skb->data + 2) = __constant_cpu_to_le32 (36); + *((u32 *) skb->data + 3) = cpu_to_le32(skb->len - 44); + + return; +} + +void rndis_free_response (int configNr, u8 *buf) +{ + rndis_resp_t *r; + struct list_head *act, *tmp; + + list_for_each_safe (act, tmp, + &(rndis_per_dev_params [configNr].resp_queue)) + { + r = list_entry (act, rndis_resp_t, list); + if (r && r->buf == buf) { + list_del (&r->list); + kfree (r); + } + } +} + +u8 *rndis_get_next_response (int configNr, u32 *length) +{ + rndis_resp_t *r; + struct list_head *act, *tmp; + + if (!length) return NULL; + + list_for_each_safe (act, tmp, + &(rndis_per_dev_params [configNr].resp_queue)) + { + r = list_entry (act, rndis_resp_t, list); + if (!r->send) { + r->send = 1; + *length = r->length; + return r->buf; + } + } + + return NULL; +} + +static rndis_resp_t *rndis_add_response (int configNr, u32 length) +{ + rndis_resp_t *r; + + r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); + if (!r) return NULL; + + r->buf = (u8 *) (r + 1); + r->length = length; + r->send = 0; + + list_add_tail (&r->list, + &(rndis_per_dev_params [configNr].resp_queue)); + return r; +} + +int rndis_rm_hdr (u8 *buf, u32 *length) +{ + u32 i, messageLen, dataOffset, *tmp; + + tmp = (u32 *) buf; + + if (!buf || !length) return -1; + if (cpu_to_le32p(tmp++) != 1) return -1; + + messageLen = cpu_to_le32p(tmp++); + dataOffset = cpu_to_le32p(tmp++) + 8; + + if (messageLen < dataOffset || messageLen > *length) return -1; + + for (i = dataOffset; i < messageLen; i++) + buf [i - dataOffset] = buf [i]; + + *length = messageLen - dataOffset; + + return 0; +} + +int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + char *out = page; + int len; + rndis_params *param = (rndis_params *) data; + + out += snprintf (out, count, + "Config Nr. %d\n" + "used : %s\n" + "state : %s\n" + "medium : 0x%08X\n" + "speed : %d\n" + "cable : %s\n" + "vendor ID : 0x%08X\n" + "vendor : %s\n", + param->confignr, (param->used) ? "y" : "n", + ({ char *s = "?"; + switch (param->state) { + case RNDIS_UNINITIALIZED: + s = "RNDIS_UNINITIALIZED"; break; + case RNDIS_INITIALIZED: + s = "RNDIS_INITIALIZED"; break; + case RNDIS_DATA_INITIALIZED: + s = "RNDIS_DATA_INITIALIZED"; break; + }; s; }), + param->medium, + (param->media_state) ? 0 : param->speed*100, + (param->media_state) ? "disconnected" : "connected", + param->vendorID, param->vendorDescr); + + len = out - page; + len -= off; + + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + + *start = page + off; + return len; +} + +int rndis_proc_write (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + u32 speed = 0; + int i, fl_speed = 0; + + for (i = 0; i < count; i++) { + switch (*buffer) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + fl_speed = 1; + speed = speed*10 + *buffer - '0'; + break; + case 'C': + case 'c': + rndis_signal_connect (((rndis_params *) data) + ->confignr); + break; + case 'D': + case 'd': + rndis_signal_disconnect (((rndis_params *) data) + ->confignr); + break; + default: + if (fl_speed) ((rndis_params *) data)->speed = speed; + else DEBUG ("%c is not valid\n", *buffer); + break; + } + + buffer++; + } + + return count; +} + +int __init rndis_init (void) +{ + u8 i; + char name [4]; + + /* FIXME this should probably be /proc/driver/rndis, + * and only if debugging is enabled + */ + + if (!(rndis_connect_dir = proc_mkdir ("rndis", NULL))) { + printk (KERN_ERR "%s: couldn't create /proc/rndis entry", + __FUNCTION__); + return -EIO; + } + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + sprintf (name, "%03d", i); + if (!(rndis_connect_state [i] + = create_proc_entry (name, 0660, + rndis_connect_dir))) + { + DEBUG ("%s :remove entries", __FUNCTION__); + for (i--; i > 0; i--) { + sprintf (name, "%03d", i); + remove_proc_entry (name, rndis_connect_dir); + } + DEBUG ("\n"); + + remove_proc_entry ("000", rndis_connect_dir); + remove_proc_entry ("rndis", NULL); + return -EIO; + } + rndis_connect_state [i]->nlink = 1; + rndis_connect_state [i]->write_proc = rndis_proc_write; + rndis_connect_state [i]->read_proc = rndis_proc_read; + rndis_connect_state [i]->data = (void *) + (rndis_per_dev_params + i); + rndis_per_dev_params [i].confignr = i; + rndis_per_dev_params [i].used = 0; + rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; + rndis_per_dev_params [i].media_state + = NDIS_MEDIA_STATE_DISCONNECTED; + INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue)); + } + + return 0; +} + +void rndis_exit (void) +{ + u8 i; + char name [4]; + + for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { + sprintf (name, "%03d", i); + remove_proc_entry (name, rndis_connect_dir); + } + remove_proc_entry ("rndis", NULL); + return; +} + diff -urN linux-2.4.26/drivers/usb/gadget/rndis.h linux-2.4.27/drivers/usb/gadget/rndis.h --- linux-2.4.26/drivers/usb/gadget/rndis.h 1969-12-31 16:00:00.000000000 -0800 +++ linux-2.4.27/drivers/usb/gadget/rndis.h 2004-08-07 16:26:05.700389758 -0700 @@ -0,0 +1,348 @@ +/* + * RNDIS Definitions for Remote NDIS + * + * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ + * + * Authors: Benedikt Spranger, Pengutronix + * Robert Schwebel, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, as published by the Free Software Foundation. + * + * This software was originally developed in conformance with + * Microsoft's Remote NDIS Specification License Agreement. + */ + +#ifndef _LINUX_RNDIS_H +#define _LINUX_RNDIS_H + +#include "ndis.h" + +#define RNDIS_MAXIMUM_FRAME_SIZE 1518 +#define RNDIS_MAX_TOTAL_SIZE 1558 + +/* Remote NDIS Versions */ +#define RNDIS_MAJOR_VERSION 1 +#define RNDIS_MINOR_VERSION 0 + +/* Status Values */ +#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ +#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */ +#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */ +#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */ +#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */ +#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */ +/* For all not specified status messages: + * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx + */ + +/* Message Set for Connectionless (802.3) Devices */ +#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ +#define REMOTE_NDIS_HALT_MSG 0x00000003U +#define REMOTE_NDIS_QUERY_MSG 0x00000004U +#define REMOTE_NDIS_SET_MSG 0x00000005U +#define REMOTE_NDIS_RESET_MSG 0x00000006U +#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U +#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U + +/* Message completion */ +#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U +#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U +#define REMOTE_NDIS_SET_CMPLT 0x80000005U +#define REMOTE_NDIS_RESET_CMPLT 0x80000006U +#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U + +/* Device Flags */ +#define RNDIS_DF_CONNECTIONLESS 0x00000001U +#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U + +#define RNDIS_MEDIUM_802_3 0x00000000U + +/* from drivers/net/sk98lin/h/skgepnmi.h */ +#define OID_PNP_CAPABILITIES 0xFD010100 +#define OID_PNP_SET_POWER 0xFD010101 +#define OID_PNP_QUERY_POWER 0xFD010102 +#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 +#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 +#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 + + +/* supported OIDs */ +static const u32 oid_supported_list [] = +{ + /* the general stuff */ + OID_GEN_SUPPORTED_LIST, + OID_GEN_HARDWARE_STATUS, + OID_GEN_MEDIA_SUPPORTED, + OID_GEN_MEDIA_IN_USE, + OID_GEN_MAXIMUM_FRAME_SIZE, + OID_GEN_LINK_SPEED, + OID_GEN_TRANSMIT_BLOCK_SIZE, + OID_GEN_RECEIVE_BLOCK_SIZE, + OID_GEN_VENDOR_ID, + OID_GEN_VENDOR_DESCRIPTION, + OID_GEN_VENDOR_DRIVER_VERSION, + OID_GEN_CURRENT_PACKET_FILTER, + OID_GEN_MAXIMUM_TOTAL_SIZE, + OID_GEN_MEDIA_CONNECT_STATUS, + OID_GEN_PHYSICAL_MEDIUM, +#if 0 + OID_GEN_RNDIS_CONFIG_PARAMETER, +#endif + + /* the statistical stuff */ + OID_GEN_XMIT_OK, + OID_GEN_RCV_OK, + OID_GEN_XMIT_ERROR, + OID_GEN_RCV_ERROR, + OID_GEN_RCV_NO_BUFFER, +#ifdef RNDIS_OPTIONAL_STATS + OID_GEN_DIRECTED_BYTES_XMIT, + OID_GEN_DIRECTED_FRAMES_XMIT, + OID_GEN_MULTICAST_BYTES_XMIT, + OID_GEN_MULTICAST_FRAMES_XMIT, + OID_GEN_BROADCAST_BYTES_XMIT, + OID_GEN_BROADCAST_FRAMES_XMIT, + OID_GEN_DIRECTED_BYTES_RCV, + OID_GEN_DIRECTED_FRAMES_RCV, + OID_GEN_MULTICAST_BYTES_RCV, + OID_GEN_MULTICAST_FRAMES_RCV, + OID_GEN_BROADCAST_BYTES_RCV, + OID_GEN_BROADCAST_FRAMES_RCV, + OID_GEN_RCV_CRC_ERROR, + OID_GEN_TRANSMIT_QUEUE_LENGTH, +#endif /* RNDIS_OPTIONAL_STATS */ + + /* mandatory 802.3 */ + /* the general stuff */ + OID_802_3_PERMANENT_ADDRESS, + OID_802_3_CURRENT_ADDRESS, + OID_802_3_MULTICAST_LIST, + OID_802_3_MAC_OPTIONS, + OID_802_3_MAXIMUM_LIST_SIZE, + + /* the statistical stuff */ + OID_802_3_RCV_ERROR_ALIGNMENT, + OID_802_3_XMIT_ONE_COLLISION, + OID_802_3_XMIT_MORE_COLLISIONS, +#ifdef RNDIS_OPTIONAL_STATS + OID_802_3_XMIT_DEFERRED, + OID_802_3_XMIT_MAX_COLLISIONS, + OID_802_3_RCV_OVERRUN, + OID_802_3_XMIT_UNDERRUN, + OID_802_3_XMIT_HEARTBEAT_FAILURE, + OID_802_3_XMIT_TIMES_CRS_LOST, + OID_802_3_XMIT_LATE_COLLISIONS, +#endif /* RNDIS_OPTIONAL_STATS */ + +#ifdef RNDIS_PM + /* PM and wakeup are mandatory for USB: */ + + /* power management */ + OID_PNP_CAPABILITIES, + OID_PNP_QUERY_POWER, + OID_PNP_SET_POWER, + + /* wake up host */ + OID_PNP_ENABLE_WAKE_UP, + OID_PNP_ADD_WAKE_UP_PATTERN, + OID_PNP_REMOVE_WAKE_UP_PATTERN, +#endif +}; + + +typedef struct rndis_init_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 MajorVersion; + u32 MinorVersion; + u32 MaxTransferSize; +} rndis_init_msg_type; + +typedef struct rndis_init_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; + u32 MajorVersion; + u32 MinorVersion; + u32 DeviceFlags; + u32 Medium; + u32 MaxPacketsPerTransfer; + u32 MaxTransferSize; + u32 PacketAlignmentFactor; + u32 AFListOffset; + u32 AFListSize; +} rndis_init_cmplt_type; + +typedef struct rndis_halt_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; +} rndis_halt_msg_type; + +typedef struct rndis_query_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 OID; + u32 InformationBufferLength; + u32 InformationBufferOffset; + u32 DeviceVcHandle; +} rndis_query_msg_type; + +typedef struct rndis_query_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; + u32 InformationBufferLength; + u32 InformationBufferOffset; +} rndis_query_cmplt_type; + +typedef struct rndis_set_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 OID; + u32 InformationBufferLength; + u32 InformationBufferOffset; + u32 DeviceVcHandle; +} rndis_set_msg_type; + +typedef struct rndis_set_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; +} rndis_set_cmplt_type; + +typedef struct rndis_reset_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 Reserved; +} rndis_reset_msg_type; + +typedef struct rndis_reset_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 Status; + u32 AddressingReset; +} rndis_reset_cmplt_type; + +typedef struct rndis_indicate_status_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 Status; + u32 StatusBufferLength; + u32 StatusBufferOffset; +} rndis_indicate_status_msg_type; + +typedef struct rndis_keepalive_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; +} rndis_keepalive_msg_type; + +typedef struct rndis_keepalive_cmplt_type +{ + u32 MessageType; + u32 MessageLength; + u32 RequestID; + u32 Status; +} rndis_keepalive_cmplt_type; + +struct rndis_packet_msg_type +{ + u32 MessageType; + u32 MessageLength; + u32 DataOffset; + u32 DataLength; + u32 OOBDataOffset; + u32 OOBDataLength; + u32 NumOOBDataElements; + u32 PerPacketInfoOffset; + u32 PerPacketInfoLength; + u32 VcHandle; + u32 Reserved; +}; + +struct rndis_config_parameter +{ + u32 ParameterNameOffset; + u32 ParameterNameLength; + u32 ParameterType; + u32 ParameterValueOffset; + u32 ParameterValueLength; +}; + +/* implementation specific */ +enum rndis_state +{ + RNDIS_UNINITIALIZED, + RNDIS_INITIALIZED, + RNDIS_DATA_INITIALIZED, +}; + +typedef struct rndis_resp_t +{ + struct list_head list; + u8 *buf; + u32 length; + int send; +} rndis_resp_t; + +typedef struct rndis_params +{ + u8 confignr; + int used; + enum rndis_state state; + u32 filter; + u32 medium; + u32 speed; + u32 media_state; + const u8 *host_mac; + struct net_device *dev; + struct net_device_stats *stats; + u32 vendorID; + const char *vendorDescr; + int (*ack) (struct net_device *); + struct list_head resp_queue; +} rndis_params; + +/* RNDIS Message parser and other useless functions */ +int rndis_msg_parser (u8 configNr, u8 *buf); +int rndis_register (int (*rndis_control_ack) (struct net_device *)); +void rndis_deregister (int configNr); +int rndis_set_param_dev (u8 configNr, struct net_device *dev, + struct net_device_stats *stats); +int rndis_set_param_vendor (u8 configNr, u32 vendorID, + const char *vendorDescr); +int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); +void rndis_add_hdr (struct sk_buff *skb); +int rndis_rm_hdr (u8 *buf, u32 *length); +u8 *rndis_get_next_response (int configNr, u32 *length); +void rndis_free_response (int configNr, u8 *buf); + +int rndis_signal_connect (int configNr); +int rndis_signal_disconnect (int configNr); +int rndis_state (int configNr); +extern void rndis_set_host_mac (int configNr, const u8 *addr); + +int __init rndis_init (void); +void rndis_exit (void); + +#endif /* _LINUX_RNDIS_H */ diff -urN linux-2.4.26/drivers/usb/gadget/usbstring.c linux-2.4.27/drivers/usb/gadget/usbstring.c --- linux-2.4.26/drivers/usb/gadget/usbstring.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/usbstring.c 2004-08-07 16:26:05.701389799 -0700 @@ -11,11 +11,12 @@ #include #include #include -#include +#include #include #include +#include #include diff -urN linux-2.4.26/drivers/usb/gadget/zero.c linux-2.4.27/drivers/usb/gadget/zero.c --- linux-2.4.26/drivers/usb/gadget/zero.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/gadget/zero.c 2004-08-07 16:26:05.703389881 -0700 @@ -87,10 +87,12 @@ #include #include +#include "gadget_chips.h" + /*-------------------------------------------------------------------------*/ -#define DRIVER_VERSION "Bastille Day 2003" +#define DRIVER_VERSION "St Patrick's Day 2004" static const char shortname [] = "zero"; static const char longname [] = "Gadget Zero"; @@ -103,100 +105,12 @@ /* * driver assumes self-powered hardware, and * has no way for users to trigger remote wakeup. - */ - -/* - * hardware-specific configuration, controlled by which device - * controller driver was configured. - * - * CHIP ... hardware identifier - * DRIVER_VERSION_NUM ... alerts the host side driver to differences - * EP_*_NAME ... which endpoints do we use for which purpose? - * EP_*_NUM ... numbers for them (often limited by hardware) * - * add other defines for other portability issues, like hardware that - * for some reason doesn't handle full speed bulk maxpacket of 64. + * this version autoconfigures as much as possible, + * which is reasonable for most "bulk-only" drivers. */ - -/* - * DRIVER_VERSION_NUM 0x0000 (?): Martin Diehl's ezusb an21/fx code - */ - -/* - * NetChip 2280, PCI based. - * - * This has half a dozen configurable endpoints, four with dedicated - * DMA channels to manage their FIFOs. It supports high speed. - * Those endpoints can be arranged in any desired configuration. - */ -#ifdef CONFIG_USB_GADGET_NET2280 -#define CHIP "net2280" -#define DRIVER_VERSION_NUM 0x0111 -static const char EP_OUT_NAME [] = "ep-a"; -#define EP_OUT_NUM 2 -static const char EP_IN_NAME [] = "ep-b"; -#define EP_IN_NUM 2 -#endif - -/* - * PXA-2xx UDC: widely used in second gen Linux-capable PDAs. - * - * This has fifteen fixed-function full speed endpoints, and it - * can support all USB transfer types. - * - * These supports three or four configurations, with fixed numbers. - * The hardware interprets SET_INTERFACE, net effect is that you - * can't use altsettings or reset the interfaces independently. - * So stick to a single interface. - */ -#ifdef CONFIG_USB_GADGET_PXA2XX -#define CHIP "pxa2xx" -#define DRIVER_VERSION_NUM 0x0113 -static const char EP_OUT_NAME [] = "ep12out-bulk"; -#define EP_OUT_NUM 12 -static const char EP_IN_NAME [] = "ep11in-bulk"; -#define EP_IN_NUM 11 -#endif - -/* - * SA-1100 UDC: widely used in first gen Linux-capable PDAs. - * - * This has only two fixed function endpoints, which can only - * be used for bulk (or interrupt) transfers. (Plus control.) - * - * Since it can't flush its TX fifos without disabling the UDC, - * the current configuration or altsettings can't change except - * in special situations. So this is a case of "choose it right - * during enumeration" ... - */ -#ifdef CONFIG_USB_GADGET_SA1100 -#define CHIP "sa1100" -#define DRIVER_VERSION_NUM 0x0115 -static const char EP_OUT_NAME [] = "ep1out-bulk"; -#define EP_OUT_NUM 1 -static const char EP_IN_NAME [] = "ep2in-bulk"; -#define EP_IN_NUM 2 -#endif - -/* - * Toshiba TC86C001 ("Goku-S") UDC - * - * This has three semi-configurable full speed bulk/interrupt endpoints. - */ -#ifdef CONFIG_USB_GADGET_GOKU -#define CHIP "goku" -#define DRIVER_VERSION_NUM 0x0116 -static const char EP_OUT_NAME [] = "ep1-bulk"; -#define EP_OUT_NUM 1 -static const char EP_IN_NAME [] = "ep2-bulk"; -#define EP_IN_NUM 2 -#endif - -/*-------------------------------------------------------------------------*/ - -#ifndef EP_OUT_NUM -# error Configure some USB peripheral controller driver! -#endif +static const char *EP_IN_NAME; /* source */ +static const char *EP_OUT_NAME; /* sink */ /*-------------------------------------------------------------------------*/ @@ -214,6 +128,9 @@ */ u8 config; struct usb_ep *in_ep, *out_ep; + + /* autoresume timer */ + struct timer_list resume; }; #define xprintk(d,level,fmt,args...) \ @@ -221,20 +138,19 @@ ## args) #ifdef DEBUG -#undef DEBUG -#define DEBUG(dev,fmt,args...) \ +#define DBG(dev,fmt,args...) \ xprintk(dev , KERN_DEBUG , fmt , ## args) #else -#define DEBUG(dev,fmt,args...) \ +#define DBG(dev,fmt,args...) \ do { } while (0) #endif /* DEBUG */ #ifdef VERBOSE -#define VDEBUG DEBUG +#define VDBG DBG #else -#define VDEBUG(dev,fmt,args...) \ +#define VDBG(dev,fmt,args...) \ do { } while (0) -#endif /* DEBUG */ +#endif /* VERBOSE */ #define ERROR(dev,fmt,args...) \ xprintk(dev , KERN_ERR , fmt , ## args) @@ -270,6 +186,13 @@ MODULE_PARM (loopdefault, "b"); MODULE_PARM_DESC (loopdefault, "true to have default config be loopback"); +/* + * if it's nonzero, autoresume says how many seconds to wait + * before trying to wake up the host after suspend. + */ +static unsigned autoresume = 0; +MODULE_PARM (autoresume, "i"); + /*-------------------------------------------------------------------------*/ /* Thanks to NetChip Technologies for donating this product ID. @@ -310,14 +233,13 @@ .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM), .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM), - .bcdDevice = __constant_cpu_to_le16 (DRIVER_VERSION_NUM), .iManufacturer = STRING_MANUFACTURER, .iProduct = STRING_PRODUCT, .iSerialNumber = STRING_SERIAL, .bNumConfigurations = 2, }; -static const struct usb_config_descriptor +static struct usb_config_descriptor source_sink_config = { .bLength = sizeof source_sink_config, .bDescriptorType = USB_DT_CONFIG, @@ -330,7 +252,7 @@ .bMaxPower = 1, /* self-powered */ }; -static const struct usb_config_descriptor +static struct usb_config_descriptor loopback_config = { .bLength = sizeof loopback_config, .bDescriptorType = USB_DT_CONFIG, @@ -367,24 +289,22 @@ /* two full speed bulk endpoints; their use is config-dependent */ -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_source_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_IN_NUM | USB_DIR_IN, + .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (64), }; -static const struct usb_endpoint_descriptor +static struct usb_endpoint_descriptor fs_sink_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = EP_OUT_NUM, + .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, - .wMaxPacketSize = __constant_cpu_to_le16 (64), }; static const struct usb_descriptor_header *fs_source_sink_function [] = { @@ -648,7 +568,7 @@ case -ECONNABORTED: /* hardware forced ep reset */ case -ECONNRESET: /* request dequeued */ case -ESHUTDOWN: /* disconnect from host */ - VDEBUG (dev, "%s gone (%d), %d/%d\n", ep->name, status, + VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status, req->actual, req->length); if (ep == dev->out_ep) check_read_data (dev, ep, req); @@ -661,7 +581,7 @@ */ default: #if 1 - DEBUG (dev, "%s complete --> %d, %d/%d\n", ep->name, + DBG (dev, "%s complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); #endif case -EREMOTEIO: /* short read */ @@ -752,7 +672,7 @@ break; } if (result == 0) - DEBUG (dev, "buflen %d\n", buflen); + DBG (dev, "buflen %d\n", buflen); /* caller is responsible for cleanup on error */ return result; @@ -863,14 +783,14 @@ req->complete = loopback_complete; result = usb_ep_queue (ep, req, GFP_ATOMIC); if (result) - DEBUG (dev, "%s queue req --> %d\n", + DBG (dev, "%s queue req --> %d\n", ep->name, result); } else result = -ENOMEM; } } if (result == 0) - DEBUG (dev, "qlen %d, buflen %d\n", qlen, buflen); + DBG (dev, "qlen %d, buflen %d\n", qlen, buflen); /* caller is responsible for cleanup on error */ return result; @@ -883,7 +803,7 @@ if (dev->config == 0) return; - DEBUG (dev, "reset config\n"); + DBG (dev, "reset config\n"); /* just disable endpoints, forcing completion of pending i/o. * all our completion handlers free their requests in this case. @@ -918,13 +838,11 @@ if (number == dev->config) return 0; -#ifdef CONFIG_USB_GADGET_SA1100 - if (dev->config) { + if (gadget_is_sa1100 (gadget) && dev->config) { /* tx fifo is full, but we can't clear it...*/ INFO (dev, "can't change configurations\n"); return -ESPIPE; } -#endif zero_reset_config (dev); switch (number) { @@ -968,7 +886,7 @@ static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req) { if (req->status || req->actual != req->length) - DEBUG ((struct zero_dev *) ep->driver_data, + DBG ((struct zero_dev *) ep->driver_data, "setup complete --> %d, %d/%d\n", req->status, req->actual, req->length); } @@ -1116,7 +1034,7 @@ default: unknown: - VDEBUG (dev, + VDBG (dev, "unknown control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, ctrl->wIndex, ctrl->wLength); @@ -1127,7 +1045,7 @@ req->length = value; value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); if (value < 0) { - DEBUG (dev, "ep_queue --> %d\n", value); + DBG (dev, "ep_queue --> %d\n", value); req->status = 0; zero_setup_complete (gadget->ep0, req); } @@ -1157,6 +1075,19 @@ */ } +static void +zero_autoresume (unsigned long _dev) +{ + struct zero_dev *dev = (struct zero_dev *) _dev; + int status; + + /* normally the host would be woken up for something + * more significant than just a timer firing... + */ + status = usb_gadget_wakeup (dev->gadget); + DBG (dev, "wakeup --> %d\n", status); +} + /*-------------------------------------------------------------------------*/ static void @@ -1164,11 +1095,12 @@ { struct zero_dev *dev = get_gadget_data (gadget); - DEBUG (dev, "unbind\n"); + DBG (dev, "unbind\n"); /* we've already been disconnected ... no i/o is active */ if (dev->req) free_ep_req (gadget->ep0, dev->req); + del_timer_sync (&dev->resume); kfree (dev); set_gadget_data (gadget, 0); } @@ -1177,7 +1109,70 @@ zero_bind (struct usb_gadget *gadget) { struct zero_dev *dev; + struct usb_ep *ep; + + /* Bulk-only drivers like this one SHOULD be able to + * autoconfigure on any sane usb controller driver, + * but there may also be important quirks to address. + */ + usb_ep_autoconfig_reset (gadget); + ep = usb_ep_autoconfig (gadget, &fs_source_desc); + if (!ep) { +autoconf_fail: + printk (KERN_ERR "%s: can't autoconfigure on %s\n", + shortname, gadget->name); + return -ENODEV; + } + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim */ + + ep = usb_ep_autoconfig (gadget, &fs_sink_desc); + if (!ep) + goto autoconf_fail; + EP_OUT_NAME = ep->name; + ep->driver_data = ep; /* claim */ + + + /* + * DRIVER POLICY CHOICE: you may want to do this differently. + * One thing to avoid is reusing a bcdDevice revision code + * with different host-visible configurations or behavior + * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc + */ + if (gadget_is_net2280 (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201); + } else if (gadget_is_pxa (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203); +#if 0 + } else if (gadget_is_sh(gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204); + /* SH has only one configuration; see "loopdefault" */ + device_desc.bNumConfigurations = 1; + /* FIXME make 1 == default.bConfigurationValue */ +#endif + } else if (gadget_is_sa1100 (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205); + } else if (gadget_is_goku (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206); + } else if (gadget_is_mq11xx (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207); + } else if (gadget_is_omap (gadget)) { + device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); + } else { + /* gadget zero is so simple (for now, no altsettings) that + * it SHOULD NOT have problems with bulk-capable hardware. + * so warn about unrcognized controllers, don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ + printk (KERN_WARNING "%s: controller '%s' not recognized\n", + shortname, gadget->name); + device_desc.bcdDevice = __constant_cpu_to_le16 (0x9999); + } + + /* ok, we made sense of the hardware ... */ dev = kmalloc (sizeof *dev, SLAB_KERNEL); if (!dev) return -ENOMEM; @@ -1208,6 +1203,16 @@ hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; #endif + usb_gadget_set_selfpowered (gadget); + + init_timer (&dev->resume); + dev->resume.function = zero_autoresume; + dev->resume.data = (unsigned long) dev; + if (autoresume) { + source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + gadget->ep0->driver_data = dev; INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname); @@ -1227,6 +1232,33 @@ /*-------------------------------------------------------------------------*/ +static void +zero_suspend (struct usb_gadget *gadget) +{ + struct zero_dev *dev = get_gadget_data (gadget); + + if (gadget->speed == USB_SPEED_UNKNOWN) + return; + + if (autoresume) { + mod_timer (&dev->resume, jiffies + (HZ * autoresume)); + DBG (dev, "suspend, wakeup in %d seconds\n", autoresume); + } else + DBG (dev, "suspend\n"); +} + +static void +zero_resume (struct usb_gadget *gadget) +{ + struct zero_dev *dev = get_gadget_data (gadget); + + DBG (dev, "resume\n"); + del_timer (&dev->resume); +} + + +/*-------------------------------------------------------------------------*/ + static struct usb_gadget_driver zero_driver = { #ifdef CONFIG_USB_GADGET_DUALSPEED .speed = USB_SPEED_HIGH, @@ -1240,6 +1272,9 @@ .setup = zero_setup, .disconnect = zero_disconnect, + .suspend = zero_suspend, + .resume = zero_resume, + .driver = { .name = (char *) shortname, // .shutdown = ... diff -urN linux-2.4.26/drivers/usb/hiddev.c linux-2.4.27/drivers/usb/hiddev.c --- linux-2.4.26/drivers/usb/hiddev.c 2004-04-14 06:05:32.000000000 -0700 +++ linux-2.4.27/drivers/usb/hiddev.c 2004-08-07 16:26:05.704389922 -0700 @@ -433,7 +433,9 @@ dinfo.product = dev->descriptor.idProduct; dinfo.version = dev->descriptor.bcdDevice; dinfo.num_applications = hid->maxapplication; - return copy_to_user((void *) arg, &dinfo, sizeof(dinfo)); + if (copy_to_user((void *) arg, &dinfo, sizeof(dinfo))) + return -EFAULT; + return 0; case HIDIOCGFLAG: return put_user(list->flags, (int *) arg); @@ -522,7 +524,9 @@ rinfo.num_fields = report->maxfield; - return copy_to_user((void *) arg, &rinfo, sizeof(rinfo)); + if (copy_to_user((void *) arg, &rinfo, sizeof(rinfo))) + return -EFAULT; + return 0; case HIDIOCGFIELDINFO: if (copy_from_user(&finfo, (void *) arg, sizeof(finfo))) @@ -552,7 +556,9 @@ finfo.unit_exponent = field->unit_exponent; finfo.unit = field->unit; - return copy_to_user((void *) arg, &finfo, sizeof(finfo)); + if (copy_to_user((void *) arg, &finfo, sizeof(finfo))) + return -EFAULT; + return 0; case HIDIOCGUCODE: if (copy_from_user(uref, (void *) arg, sizeof(*uref))) @@ -572,7 +578,9 @@ uref->usage_code = field->usage[uref->usage_index].hid; - return copy_to_user((void *) arg, uref, sizeof(*uref)); + if (copy_to_user((void *) arg, uref, sizeof(*uref))) + return -EFAULT; + return 0; case HIDIOCGUSAGE: case HIDIOCSUSAGE: @@ -656,7 +664,9 @@ cinfo.usage = hid->collection[cinfo.index].usage; cinfo.level = hid->collection[cinfo.index].level; - return copy_to_user((void *) arg, &cinfo, sizeof(cinfo)); + if (copy_to_user((void *) arg, &cinfo, sizeof(cinfo))) + return -EFAULT; + return 0; default: diff -urN linux-2.4.26/drivers/usb/host/ehci-hcd.c linux-2.4.27/drivers/usb/host/ehci-hcd.c --- linux-2.4.26/drivers/usb/host/ehci-hcd.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/usb/host/ehci-hcd.c 2004-08-07 16:26:05.747391689 -0700 @@ -291,7 +291,7 @@ int msec = 500; /* request handoff to OS */ - cap &= 1 << 24; + cap |= 1 << 24; pci_write_config_dword (ehci->hcd.pdev, where, cap); /* and wait a while for it to happen */ diff -urN linux-2.4.26/drivers/usb/host/ehci-sched.c linux-2.4.27/drivers/usb/host/ehci-sched.c --- linux-2.4.26/drivers/usb/host/ehci-sched.c 2004-04-14 06:05:33.000000000 -0700 +++ linux-2.4.27/drivers/usb/host/ehci-sched.c 2004-08-07 16:26:05.794393620 -0700 @@ -402,7 +402,7 @@ qh->start = frame; /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= ~0xffff; + qh->hw_info2 &= ~__constant_cpu_to_le32(0xffff); qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; } else dbg ("reused previous qh %p schedule", qh); diff -urN linux-2.4.26/drivers/usb/host/uhci-debug.h linux-2.4.27/drivers/usb/host/uhci-debug.h --- linux-2.4.26/drivers/usb/host/uhci-debug.h 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/host/uhci-debug.h 2004-08-07 16:26:05.795393661 -0700 @@ -530,16 +530,14 @@ loff_t *ppos) { struct uhci_proc *up = file->private_data; - unsigned int pos; + loff_t n = *ppos; + unsigned int pos = n; unsigned int size; - pos = *ppos; size = up->size; - if (pos >= size) + if (pos != n || pos >= size) return 0; - if (nbytes >= size) - nbytes = size; - if (pos + nbytes > size) + if (nbytes > size - pos) nbytes = size - pos; if (!access_ok(VERIFY_WRITE, buf, nbytes)) @@ -548,7 +546,7 @@ if (copy_to_user(buf, up->data + pos, nbytes)) return -EFAULT; - *ppos += nbytes; + *ppos = pos + nbytes; return nbytes; } diff -urN linux-2.4.26/drivers/usb/printer.c linux-2.4.27/drivers/usb/printer.c --- linux-2.4.26/drivers/usb/printer.c 2004-04-14 06:05:33.000000000 -0700 +++ linux-2.4.27/drivers/usb/printer.c 2004-08-07 16:26:05.796393702 -0700 @@ -1,9 +1,9 @@ /* - * printer.c Version 0.11 + * printer.c Version 0.13 * * Copyright (c) 1999 Michael Gee * Copyright (c) 1999 Pavel Machek - * Copyright (c) 2000 Randy Dunlap + * Copyright (c) 2000 Randy Dunlap * Copyright (c) 2000 Vojtech Pavlik # Copyright (c) 2001 Pete Zaitcev # Copyright (c) 2001 David Paschal @@ -222,6 +222,7 @@ static int usblp_set_protocol(struct usblp *usblp, int protocol); static int usblp_cache_device_id_string(struct usblp *usblp); +static DECLARE_MUTEX(usblp_sem); /* locks the existence of usblp's. */ /* * Functions for usblp control messages. @@ -229,11 +230,21 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, int recip, int value, void *buf, int len) { - int retval = usb_control_msg(usblp->dev, + int retval; + int index = usblp->ifnum; + + /* High byte has the interface index. + Low byte has the alternate setting. + */ + if ((request == USBLP_REQ_GET_ID) && (type == USB_TYPE_CLASS)) { + index = (usblp->ifnum<<8)|usblp->protocol[usblp->current_protocol].alt_setting; + } + + retval = usb_control_msg(usblp->dev, dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), - request, type | dir | recip, value, usblp->ifnum, buf, len, USBLP_WRITE_TIMEOUT); - dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d len: %#x result: %d", - request, !!dir, recip, value, len, retval); + request, type | dir | recip, value, index, buf, len, USBLP_WRITE_TIMEOUT); + dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d", + request, !!dir, recip, value, index, len, retval); return retval < 0 ? retval : 0; } @@ -332,7 +343,7 @@ if (minor < 0 || minor >= USBLP_MINORS) return -ENODEV; - lock_kernel(); + down (&usblp_sem); usblp = usblp_table[minor]; retval = -ENODEV; @@ -374,7 +385,7 @@ } } out: - unlock_kernel(); + up (&usblp_sem); return retval; } @@ -404,15 +415,13 @@ { struct usblp *usblp = file->private_data; - down (&usblp->sem); - lock_kernel(); + down (&usblp_sem); usblp->used = 0; if (usblp->present) { usblp_unlink_urbs(usblp); - up(&usblp->sem); } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); - unlock_kernel(); + up (&usblp_sem); return 0; } @@ -748,6 +757,7 @@ usblp->minor, usblp->readurb->status); usblp->readurb->dev = usblp->dev; usblp->readcount = 0; + usblp->rcomplete = 0; if (usb_submit_urb(usblp->readurb) < 0) dbg("error submitting urb"); count = -EIO; @@ -1112,17 +1122,16 @@ BUG (); } + down (&usblp_sem); down (&usblp->sem); - lock_kernel(); usblp->present = 0; usblp_unlink_urbs(usblp); + up (&usblp->sem); if (!usblp->used) usblp_cleanup (usblp); - else /* cleanup later, on release */ - up (&usblp->sem); - unlock_kernel(); + up (&usblp_sem); } static struct usb_device_id usblp_ids [] = { diff -urN linux-2.4.26/drivers/usb/serial/ftdi_sio.c linux-2.4.27/drivers/usb/serial/ftdi_sio.c --- linux-2.4.26/drivers/usb/serial/ftdi_sio.c 2004-04-14 06:05:34.000000000 -0700 +++ linux-2.4.27/drivers/usb/serial/ftdi_sio.c 2004-08-07 16:26:05.798393784 -0700 @@ -17,6 +17,9 @@ * See http://ftdi-usb-sio.sourceforge.net for upto date testing info * and extra documentation * + * (10/Mar/2004) Jan Capek + * Added PID's for ICD-U20/ICD-U40 - incircuit PIC debuggers from CCS Inc. + * * (09/Feb/2004) Ian Abbott * Changed full name of USB-UIRT device to avoid "/" character. * Added FTDI's alternate PID (0x6006) for FT232/245 devices. @@ -428,6 +431,8 @@ { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0x400, 0xffff) }, { USB_DEVICE_VER(FTDI_VID, FTDI_ELV_UO100_PID, 0x400, 0xffff) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, { } /* Terminating entry */ }; @@ -526,6 +531,8 @@ { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) }, { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_UO100_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU20_0_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_CCSICDU40_1_PID) }, { } /* Terminating entry */ }; diff -urN linux-2.4.26/drivers/usb/serial/ftdi_sio.h linux-2.4.27/drivers/usb/serial/ftdi_sio.h --- linux-2.4.26/drivers/usb/serial/ftdi_sio.h 2004-04-14 06:05:34.000000000 -0700 +++ linux-2.4.27/drivers/usb/serial/ftdi_sio.h 2004-08-07 16:26:05.799393826 -0700 @@ -165,6 +165,11 @@ #define PROTEGO_SPECIAL_3 0xFC72 /* special/unknown device */ #define PROTEGO_SPECIAL_4 0xFC73 /* special/unknown device */ +/* CCS Inc. ICDU/ICDU40 product ID - the FT232BM is used in an in-circuit-debugger */ +/* unit for PIC16's/PIC18's */ +#define FTDI_CCSICDU20_0_PID 0xF9D0 +#define FTDI_CCSICDU40_1_PID 0xF9D1 + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ diff -urN linux-2.4.26/drivers/usb/serial/io_edgeport.c linux-2.4.27/drivers/usb/serial/io_edgeport.c --- linux-2.4.26/drivers/usb/serial/io_edgeport.c 2004-04-14 06:05:35.000000000 -0700 +++ linux-2.4.27/drivers/usb/serial/io_edgeport.c 2004-08-07 16:26:05.802393949 -0700 @@ -1913,6 +1913,7 @@ case TIOCGICOUNT: cnow = edge_port->icount; + memset(&icount, 0, sizeof(icount)); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; diff -urN linux-2.4.26/drivers/usb/serial/ipaq.c linux-2.4.27/drivers/usb/serial/ipaq.c --- linux-2.4.26/drivers/usb/serial/ipaq.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/serial/ipaq.c 2004-08-07 16:26:05.803393990 -0700 @@ -188,6 +188,7 @@ port->private = (void *)priv; priv->active = 0; priv->queue_len = 0; + priv->free_len = 0; INIT_LIST_HEAD(&priv->queue); INIT_LIST_HEAD(&priv->freelist); diff -urN linux-2.4.26/drivers/usb/serial/mct_u232.c linux-2.4.27/drivers/usb/serial/mct_u232.c --- linux-2.4.26/drivers/usb/serial/mct_u232.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/usb/serial/mct_u232.c 2004-08-07 16:26:05.805394072 -0700 @@ -169,7 +169,8 @@ struct mct_u232_private { - unsigned long control_state; /* Modem Line Setting (TIOCM) */ + spinlock_t lock; + unsigned int control_state; /* Modem Line Setting (TIOCM) */ unsigned char last_lcr; /* Line Control Register */ unsigned char last_lsr; /* Line Status Register */ unsigned char last_msr; /* Modem Status Register */ @@ -181,25 +182,49 @@ #define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ +/* + * Later day 2.6.0-test kernels have new baud rates like B230400 which + * we do not know how to support. We ignore them for the moment. + * XXX Rate-limit the error message, it's user triggerable. + */ static int mct_u232_calculate_baud_rate(struct usb_serial *serial, int value) { if (serial->dev->descriptor.idProduct == MCT_U232_SITECOM_PID || serial->dev->descriptor.idProduct == MCT_U232_BELKIN_F5U109_PID) { switch (value) { - case 300: return 0x01; - case 600: return 0x02; /* this one not tested */ - case 1200: return 0x03; - case 2400: return 0x04; - case 4800: return 0x06; - case 9600: return 0x08; - case 19200: return 0x09; - case 38400: return 0x0a; - case 57600: return 0x0b; - case 115200: return 0x0c; - default: return -1; /* normally not reached */ + case B300: return 0x01; + case B600: return 0x02; /* this one not tested */ + case B1200: return 0x03; + case B2400: return 0x04; + case B4800: return 0x06; + case B9600: return 0x08; + case B19200: return 0x09; + case B38400: return 0x0a; + case B57600: return 0x0b; + case B115200: return 0x0c; + default: + err("MCT USB-RS232: unsupported baudrate request 0x%x," + " using default of B9600", value); + return 0x08; + } + } else { + switch (value) { + case B300: value = 300; + case B600: value = 600; + case B1200: value = 1200; + case B2400: value = 2400; + case B4800: value = 4800; + case B9600: value = 9600; + case B19200: value = 19200; + case B38400: value = 38400; + case B57600: value = 57600; + case B115200: value = 115200; + default: + err("MCT USB-RS232: unsupported baudrate request 0x%x," + " using default of B9600", value); + value = 9600; } + return 115200/value; } - else - return MCT_U232_BAUD_RATE(value); } static int mct_u232_set_baud_rate(struct usb_serial *serial, int value) @@ -207,7 +232,9 @@ unsigned int divisor; int rc; unsigned char zero_byte = 0; + divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value)); + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCT_U232_SET_BAUD_RATE_REQUEST, MCT_U232_SET_REQUEST_TYPE, @@ -215,7 +242,7 @@ WDR_TIMEOUT); if (rc < 0) err("Set BAUD RATE %d failed (error = %d)", value, rc); - dbg("set_baud_rate: value: %d, divisor: 0x%x", value, divisor); + dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor); /* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which always sends two extra USB 'device request' messages after the @@ -263,7 +290,7 @@ } /* mct_u232_set_line_ctrl */ static int mct_u232_set_modem_ctrl(struct usb_serial *serial, - unsigned long control_state) + unsigned int control_state) { int rc; unsigned char mcr = MCT_U232_MCR_NONE; @@ -280,7 +307,7 @@ WDR_TIMEOUT); if (rc < 0) err("Set MODEM CTRL 0x%x failed (error = %d)", mcr, rc); - dbg("set_modem_ctrl: state=0x%lx ==> mcr=0x%x", control_state, mcr); + dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr); return rc; } /* mct_u232_set_modem_ctrl */ @@ -301,7 +328,7 @@ return rc; } /* mct_u232_get_modem_stat */ -static void mct_u232_msr_to_state(unsigned long *control_state, unsigned char msr) +static void mct_u232_msr_to_state(unsigned int *control_state, unsigned char msr) { /* Translate Control Line states */ if (msr & MCT_U232_MSR_DSR) @@ -320,7 +347,7 @@ *control_state |= TIOCM_CD; else *control_state &= ~TIOCM_CD; - dbg("msr_to_state: msr=0x%x ==> state=0x%lx", msr, *control_state); + dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state); } /* mct_u232_msr_to_state */ /* @@ -330,20 +357,32 @@ static int mct_u232_startup (struct usb_serial *serial) { struct mct_u232_private *priv; + struct usb_serial_port *port, *rport; /* allocate the private data structure */ - serial->port->private = kmalloc(sizeof(struct mct_u232_private), - GFP_KERNEL); - if (!serial->port->private) - return (-1); /* error */ - priv = (struct mct_u232_private *)serial->port->private; + priv = kmalloc(sizeof(struct mct_u232_private), GFP_KERNEL); + if (!priv) + return -ENOMEM; /* set initial values for control structures */ + spin_lock_init(&priv->lock); priv->control_state = 0; priv->last_lsr = 0; priv->last_msr = 0; - + serial->port->private = priv; + init_waitqueue_head(&serial->port->write_wait); - + + /* Puh, that's dirty */ + port = &serial->port[0]; + rport = &serial->port[1]; + if (port->read_urb) { + /* No unlinking, it wasn't submitted yet. */ + usb_free_urb(port->read_urb); + } + port->read_urb = rport->interrupt_in_urb; + rport->interrupt_in_urb = NULL; + port->read_urb->context = port; + return (0); } /* mct_u232_startup */ @@ -354,7 +393,6 @@ dbg("%s", __FUNCTION__); - /* stop reads and writes on all ports */ for (i=0; i < serial->num_ports; ++i) { /* My special items, the standard routines free my urbs */ if (serial->port[i].private) @@ -367,6 +405,10 @@ struct usb_serial *serial = port->serial; struct mct_u232_private *priv = (struct mct_u232_private *)port->private; int retval = 0; + unsigned int control_state; + unsigned long flags; + unsigned char last_lcr; + unsigned char last_msr; dbg("%s port %d", __FUNCTION__, port->number); @@ -383,29 +425,27 @@ * sure if this is really necessary. But it should not harm * either. */ + spin_lock_irqsave(&priv->lock, flags); if (port->tty->termios->c_cflag & CBAUD) priv->control_state = TIOCM_DTR | TIOCM_RTS; else priv->control_state = 0; - mct_u232_set_modem_ctrl(serial, priv->control_state); priv->last_lcr = (MCT_U232_DATA_BITS_8 | MCT_U232_PARITY_NONE | MCT_U232_STOP_BITS_1); - mct_u232_set_line_ctrl(serial, priv->last_lcr); + control_state = priv->control_state; + last_lcr = priv->last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); + mct_u232_set_modem_ctrl(serial, control_state); + mct_u232_set_line_ctrl(serial, last_lcr); /* Read modem status and update control state */ - mct_u232_get_modem_stat(serial, &priv->last_msr); + mct_u232_get_modem_stat(serial, &last_msr); + spin_lock_irqsave(&priv->lock, flags); + priv->last_msr = last_msr; mct_u232_msr_to_state(&priv->control_state, priv->last_msr); - - { - /* Puh, that's dirty */ - struct usb_serial_port *rport; - rport = &serial->port[1]; - rport->tty = port->tty; - rport->private = port->private; - port->read_urb = rport->interrupt_in_urb; - } + spin_unlock_irqrestore(&priv->lock, flags); port->read_urb->dev = port->serial->dev; retval = usb_submit_urb(port->read_urb); @@ -551,6 +591,7 @@ struct usb_serial *serial = port->serial; struct tty_struct *tty; unsigned char *data = urb->transfer_buffer; + unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -587,6 +628,7 @@ * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. */ + spin_lock_irqsave(&priv->lock, flags); priv->last_msr = data[MCT_U232_MSR_INDEX]; /* Record Control Line states */ @@ -617,6 +659,7 @@ } } #endif + spin_unlock_irqrestore(&priv->lock, flags); /* INT urbs are automatically re-submitted */ } /* mct_u232_read_int_callback */ @@ -628,125 +671,113 @@ struct usb_serial *serial = port->serial; struct mct_u232_private *priv = (struct mct_u232_private *)port->private; unsigned int iflag = port->tty->termios->c_iflag; - unsigned int old_iflag = old_termios->c_iflag; unsigned int cflag = port->tty->termios->c_cflag; unsigned int old_cflag = old_termios->c_cflag; - + unsigned long flags; + unsigned int control_state, new_state; + unsigned char last_lcr; + + /* get a local copy of the current port settings */ + spin_lock_irqsave(&priv->lock, flags); + control_state = priv->control_state; + spin_unlock_irqrestore(&priv->lock, flags); + last_lcr = 0; + /* - * Update baud rate + * Update baud rate. + * Do not attempt to cache old rates and skip settings, + * disconnects screw such tricks up completely. + * Premature optimization is the root of all evil. */ - if( (cflag & CBAUD) != (old_cflag & CBAUD) ) { - /* reassert DTR and (maybe) RTS on transition from B0 */ - if( (old_cflag & CBAUD) == B0 ) { - dbg("%s: baud was B0", __FUNCTION__); - priv->control_state |= TIOCM_DTR; - /* don't set RTS if using hardware flow control */ - if (!(old_cflag & CRTSCTS)) { - priv->control_state |= TIOCM_RTS; - } - mct_u232_set_modem_ctrl(serial, priv->control_state); - } - - switch(cflag & CBAUD) { - case B0: /* handled below */ - break; - case B300: mct_u232_set_baud_rate(serial, 300); - break; - case B600: mct_u232_set_baud_rate(serial, 600); - break; - case B1200: mct_u232_set_baud_rate(serial, 1200); - break; - case B2400: mct_u232_set_baud_rate(serial, 2400); - break; - case B4800: mct_u232_set_baud_rate(serial, 4800); - break; - case B9600: mct_u232_set_baud_rate(serial, 9600); - break; - case B19200: mct_u232_set_baud_rate(serial, 19200); - break; - case B38400: mct_u232_set_baud_rate(serial, 38400); - break; - case B57600: mct_u232_set_baud_rate(serial, 57600); - break; - case B115200: mct_u232_set_baud_rate(serial, 115200); - break; - default: err("MCT USB-RS232 converter: unsupported baudrate request, using default of 9600"); - mct_u232_set_baud_rate(serial, 9600); break; - } - if ((cflag & CBAUD) == B0 ) { - dbg("%s: baud is B0", __FUNCTION__); - /* Drop RTS and DTR */ - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); - } + + /* reassert DTR and (maybe) RTS on transition from B0 */ + if ((old_cflag & CBAUD) == B0) { + dbg("%s: baud was B0", __FUNCTION__); + control_state |= TIOCM_DTR; + /* don't set RTS if using hardware flow control */ + if (!(old_cflag & CRTSCTS)) { + control_state |= TIOCM_RTS; + } + mct_u232_set_modem_ctrl(serial, control_state); + } + + mct_u232_set_baud_rate(serial, cflag & CBAUD); + + if ((cflag & CBAUD) == B0 ) { + dbg("%s: baud is B0", __FUNCTION__); + /* Drop RTS and DTR */ + control_state &= ~(TIOCM_DTR | TIOCM_RTS); + mct_u232_set_modem_ctrl(serial, control_state); } /* * Update line control register (LCR) */ - if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD)) - || (cflag & CSIZE) != (old_cflag & CSIZE) - || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) { - - priv->last_lcr = 0; + /* set the parity */ + if (cflag & PARENB) + last_lcr |= (cflag & PARODD) ? + MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; + else + last_lcr |= MCT_U232_PARITY_NONE; - /* set the parity */ - if (cflag & PARENB) - priv->last_lcr |= (cflag & PARODD) ? - MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN; - else - priv->last_lcr |= MCT_U232_PARITY_NONE; + /* set the number of data bits */ + switch (cflag & CSIZE) { + case CS5: + last_lcr |= MCT_U232_DATA_BITS_5; break; + case CS6: + last_lcr |= MCT_U232_DATA_BITS_6; break; + case CS7: + last_lcr |= MCT_U232_DATA_BITS_7; break; + case CS8: + last_lcr |= MCT_U232_DATA_BITS_8; break; + default: + err("CSIZE was not CS5-CS8, using default of 8"); + last_lcr |= MCT_U232_DATA_BITS_8; + break; + } - /* set the number of data bits */ - switch (cflag & CSIZE) { - case CS5: - priv->last_lcr |= MCT_U232_DATA_BITS_5; break; - case CS6: - priv->last_lcr |= MCT_U232_DATA_BITS_6; break; - case CS7: - priv->last_lcr |= MCT_U232_DATA_BITS_7; break; - case CS8: - priv->last_lcr |= MCT_U232_DATA_BITS_8; break; - default: - err("CSIZE was not CS5-CS8, using default of 8"); - priv->last_lcr |= MCT_U232_DATA_BITS_8; - break; - } + /* set the number of stop bits */ + last_lcr |= (cflag & CSTOPB) ? + MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; - /* set the number of stop bits */ - priv->last_lcr |= (cflag & CSTOPB) ? - MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1; + mct_u232_set_line_ctrl(serial, last_lcr); - mct_u232_set_line_ctrl(serial, priv->last_lcr); - } - /* * Set flow control: well, I do not really now how to handle DTR/RTS. * Just do what we have seen with SniffUSB on Win98. */ - if( (iflag & IXOFF) != (old_iflag & IXOFF) - || (iflag & IXON) != (old_iflag & IXON) - || (cflag & CRTSCTS) != (old_cflag & CRTSCTS) ) { - - /* Drop DTR/RTS if no flow control otherwise assert */ - if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) ) - priv->control_state |= TIOCM_DTR | TIOCM_RTS; - else - priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS); - mct_u232_set_modem_ctrl(serial, priv->control_state); + /* Drop DTR/RTS if no flow control otherwise assert */ + new_state = control_state; + if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) + new_state |= TIOCM_DTR | TIOCM_RTS; + else + new_state &= ~(TIOCM_DTR | TIOCM_RTS); + if (new_state != control_state) { + mct_u232_set_modem_ctrl(serial, control_state); + control_state = new_state; } -} /* mct_u232_set_termios */ + /* save off the modified port settings */ + spin_lock_irqsave(&priv->lock, flags); + priv->control_state = control_state; + priv->last_lcr = last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); +} /* mct_u232_set_termios */ static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state ) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv = (struct mct_u232_private *)port->private; - unsigned char lcr = priv->last_lcr; + unsigned char lcr; + unsigned long flags; dbg("%sstate=%d", __FUNCTION__, break_state); + spin_lock_irqsave(&priv->lock, flags); + lcr = priv->last_lcr; + spin_unlock_irqrestore(&priv->lock, flags); + if (break_state) lcr |= MCT_U232_SET_BREAK; @@ -760,7 +791,8 @@ struct usb_serial *serial = port->serial; struct mct_u232_private *priv = (struct mct_u232_private *)port->private; int mask; - + unsigned long flags; + dbg("%scmd=0x%x", __FUNCTION__, cmd); /* Based on code from acm.c and others */ @@ -775,6 +807,7 @@ if (get_user(mask, (unsigned long *) arg)) return -EFAULT; + spin_lock_irqsave(&priv->lock, flags); if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) { /* RTS needs set */ if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) || @@ -792,6 +825,7 @@ else priv->control_state &= ~TIOCM_DTR; } + spin_unlock_irqrestore(&priv->lock, flags); mct_u232_set_modem_ctrl(serial, priv->control_state); break; diff -urN linux-2.4.26/drivers/usb/serial/pl2303.c linux-2.4.27/drivers/usb/serial/pl2303.c --- linux-2.4.26/drivers/usb/serial/pl2303.c 2004-04-14 06:05:35.000000000 -0700 +++ linux-2.4.27/drivers/usb/serial/pl2303.c 2004-08-07 16:26:05.806394113 -0700 @@ -107,6 +107,7 @@ #define VENDOR_READ_REQUEST 0x01 #define UART_STATE 0x08 +#define UART_STATE_TRANSIENT_MASK 0x74 #define UART_DCD 0x01 #define UART_DSR 0x02 #define UART_BREAK_ERROR 0x04 @@ -198,6 +199,9 @@ dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count); + if (!count) + return count; + if (port->write_urb->status == -EINPROGRESS) { dbg("%s - already writing", __FUNCTION__); return 0; @@ -648,7 +652,7 @@ state = BREAK_OFF; else state = BREAK_ON; - dbg("%s - turning break %s", state==BREAK_OFF ? "off" : "on", __FUNCTION__); + dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on"); result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0), BREAK_REQUEST, BREAK_REQUEST_TYPE, state, @@ -678,6 +682,7 @@ struct pl2303_private *priv = usb_get_serial_port_data(port); unsigned char *data = urb->transfer_buffer; unsigned long flags; + u8 uart_state; dbg("%s (%d)", __FUNCTION__, port->number); @@ -708,8 +713,10 @@ return; /* Save off the uart status for others to look at */ + uart_state = data[UART_STATE]; spin_lock_irqsave(&priv->lock, flags); - priv->line_status = data[UART_STATE]; + uart_state |= (priv->line_status & UART_STATE_TRANSIENT_MASK); + priv->line_status = uart_state; spin_unlock_irqrestore(&priv->lock, flags); wake_up_interruptible (&priv->delta_msr_wait); @@ -767,7 +774,9 @@ spin_lock_irqsave(&priv->lock, flags); status = priv->line_status; + priv->line_status &= ~UART_STATE_TRANSIENT_MASK; spin_unlock_irqrestore(&priv->lock, flags); + wake_up_interruptible (&priv->delta_msr_wait); //AF from 2.6 /* break takes precedence over parity, */ /* which takes precedence over framing errors */ diff -urN linux-2.4.26/drivers/usb/serial/usb-serial.h linux-2.4.27/drivers/usb/serial/usb-serial.h --- linux-2.4.26/drivers/usb/serial/usb-serial.h 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/serial/usb-serial.h 2004-08-07 16:26:05.806394113 -0700 @@ -111,6 +111,8 @@ int bulk_out_size; struct urb * write_urb; __u8 bulk_out_endpointAddress; + char write_busy; /* URB is active */ + int write_backlog; /* Fifo used */ wait_queue_head_t write_wait; struct tq_struct tqueue; @@ -161,6 +163,7 @@ __u16 product; struct usb_serial_port port[MAX_NUM_PORTS]; void * private; + int ref; }; diff -urN linux-2.4.26/drivers/usb/serial/usbserial.c linux-2.4.27/drivers/usb/serial/usbserial.c --- linux-2.4.26/drivers/usb/serial/usbserial.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/serial/usbserial.c 2004-08-07 16:26:05.809394236 -0700 @@ -347,11 +347,29 @@ }; #endif +/* + * The post kludge structures and variables. + */ +#define POST_BSIZE 100 /* little below 128 in total */ +struct usb_serial_post_job { + struct list_head link; + struct usb_serial_port *port; + int len; + char buff[POST_BSIZE]; +}; +static spinlock_t post_lock = SPIN_LOCK_UNLOCKED; /* Also covers ->ref */ +static struct list_head post_list = LIST_HEAD_INIT(post_list); +static struct tq_struct post_task; /* local function prototypes */ static int serial_open (struct tty_struct *tty, struct file * filp); static void serial_close (struct tty_struct *tty, struct file * filp); +static int __serial_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count); +static int serial_post_job(struct usb_serial_port *port, int from_user, + int gfp, const unsigned char *buf, int count); +static int serial_post_one(struct usb_serial_port *port, int from_user, + int gfp, const unsigned char *buf, int count); static int serial_write_room (struct tty_struct *tty); static int serial_chars_in_buffer (struct tty_struct *tty); static void serial_throttle (struct tty_struct * tty); @@ -448,6 +466,63 @@ return; } +/* + * The post kludge. + * + * Our component drivers are hideously buggy and written by people + * who have difficulty understanding the concept of spinlocks. + * There were so many races and lockups that Greg K-H made a watershed + * decision to provide what is essentially a single-threaded sandbox + * for component drivers, protected by a semaphore. It helped a lot, but + * for one little problem: when tty->low_latency is set, line disciplines + * can call ->write from an interrupt, where the semaphore oopses. + * + * Rather than open the whole can of worms again, we just post writes + * into a helper which can sleep. + * + * Kernel 2.6 has a proper fix. It replaces semaphores with proper locking. + */ +static void post_helper(void *arg) +{ + struct list_head *pos; + struct usb_serial_post_job *job; + struct usb_serial_port *port; + struct usb_serial *serial; + unsigned int flags; + + spin_lock_irqsave(&post_lock, flags); + pos = post_list.next; + while (pos != &post_list) { + job = list_entry(pos, struct usb_serial_post_job, link); + port = job->port; + /* get_usb_serial checks port->tty, so cannot be used */ + serial = port->serial; + if (port->write_busy) { + dbg("%s - port %d busy", __FUNCTION__, port->number); + pos = pos->next; + continue; + } + list_del(&job->link); + spin_unlock_irqrestore(&post_lock, flags); + + down(&port->sem); + dbg("%s - port %d len %d backlog %d", __FUNCTION__, + port->number, job->len, port->write_backlog); + if (port->tty != NULL) + __serial_write(port, 0, job->buff, job->len); + up(&port->sem); + + spin_lock_irqsave(&post_lock, flags); + port->write_backlog -= job->len; + kfree(job); + if (--serial->ref == 0) + kfree(serial); + /* Have to reset because we dropped spinlock */ + pos = post_list.next; + } + spin_unlock_irqrestore(&post_lock, flags); +} + #ifdef USES_EZUSB_FUNCTIONS /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */ #define CPUCS_REG 0x7F92 @@ -580,23 +655,34 @@ /* if disconnect beat us to the punch here, there's nothing to do */ if (tty->driver_data) { + /* + * XXX The right thing would be to wait for the output to drain. + * But we are not sufficiently daring to experiment in 2.4. + * N.B. If we do wait, no need to run post_helper here. + * Normall callback mechanism wakes it up just fine. + */ +#if I_AM_A_DARING_HACKER + tty->closing = 1; + up (&port->sem); + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + down (&port->sem); + if (!tty->driver_data) /* woopsie, disconnect, now what */ ; +#endif __serial_close(port, filp); } up (&port->sem); } -static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) +static int __serial_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) { - struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); int retval = -EINVAL; if (!serial) return -ENODEV; - down (&port->sem); - dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count); if (!port->open_count) { @@ -611,10 +697,132 @@ retval = generic_write(port, from_user, buf, count); exit: - up (&port->sem); return retval; } +static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + int rc; + + if (!in_interrupt()) { + /* + * Run post_list to reduce a possiblity of reordered writes. + * Tasks can make keventd to sleep, sometimes for a long time. + */ + post_helper(NULL); + + down(&port->sem); + /* + * This happens when a line discipline asks how much room + * we have, gets 64, then tries to perform two writes + * for a byte each. First write takes whole URB, second + * write hits this check. + */ + if (port->write_busy) { + up(&port->sem); + return serial_post_job(port, from_user, GFP_KERNEL, + buf, count); + } + + rc = __serial_write(port, from_user, buf, count); + up(&port->sem); + return rc; + } + + if (from_user) { + /* + * This is a BUG-able offense because we cannot + * pagefault while in_interrupt, but we want to see + * something in dmesg rather than just blinking LEDs. + */ + err("user data in interrupt write"); + return -EINVAL; + } + + return serial_post_job(port, 0, GFP_ATOMIC, buf, count); +} + +static int serial_post_job(struct usb_serial_port *port, int from_user, + int gfp, const unsigned char *buf, int count) +{ + int done = 0, length; + int rc; + + if (port == NULL) + return -EPIPE; + + if (count >= 512) { + static int rate = 0; + /* + * Data loss due to extreme circumstances. + * It's a ususal thing on serial to lose characters, isn't it? + * Neener, neener! Actually, it's probably an echo loop anyway. + * Only happens when getty starts talking to Visor. + */ + if (++rate % 1000 < 3) { + err("too much data (%d) from %s", count, + from_user? "user": "kernel"); + } + count = 512; + } + + while (done < count) { + length = count - done; + if (length > POST_BSIZE) + length = POST_BSIZE; + if (length > port->bulk_out_size) + length = port->bulk_out_size; + + rc = serial_post_one(port, from_user, gfp, buf + done, length); + if (rc <= 0) { + if (done != 0) + return done; + return rc; + } + done += rc; + } + + return done; +} + +static int serial_post_one(struct usb_serial_port *port, int from_user, + int gfp, const unsigned char *buf, int count) +{ + struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); + struct usb_serial_post_job *job; + unsigned long flags; + + dbg("%s - port %d user %d count %d", __FUNCTION__, port->number, from_user, count); + + job = kmalloc(sizeof(struct usb_serial_post_job), gfp); + if (job == NULL) + return -ENOMEM; + + job->port = port; + if (count >= POST_BSIZE) + count = POST_BSIZE; + job->len = count; + + if (from_user) { + if (copy_from_user(job->buff, buf, count)) { + kfree(job); + return -EFAULT; + } + } else { + memcpy(job->buff, buf, count); + } + + spin_lock_irqsave(&post_lock, flags); + port->write_backlog += count; + list_add_tail(&job->link, &post_list); + serial->ref++; /* Protect the port->sem from kfree() */ + schedule_task(&post_task); + spin_unlock_irqrestore(&post_lock, flags); + + return count; +} + static int serial_write_room (struct tty_struct *tty) { struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; @@ -624,6 +832,14 @@ if (!serial) return -ENODEV; + if (in_interrupt()) { + retval = 0; + if (!port->write_busy && port->write_backlog == 0) + retval = port->bulk_out_size; + dbg("%s - returns %d", __FUNCTION__, retval); + return retval; + } + down (&port->sem); dbg("%s - port %d", __FUNCTION__, port->number); @@ -655,10 +871,8 @@ down (&port->sem); - dbg("%s = port %d", __FUNCTION__, port->number); - if (!port->open_count) { - dbg("%s - port not open", __FUNCTION__); + dbg("%s - port %d: not open", __FUNCTION__, port->number); goto exit; } @@ -917,18 +1131,23 @@ { struct usb_serial *serial = port->serial; int result; - - dbg("%s - port %d", __FUNCTION__, port->number); + unsigned long flags; if (count == 0) { dbg("%s - write request of 0 bytes", __FUNCTION__); return (0); } + if (count < 0) { + err("%s - port %d: write request of %d bytes", __FUNCTION__, + port->number, count); + return (0); + } /* only do something if we have a bulk out endpoint */ if (serial->num_bulk_out) { - if (port->write_urb->status == -EINPROGRESS) { - dbg("%s - already writing", __FUNCTION__); + if (port->write_busy) { + /* Happens when two threads run port_helper. Watch. */ + info("%s - already writing", __FUNCTION__); return (0); } @@ -937,12 +1156,10 @@ if (from_user) { if (copy_from_user(port->write_urb->transfer_buffer, buf, count)) return -EFAULT; - } - else { + } else { memcpy (port->write_urb->transfer_buffer, buf, count); } - - usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer); + dbg("%s - port %d [%d]", __FUNCTION__, port->number, count); /* set up our urb */ usb_fill_bulk_urb (port->write_urb, serial->dev, @@ -954,10 +1171,18 @@ generic_write_bulk_callback), port); /* send the data out the bulk port */ + port->write_busy = 1; result = usb_submit_urb(port->write_urb); - if (result) - err("%s - failed submitting write urb, error %d", __FUNCTION__, result); - else + if (result) { + err("%s - port %d: failed submitting write urb (%d)", + __FUNCTION__, port->number, result); + port->write_busy = 0; + spin_lock_irqsave(&post_lock, flags); + if (port->write_backlog != 0) + schedule_task(&post_task); + spin_unlock_irqrestore(&post_lock, flags); + + } else result = count; return result; @@ -972,14 +1197,12 @@ struct usb_serial *serial = port->serial; int room = 0; - dbg("%s - port %d", __FUNCTION__, port->number); - if (serial->num_bulk_out) { - if (port->write_urb->status != -EINPROGRESS) + if (!port->write_busy && port->write_backlog == 0) room = port->bulk_out_size; } - dbg("%s - returns %d", __FUNCTION__, room); + dbg("%s - port %d, returns %d", __FUNCTION__, port->number, room); return (room); } @@ -991,8 +1214,9 @@ dbg("%s - port %d", __FUNCTION__, port->number); if (serial->num_bulk_out) { - if (port->write_urb->status == -EINPROGRESS) - chars = port->write_urb->transfer_buffer_length; + if (port->write_busy) + chars += port->write_urb->transfer_buffer_length; + chars += port->write_backlog; /* spin_lock... Baah */ } dbg("%s - returns %d", __FUNCTION__, chars); @@ -1056,14 +1280,16 @@ dbg("%s - port %d", __FUNCTION__, port->number); + port->write_busy = 0; + wmb(); + if (!serial) { - dbg("%s - bad serial pointer, exiting", __FUNCTION__); + err("%s - null serial pointer, exiting", __FUNCTION__); return; } if (urb->status) { dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status); - return; } queue_task(&port->tqueue, &tq_immediate); @@ -1089,12 +1315,18 @@ struct usb_serial_port *port = (struct usb_serial_port *)private; struct usb_serial *serial = get_usb_serial (port, __FUNCTION__); struct tty_struct *tty; + unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); if (!serial) return; + spin_lock_irqsave(&post_lock, flags); + if (port->write_backlog != 0) + schedule_task(&post_task); + spin_unlock_irqrestore(&post_lock, flags); + tty = port->tty; if (!tty) return; @@ -1108,7 +1340,6 @@ } - static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id) { @@ -1132,6 +1363,7 @@ int num_ports; int max_endpoints; const struct usb_device_id *id_pattern = NULL; + unsigned long flags; /* loop through our list of known serial converters, and see if this device matches. */ @@ -1342,11 +1574,15 @@ init_MUTEX (&port->sem); } + spin_lock_irqsave(&post_lock, flags); + serial->ref = 1; + spin_unlock_irqrestore(&post_lock, flags); + /* if this device type has a startup function, call it */ if (type->startup) { i = type->startup (serial); if (i < 0) - goto probe_error; + goto startup_error; if (i > 0) return serial; } @@ -1361,6 +1597,12 @@ return serial; /* success */ +startup_error: + spin_lock_irqsave(&post_lock, flags); + if (serial->ref != 1) { + err("bug in component startup: ref %d\n", serial->ref); + } + spin_unlock_irqrestore(&post_lock, flags); probe_error: for (i = 0; i < num_bulk_in; ++i) { port = &serial->port[i]; @@ -1396,6 +1638,7 @@ { struct usb_serial *serial = (struct usb_serial *) ptr; struct usb_serial_port *port; + unsigned long flags; int i; dbg ("%s", __FUNCTION__); @@ -1453,7 +1696,10 @@ return_serial (serial); /* free up any memory that we allocated */ - kfree (serial); + spin_lock_irqsave(&post_lock, flags); + if (--serial->ref == 0) + kfree(serial); + spin_unlock_irqrestore(&post_lock, flags); } else { info("device disconnected"); @@ -1505,6 +1751,7 @@ for (i = 0; i < SERIAL_TTY_MINORS; ++i) { serial_table[i] = NULL; } + post_task.routine = post_helper; /* register the tty driver */ serial_tty_driver.init_termios = tty_std_termios; diff -urN linux-2.4.26/drivers/usb/serial/visor.c linux-2.4.27/drivers/usb/serial/visor.c --- linux-2.4.26/drivers/usb/serial/visor.c 2004-04-14 06:05:35.000000000 -0700 +++ linux-2.4.27/drivers/usb/serial/visor.c 2004-08-07 16:26:05.810394278 -0700 @@ -184,6 +184,7 @@ static void visor_read_bulk_callback (struct urb *urb); static void visor_read_int_callback (struct urb *urb); static int clie_3_5_startup (struct usb_serial *serial); +static int clie_5_startup (struct usb_serial *serial); static void treo_attach (struct usb_serial *serial); /* Parameters that may be passed into the module. */ @@ -211,7 +212,6 @@ { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_4_1_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NX60_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_NZ90V_ID) }, - { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) }, { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_TJ25_ID) }, { USB_DEVICE(SAMSUNG_VENDOR_ID, SAMSUNG_SCH_I330_ID) }, { USB_DEVICE(GARMIN_VENDOR_ID, GARMIN_IQUE_3600_ID) }, @@ -224,6 +224,12 @@ { } /* Terminating entry */ }; + +static struct usb_device_id clie_id_5_table [] = { + { USB_DEVICE(SONY_VENDOR_ID, SONY_CLIE_UX50_ID) }, + { } /* Terminating entry */ +}; + static __devinitdata struct usb_device_id id_table_combined [] = { { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID) }, { USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_TREO_ID) }, @@ -310,6 +316,32 @@ .read_bulk_callback = visor_read_bulk_callback, }; + +/* device info for the Sony Clie OS version 5.0 */ +static struct usb_serial_device_type clie_5_device = { + .owner = THIS_MODULE, + .name = "Sony Clié 5.0", + .id_table = clie_id_5_table, + .num_interrupt_in = NUM_DONT_CARE, + .num_bulk_in = 2, + .num_bulk_out = 2, + .num_ports = 2, + .open = visor_open, + .close = visor_close, + .throttle = visor_throttle, + .unthrottle = visor_unthrottle, + .startup = clie_5_startup, + .shutdown = visor_shutdown, + .ioctl = visor_ioctl, + .set_termios = visor_set_termios, + .write = visor_write, + .write_room = visor_write_room, + .chars_in_buffer = visor_chars_in_buffer, + .write_bulk_callback = visor_write_bulk_callback, + .read_bulk_callback = visor_read_bulk_callback, + .read_int_callback = visor_read_int_callback, +}; + /* This structure is for Handspring Visor, and Palm 4.0 devices that are not * compiled into the kernel, but can be passed in when the module is loaded. * This will allow the visor driver to work with new Vendor and Device IDs @@ -853,6 +885,67 @@ return 0; } + +static int clie_5_startup (struct usb_serial *serial) +{ + int response; + unsigned char *transfer_buffer; + struct palm_ext_connection_info *connection_info; + + dbg("%s", __FUNCTION__); + + dbg("%s - Set config to 1", __FUNCTION__); + usb_set_configuration(serial->dev, 1); + + transfer_buffer = kmalloc(sizeof (*connection_info), + GFP_KERNEL); + if (!transfer_buffer) { + err("%s - kmalloc(%d) failed.", __FUNCTION__, + sizeof (*connection_info)); + return -ENOMEM; + } + + response = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + PALM_GET_EXT_CONNECTION_INFORMATION, + 0xc2, 0x0000, 0x0000, transfer_buffer, + sizeof(*connection_info), 300); + if (response < 0) { + err("%s - error %d getting connection info", + __FUNCTION__, response); + } else { + usb_serial_debug_data (__FILE__, __FUNCTION__, 0x14, transfer_buffer); + } + + /* ask for the number of bytes available, but ignore the response as it is broken */ + response = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE, + 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300); + if (response < 0) { + err("%s - error getting bytes available request", __FUNCTION__); + } + + kfree (transfer_buffer); + + /* UX50/TH55 registers 2 ports. + Communication in from the TH55 uses bulk_in_endpointAddress from port 0 + Communication out to the TH55 uses bulk_out_endpointAddress from port 1 + + Lets do a quick and dirty mapping + */ + + /* some sanity check */ + if (serial->num_ports < 2) + return -ENODEV; + + /* port 0 now uses the modified endpoint Address */ + serial->port[0].bulk_out_endpointAddress = serial->port[1].bulk_out_endpointAddress; + + /* continue on with initialization */ + return 0; +} + + + + static void treo_attach (struct usb_serial *serial) { struct usb_serial_port *port; @@ -993,6 +1086,7 @@ } usb_serial_register (&handspring_device); usb_serial_register (&clie_3_5_device); + usb_serial_register (&clie_5_device); /* create our write urb pool and transfer buffers */ spin_lock_init (&write_urb_pool_lock); @@ -1029,6 +1123,7 @@ } usb_serial_deregister (&handspring_device); usb_serial_deregister (&clie_3_5_device); + usb_serial_deregister (&clie_5_device); spin_lock_irqsave (&write_urb_pool_lock, flags); diff -urN linux-2.4.26/drivers/usb/speedtch.c linux-2.4.27/drivers/usb/speedtch.c --- linux-2.4.26/drivers/usb/speedtch.c 2003-11-28 10:26:20.000000000 -0800 +++ linux-2.4.27/drivers/usb/speedtch.c 2004-08-07 16:26:05.811394319 -0700 @@ -83,6 +83,10 @@ #define VERBOSE_DEBUG */ +#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG) +# define DEBUG +#endif + #include #ifdef DEBUG @@ -101,8 +105,8 @@ #endif #define DRIVER_AUTHOR "Johan Verrept, Duncan Sands " -#define DRIVER_DESC "Alcatel SpeedTouch USB driver" -#define DRIVER_VERSION "1.7" +#define DRIVER_VERSION "1.8" +#define DRIVER_DESC "Alcatel SpeedTouch USB driver version " DRIVER_VERSION static const char udsl_driver_name [] = "speedtch"; @@ -294,6 +298,19 @@ }; +/*********** +** misc ** +***********/ + +static inline void udsl_pop (struct atm_vcc *vcc, struct sk_buff *skb) +{ + if (vcc->pop) + vcc->pop (vcc, skb); + else + dev_kfree_skb (skb); +} + + /************* ** decode ** *************/ @@ -717,10 +734,7 @@ if (!UDSL_SKB (skb)->num_cells) { struct atm_vcc *vcc = UDSL_SKB (skb)->atm_data.vcc; - if (vcc->pop) - vcc->pop (vcc, skb); - else - dev_kfree_skb (skb); + udsl_pop (vcc, skb); instance->current_skb = NULL; atomic_inc (&vcc->stats->tx); @@ -739,10 +753,7 @@ if (UDSL_SKB (skb)->atm_data.vcc == vcc) { dbg ("udsl_cancel_send: popping skb 0x%p", skb); __skb_unlink (skb, &instance->sndqueue); - if (vcc->pop) - vcc->pop (vcc, skb); - else - dev_kfree_skb (skb); + udsl_pop (vcc, skb); } spin_unlock_irq (&instance->sndqueue.lock); @@ -750,10 +761,7 @@ if ((skb = instance->current_skb) && (UDSL_SKB (skb)->atm_data.vcc == vcc)) { dbg ("udsl_cancel_send: popping current skb (0x%p)", skb); instance->current_skb = NULL; - if (vcc->pop) - vcc->pop (vcc, skb); - else - dev_kfree_skb (skb); + udsl_pop (vcc, skb); } tasklet_enable (&instance->send_tasklet); dbg ("udsl_cancel_send done"); @@ -762,22 +770,26 @@ static int udsl_atm_send (struct atm_vcc *vcc, struct sk_buff *skb) { struct udsl_instance_data *instance = vcc->dev->dev_data; + int err; vdbg ("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len); if (!instance || !instance->usb_dev) { dbg ("udsl_atm_send: NULL data!"); - return -ENODEV; + err = -ENODEV; + goto fail; } if (vcc->qos.aal != ATM_AAL5) { dbg ("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal); - return -EINVAL; + err = -EINVAL; + goto fail; } if (skb->len > ATM_MAX_AAL5_PDU) { dbg ("udsl_atm_send: packet too long (%d vs %d)!", skb->len, ATM_MAX_AAL5_PDU); - return -EINVAL; + err = -EINVAL; + goto fail; } PACKETDEBUG (skb->data, skb->len); @@ -787,6 +799,10 @@ tasklet_schedule (&instance->send_tasklet); return 0; + +fail: + udsl_pop (vcc, skb); + return err; } diff -urN linux-2.4.26/drivers/usb/storage/jumpshot.c linux-2.4.27/drivers/usb/storage/jumpshot.c --- linux-2.4.26/drivers/usb/storage/jumpshot.c 2003-06-13 07:51:37.000000000 -0700 +++ linux-2.4.27/drivers/usb/storage/jumpshot.c 2004-08-07 16:26:05.812394360 -0700 @@ -710,15 +710,8 @@ // build the reply // - ptr[0] = (info->sectors >> 24) & 0xFF; - ptr[1] = (info->sectors >> 16) & 0xFF; - ptr[2] = (info->sectors >> 8) & 0xFF; - ptr[3] = (info->sectors) & 0xFF; - - ptr[4] = (info->ssize >> 24) & 0xFF; - ptr[5] = (info->ssize >> 16) & 0xFF; - ptr[6] = (info->ssize >> 8) & 0xFF; - ptr[7] = (info->ssize) & 0xFF; + ((u32 *) ptr)[0] = cpu_to_be32(info->sectors - 1); + ((u32 *) ptr)[1] = cpu_to_be32(info->ssize); return USB_STOR_TRANSPORT_GOOD; } diff -urN linux-2.4.26/drivers/usb/storage/scsiglue.c linux-2.4.27/drivers/usb/storage/scsiglue.c --- linux-2.4.26/drivers/usb/storage/scsiglue.c 2004-04-14 06:05:35.000000000 -0700 +++ linux-2.4.27/drivers/usb/storage/scsiglue.c 2004-08-07 16:26:05.813394401 -0700 @@ -218,7 +218,14 @@ US_DEBUGP("device_reset() called\n" ); spin_unlock_irq(&io_request_lock); + down(&(us->dev_semaphore)); + if (!us->pusb_dev) { + up(&(us->dev_semaphore)); + spin_lock_irq(&io_request_lock); + return SUCCESS; + } rc = us->transport_reset(us); + up(&(us->dev_semaphore)); spin_lock_irq(&io_request_lock); return rc; } @@ -235,27 +242,44 @@ /* we use the usb_reset_device() function to handle this for us */ US_DEBUGP("bus_reset() called\n"); + spin_unlock_irq(&io_request_lock); + + down(&(us->dev_semaphore)); + /* if the device has been removed, this worked */ if (!us->pusb_dev) { US_DEBUGP("-- device removed already\n"); + up(&(us->dev_semaphore)); + spin_lock_irq(&io_request_lock); return SUCCESS; } - spin_unlock_irq(&io_request_lock); + /* The USB subsystem doesn't handle synchronisation between + * a device's several drivers. Therefore we reset only devices + * with just one interface, which we of course own. */ + if (us->pusb_dev->actconfig->bNumInterfaces != 1) { + printk(KERN_NOTICE "usb-storage: " + "Refusing to reset a multi-interface device\n"); + up(&(us->dev_semaphore)); + spin_lock_irq(&io_request_lock); + /* XXX Don't just return success, make sure current cmd fails */ + return SUCCESS; + } /* release the IRQ, if we have one */ - down(&(us->irq_urb_sem)); if (us->irq_urb) { US_DEBUGP("-- releasing irq URB\n"); result = usb_unlink_urb(us->irq_urb); US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); } - up(&(us->irq_urb_sem)); /* attempt to reset the port */ if (usb_reset_device(us->pusb_dev) < 0) { - spin_lock_irq(&io_request_lock); - return FAILED; + /* + * Do not return errors, or else the error handler might + * invoke host_reset, which is not implemented. + */ + goto bail_out; } /* FIXME: This needs to lock out driver probing while it's working @@ -286,17 +310,18 @@ up(&intf->driver->serialize); } +bail_out: /* re-allocate the IRQ URB and submit it to restore connectivity * for CBI devices */ if (us->protocol == US_PR_CBI) { - down(&(us->irq_urb_sem)); us->irq_urb->dev = us->pusb_dev; result = usb_submit_urb(us->irq_urb); US_DEBUGP("usb_submit_urb() returns %d\n", result); - up(&(us->irq_urb_sem)); } - + + up(&(us->dev_semaphore)); + spin_lock_irq(&io_request_lock); US_DEBUGP("bus_reset() complete\n"); diff -urN linux-2.4.26/drivers/usb/storage/unusual_devs.h linux-2.4.27/drivers/usb/storage/unusual_devs.h --- linux-2.4.26/drivers/usb/storage/unusual_devs.h 2004-04-14 06:05:35.000000000 -0700 +++ linux-2.4.27/drivers/usb/storage/unusual_devs.h 2004-08-07 16:26:05.814394442 -0700 @@ -185,7 +185,7 @@ UNUSUAL_DEV( 0x04e6, 0x0006, 0x0100, 0x0205, "Shuttle", "eUSB MMC Adapter", - US_SC_SCSI, US_PR_CB, NULL, + US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN), UNUSUAL_DEV( 0x04e6, 0x0007, 0x0100, 0x0200, @@ -273,6 +273,13 @@ US_SC_SCSI, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN | US_FL_MODE_XLATE), +/* Submitted by Rajesh Kumble Nayak */ +UNUSUAL_DEV( 0x054c, 0x002e, 0x0500, 0x0500, + "Sony", + "Handycam HC-85", + US_SC_UFI, US_PR_DEVICE, NULL, + US_FL_SINGLE_LUN | US_FL_MODE_XLATE), + UNUSUAL_DEV( 0x054c, 0x0032, 0x0000, 0x9999, "Sony", "Memorystick MSC-U01N", @@ -311,6 +318,15 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN), +/* Reported by Johann Cardon + * This entry is needed only because the device reports + * bInterfaceClass = 0xff (vendor-specific) + */ +UNUSUAL_DEV( 0x057b, 0x0022, 0x0000, 0x9999, + "Y-E Data", + "Silicon Media R/W", + US_SC_DEVICE, US_PR_DEVICE, NULL, 0), + /* Fabrizio Fellini */ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210, "Fujifilm", @@ -669,7 +685,7 @@ UNUSUAL_DEV( 0x0a16, 0x8888, 0x0100, 0x0100, "IBM", "IBM USB Memory Key", - US_SC_SCSI, US_PR_BULK, NULL, + US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY ), /* This Pentax still camera is not conformant diff -urN linux-2.4.26/drivers/usb/storage/usb.c linux-2.4.27/drivers/usb/storage/usb.c --- linux-2.4.26/drivers/usb/storage/usb.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/usb/storage/usb.c 2004-08-07 16:26:05.815394483 -0700 @@ -501,6 +501,9 @@ * strucuture is current. This includes the ep_int field, which gives us * the endpoint for the interrupt. * Returns non-zero on failure, zero on success + * + * ss->dev_semaphore is expected taken, except for a newly minted, + * unregistered device. */ static int usb_stor_allocate_irq(struct us_data *ss) { @@ -510,13 +513,9 @@ US_DEBUGP("Allocating IRQ for CBI transport\n"); - /* lock access to the data structure */ - down(&(ss->irq_urb_sem)); - /* allocate the URB */ ss->irq_urb = usb_alloc_urb(0); if (!ss->irq_urb) { - up(&(ss->irq_urb_sem)); US_DEBUGP("couldn't allocate interrupt URB"); return 1; } @@ -537,12 +536,9 @@ US_DEBUGP("usb_submit_urb() returns %d\n", result); if (result) { usb_free_urb(ss->irq_urb); - up(&(ss->irq_urb_sem)); return 2; } - /* unlock the data structure and return success */ - up(&(ss->irq_urb_sem)); return 0; } @@ -772,7 +768,6 @@ init_completion(&(ss->notify)); init_MUTEX_LOCKED(&(ss->ip_waitq)); spin_lock_init(&(ss->queue_exclusion)); - init_MUTEX(&(ss->irq_urb_sem)); init_MUTEX(&(ss->current_urb_sem)); init_MUTEX(&(ss->dev_semaphore)); @@ -1063,7 +1058,6 @@ down(&(ss->dev_semaphore)); /* release the IRQ, if we have one */ - down(&(ss->irq_urb_sem)); if (ss->irq_urb) { US_DEBUGP("-- releasing irq URB\n"); result = usb_unlink_urb(ss->irq_urb); @@ -1071,7 +1065,6 @@ usb_free_urb(ss->irq_urb); ss->irq_urb = NULL; } - up(&(ss->irq_urb_sem)); /* free up the main URB for this device */ US_DEBUGP("-- releasing main URB\n"); diff -urN linux-2.4.26/drivers/usb/storage/usb.h linux-2.4.27/drivers/usb/storage/usb.h --- linux-2.4.26/drivers/usb/storage/usb.h 2003-08-25 04:44:42.000000000 -0700 +++ linux-2.4.27/drivers/usb/storage/usb.h 2004-08-07 16:26:05.816394524 -0700 @@ -116,7 +116,7 @@ struct us_data *next; /* next device */ /* the device we're working with */ - struct semaphore dev_semaphore; /* protect pusb_dev */ + struct semaphore dev_semaphore; /* protect many things */ struct usb_device *pusb_dev; /* this usb_device */ unsigned int flags; /* from filter initially */ @@ -162,7 +162,6 @@ atomic_t ip_wanted[1]; /* is an IRQ expected? */ /* interrupt communications data */ - struct semaphore irq_urb_sem; /* to protect irq_urb */ struct urb *irq_urb; /* for USB int requests */ unsigned char irqbuf[2]; /* buffer for USB IRQ */ unsigned char irqdata[2]; /* data from USB IRQ */ diff -urN linux-2.4.26/drivers/usb/tiglusb.c linux-2.4.27/drivers/usb/tiglusb.c --- linux-2.4.26/drivers/usb/tiglusb.c 2003-06-13 07:51:37.000000000 -0700 +++ linux-2.4.27/drivers/usb/tiglusb.c 2004-08-07 16:26:05.817394565 -0700 @@ -3,7 +3,7 @@ * tiglusb -- Texas Instruments' USB GraphLink (aka SilverLink) driver. * Target: Texas Instruments graphing calculators (http://lpg.ticalc.org). * - * Copyright (C) 2001-2002: + * Copyright (C) 2001-2004: * Romain Lievin * Julien BLACHE * under the terms of the GNU General Public License. @@ -14,11 +14,14 @@ * and the website at: http://lpg.ticalc.org/prj_usb/ * for more info. * + * History: * 1.0x, Romain & Julien: initial submit. * 1.03, Greg Kroah: modifications. * 1.04, Julien: clean-up & fixes; Romain: 2.4 backport. * 1.05, Randy Dunlap: bug fix with the timeout parameter (divide-by-zero). * 1.06, Romain: synched with 2.5, version/firmware changed (confusing). + * 1.07, Romain: fixed bad use of usb_clear_halt (invalid argument); + * timeout argument checked in ioctl + clean-up. */ #include @@ -38,8 +41,8 @@ /* * Version Information */ -#define DRIVER_VERSION "1.06" -#define DRIVER_AUTHOR "Romain Lievin & Julien Blache " +#define DRIVER_VERSION "1.07" +#define DRIVER_AUTHOR "Romain Lievin & Julien Blache " #define DRIVER_DESC "TI-GRAPH LINK USB (aka SilverLink) driver" #define DRIVER_LICENSE "GPL" @@ -74,15 +77,15 @@ { unsigned int pipe; - pipe = usb_sndbulkpipe (dev, 1); - if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { - err ("clear_pipe (r), request failed"); + pipe = usb_sndbulkpipe (dev, 2); + if (usb_clear_halt (dev, pipe)) { + err ("clear_pipe (w), request failed"); return -1; } - pipe = usb_sndbulkpipe (dev, 2); - if (usb_clear_halt (dev, usb_pipeendpoint (pipe))) { - err ("clear_pipe (w), request failed"); + pipe = usb_rcvbulkpipe (dev, 1); + if (usb_clear_halt (dev, pipe)) { + err ("clear_pipe (r), request failed"); return -1; } @@ -181,15 +184,14 @@ result = usb_bulk_msg (s->dev, pipe, buffer, bytes_to_read, &bytes_read, HZ * 10 / timeout); if (result == -ETIMEDOUT) { /* NAK */ - ret = result; - if (!bytes_read) { + if (!bytes_read) dbg ("quirk !"); - } warn ("tiglusb_read, NAK received."); + ret = result; goto out; } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ warn ("clear_halt request to remove STALL condition."); - if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) + if (usb_clear_halt (s->dev, pipe)) err ("clear_halt, request failed"); clear_device (s->dev); ret = result; @@ -244,7 +246,7 @@ goto out; } else if (result == -EPIPE) { /* STALL -- shouldn't happen */ warn ("clear_halt request to remove STALL condition."); - if (usb_clear_halt (s->dev, usb_pipeendpoint (pipe))) + if (usb_clear_halt (s->dev, pipe)) err ("clear_halt, request failed"); clear_device (s->dev); ret = result; @@ -284,15 +286,16 @@ switch (cmd) { case IOCTL_TIUSB_TIMEOUT: - timeout = arg; // timeout value in tenth of seconds + if (arg > 0) + timeout = (int)arg; + else + ret = -EINVAL; break; case IOCTL_TIUSB_RESET_DEVICE: - dbg ("IOCTL_TIGLUSB_RESET_DEVICE"); if (clear_device (s->dev)) ret = -EIO; break; case IOCTL_TIUSB_RESET_PIPES: - dbg ("IOCTL_TIGLUSB_RESET_PIPES"); if (clear_pipes (s->dev)) ret = -EIO; break; @@ -430,7 +433,7 @@ #ifndef MODULE /* - * You can use 'tiusb=timeout' + * You can use 'tiusb=timeout' to set timeout. */ static int __init tiglusb_setup (char *str) @@ -440,10 +443,11 @@ str = get_options (str, ARRAY_SIZE (ints), ints); if (ints[0] > 0) { - timeout = ints[1]; + if (ints[1] > 0) + timeout = ints[1]; + else + info ("tiglusb: wrong timeout value (0), using default value."); } - if (!timeout) - timeout = TIMAXTIME; return 1; } @@ -466,8 +470,6 @@ init_waitqueue_head (&s->wait); init_waitqueue_head (&s->remove_ok); } - if (timeout <= 0) - timeout = TIMAXTIME; /* register device */ if (register_chrdev (TIUSB_MAJOR, "tiglusb", &tiglusb_fops)) { @@ -487,12 +489,6 @@ info (DRIVER_DESC ", version " DRIVER_VERSION); - if (timeout <= 0) - timeout = TIMAXTIME; - - if (!timeout) - timeout = TIMAXTIME; - return 0; } diff -urN linux-2.4.26/drivers/usb/vicam.c linux-2.4.27/drivers/usb/vicam.c --- linux-2.4.26/drivers/usb/vicam.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/usb/vicam.c 2004-08-07 16:26:05.818394606 -0700 @@ -481,6 +481,7 @@ struct video_capability b; DBG("VIDIOCGCAP\n"); + memset(&b, 0, sizeof(b)); strcpy(b.name, "ViCam-based Camera"); b.type = VID_TYPE_CAPTURE; b.channels = 1; diff -urN linux-2.4.26/drivers/video/fbmem.c linux-2.4.27/drivers/video/fbmem.c --- linux-2.4.26/drivers/video/fbmem.c 2004-02-18 05:36:31.000000000 -0800 +++ linux-2.4.27/drivers/video/fbmem.c 2004-08-07 16:26:05.819394647 -0700 @@ -404,7 +404,7 @@ static ssize_t fb_read(struct file *file, char *buf, size_t count, loff_t *ppos) { - unsigned long p = *ppos; + loff_t p = *ppos; struct inode *inode = file->f_dentry->d_inode; int fbidx = GET_FB_IDX(inode->i_rdev); struct fb_info *info = registered_fb[fbidx]; @@ -414,12 +414,13 @@ if (! fb || ! info->disp) return -ENODEV; + if (p < 0) + return -EINVAL; + fb->fb_get_fix(&fix,PROC_CONSOLE(info), info); if (p >= fix.smem_len) return 0; - if (count >= fix.smem_len) - count = fix.smem_len; - if (count + p > fix.smem_len) + if (count > fix.smem_len - p) count = fix.smem_len - p; if (count) { char *base_addr; @@ -428,7 +429,7 @@ count -= copy_to_user(buf, base_addr+p, count); if (!count) return -EFAULT; - *ppos += count; + *ppos = p + count; } return count; } @@ -436,7 +437,7 @@ static ssize_t fb_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { - unsigned long p = *ppos; + loff_t p = *ppos; struct inode *inode = file->f_dentry->d_inode; int fbidx = GET_FB_IDX(inode->i_rdev); struct fb_info *info = registered_fb[fbidx]; @@ -447,13 +448,14 @@ if (! fb || ! info->disp) return -ENODEV; + if (p < 0) + return -EINVAL; + fb->fb_get_fix(&fix, PROC_CONSOLE(info), info); if (p > fix.smem_len) return -ENOSPC; - if (count >= fix.smem_len) - count = fix.smem_len; err = 0; - if (count + p > fix.smem_len) { + if (count > fix.smem_len - p) { count = fix.smem_len - p; err = -ENOSPC; } @@ -462,7 +464,7 @@ base_addr = info->disp->screen_base; count -= copy_from_user(base_addr+p, buf, count); - *ppos += count; + *ppos = p + count; err = -EFAULT; } if (count) diff -urN linux-2.4.26/drivers/video/riva/accel.c linux-2.4.27/drivers/video/riva/accel.c --- linux-2.4.26/drivers/video/riva/accel.c 2003-06-13 07:51:37.000000000 -0700 +++ linux-2.4.27/drivers/video/riva/accel.c 2004-08-07 16:26:05.880397154 -0700 @@ -300,8 +300,8 @@ static inline void convert_bgcolor_16(u32 *col) { - *col = ((*col & 0x00007C00) << 9) - | ((*col & 0x000003E0) << 6) + *col = ((*col & 0x0000F800) << 8) + | ((*col & 0x000007E0) << 5) | ((*col & 0x0000001F) << 3) | 0xFF000000; } diff -urN linux-2.4.26/drivers/video/riva/fbdev.c linux-2.4.27/drivers/video/riva/fbdev.c --- linux-2.4.26/drivers/video/riva/fbdev.c 2003-06-13 07:51:37.000000000 -0700 +++ linux-2.4.27/drivers/video/riva/fbdev.c 2004-08-07 16:26:05.900397976 -0700 @@ -952,7 +952,7 @@ newmode.crtc[0x12] = Set8Bits (vDisplay); newmode.crtc[0x13] = ((width / 8) * ((bpp + 1) / 8)) & 0xFF; newmode.crtc[0x15] = Set8Bits (vBlankStart); - newmode.crtc[0x16] = Set8Bits (vBlankEnd); + newmode.crtc[0x16] = Set8Bits (vBlankEnd + 1); newmode.ext.bpp = bpp; newmode.ext.width = width; diff -urN linux-2.4.26/drivers/video/sis/300vtbl.h linux-2.4.27/drivers/video/sis/300vtbl.h --- linux-2.4.26/drivers/video/sis/300vtbl.h 2004-04-14 06:05:39.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/300vtbl.h 2004-08-07 16:26:05.903398099 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Register settings for SiS 300 series * @@ -75,93 +76,93 @@ static const SiS_ExtStruct SiS300_EModeIDTable[] = { - {0x6a,0x2212,0x0102,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00}, /* 800x600x? */ - {0x2e,0x0a1b,0x0101,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08}, - {0x2f,0x021b,0x0100,SIS_RI_640x400, 0x00,0x00,0x00,0x00,0x10}, /* 640x400x8 */ - {0x30,0x2a1b,0x0103,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00}, - {0x31,0x0a1b,0x0000,SIS_RI_720x480, 0x00,0x00,0x00,0x00,0x11}, /* 720x480x8 */ - {0x32,0x2a1b,0x0000,SIS_RI_720x576, 0x00,0x00,0x00,0x00,0x12}, /* 720x576x8 */ - {0x33,0x0a1d,0x0000,SIS_RI_720x480, 0x00,0x00,0x00,0x00,0x11}, /* 720x480x16 */ - {0x34,0x2a1d,0x0000,SIS_RI_720x576, 0x00,0x00,0x00,0x00,0x12}, /* 720x576x16 */ - {0x35,0x0a1f,0x0000,SIS_RI_720x480, 0x00,0x00,0x00,0x00,0x11}, /* 720x480x32 */ - {0x36,0x2a1f,0x0000,SIS_RI_720x576, 0x00,0x00,0x00,0x00,0x12}, /* 720x576x32 */ - {0x37,0x0212,0x0104,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13}, /* 1024x768x? */ - {0x38,0x0a1b,0x0105,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13}, /* 1024x768x8 */ - {0x3a,0x0e3b,0x0107,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, /* 1280x1024x8 */ - {0x3c,0x063b,0x0130,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e}, - {0x3d,0x067d,0x0131,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e}, - {0x40,0x921c,0x010d,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23}, /* 320x200x15 */ - {0x41,0x921d,0x010e,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23}, /* 320x200x16 */ - {0x43,0x0a1c,0x0110,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08}, - {0x44,0x0a1d,0x0111,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08}, - {0x46,0x2a1c,0x0113,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00}, /* 800x600x15 */ - {0x47,0x2a1d,0x0114,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00}, /* 800x600x16 */ - {0x49,0x0a3c,0x0116,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13}, - {0x4a,0x0a3d,0x0117,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13}, - {0x4c,0x0e7c,0x0119,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, - {0x4d,0x0e7d,0x011a,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, - {0x50,0x921b,0x0132,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x24}, /* 320x240x8 */ - {0x51,0xb21b,0x0133,SIS_RI_400x300, 0x00,0x00,0x00,0x00,0x25}, /* 400x300x8 */ - {0x52,0x921b,0x0134,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x26}, /* 512x384x8 */ - {0x56,0x921d,0x0135,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x24}, /* 320x240x16 */ - {0x57,0xb21d,0x0136,SIS_RI_400x300, 0x00,0x00,0x00,0x00,0x25}, /* 400x300x16 */ - {0x58,0x921d,0x0137,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x26}, /* 512x384x16 */ - {0x59,0x921b,0x0138,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23}, /* 320x200x8 */ - {0x5c,0x921f,0x0000,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x26}, /* 512x384x32 */ - {0x5d,0x021d,0x0139,SIS_RI_640x400, 0x00,0x00,0x00,0x00,0x10}, /* 640x400x16 */ - {0x5e,0x021f,0x0000,SIS_RI_640x400, 0x00,0x00,0x00,0x00,0x10}, /* 640x400x32 */ - {0x62,0x0a3f,0x013a,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08}, - {0x63,0x2a3f,0x013b,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00}, /* 800x600x32 */ - {0x64,0x0a7f,0x013c,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13}, - {0x65,0x0eff,0x013d,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, - {0x66,0x06ff,0x013e,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e}, - {0x68,0x067b,0x013f,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x27}, - {0x69,0x06fd,0x0140,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x27}, - {0x6b,0x07ff,0x0000,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x27}, - {0x6c,0x067b,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x28}, /* 2048x1536x8 - not in BIOS! */ - {0x6d,0x06fd,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x28}, /* 2048x1536x16 - not in BIOS! */ - {0x70,0x2a1b,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x00,0x2d}, /* 800x480x8 */ - {0x71,0x0a1b,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x30}, /* 1024x576x8 */ - {0x74,0x0a1d,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x30}, /* 1024x576x16 */ - {0x75,0x0e3d,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x33}, /* 1280x720x16 */ - {0x76,0x2a1f,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x00,0x2d}, /* 800x480x32 */ - {0x77,0x0a3f,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x30}, /* 1024x576x32 */ - {0x78,0x0eff,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x33}, /* 1280x720x32 */ - {0x79,0x0e3b,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x33}, /* 1280x720x8 */ - {0x7a,0x2a1d,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x00,0x2d}, /* 800x480x16 */ - {0x7c,0x0a3b,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x29}, /* 1280x960x8 */ - {0x7d,0x0a7d,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x29}, /* 1280x960x16 */ - {0x7e,0x0aff,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x29}, /* 1280x960x32 */ - {0x20,0x0a1b,0x0000,SIS_RI_1024x600, 0x00,0x00,0x00,0x00,0x2b}, /* 1024x600 */ - {0x21,0x0a3d,0x0000,SIS_RI_1024x600, 0x00,0x00,0x00,0x00,0x2b}, - {0x22,0x0a7f,0x0000,SIS_RI_1024x600, 0x00,0x00,0x00,0x00,0x2b}, - {0x23,0x0a1b,0x0000,SIS_RI_1152x768, 0x00,0x00,0x00,0x00,0x2c}, /* 1152x768 */ - {0x24,0x0a3d,0x0000,SIS_RI_1152x768, 0x00,0x00,0x00,0x00,0x2c}, - {0x25,0x0a7f,0x0000,SIS_RI_1152x768, 0x00,0x00,0x00,0x00,0x2c}, - {0x29,0x0e1b,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x36}, /* 1152x864 */ - {0x2a,0x0e3d,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x36}, - {0x2b,0x0e7f,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x36}, - {0x39,0x2a1b,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x38}, /* 848x480 */ - {0x3b,0x2a3d,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x38}, - {0x3e,0x2a7f,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x38}, - {0x3f,0x2a1b,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x3a}, /* 856x480 */ - {0x42,0x2a3d,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x3a}, - {0x45,0x2a7f,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x3a}, - {0x48,0x223b,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x3c}, /* 1360x768 */ - {0x4b,0x227d,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x3c}, - {0x4e,0x22ff,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x3c}, - {0x4f,0x921f,0x0000,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23}, /* 320x200x32 */ - {0x53,0x921f,0x0000,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x24}, /* 320x240x32 */ - {0x54,0xb21f,0x0000,SIS_RI_400x300, 0x00,0x00,0x00,0x00,0x25}, /* 400x300x32 */ - {0x55,0x2e3b,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x3d}, /* 1280x768 */ - {0x5a,0x2e7d,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x3d}, - {0x5b,0x2eff,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x3d}, - {0x5f,0x2a1b,0x0000,SIS_RI_768x576, 0x00,0x00,0x00,0x00,0x3e}, /* 768x576x8 */ - {0x60,0x2a1d,0x0000,SIS_RI_768x576, 0x00,0x00,0x00,0x00,0x3e}, /* 768x576x16 */ - {0x61,0x2a1f,0x0000,SIS_RI_768x576, 0x00,0x00,0x00,0x00,0x3e}, /* 768x576x32 */ - {0x67,0x2e3b,0x0000,SIS_RI_1360x1024,0x00,0x00,0x00,0x00,0x3f}, /* 1360x1024x8 (BARCO) */ - {0x6f,0x2e7d,0x0000,SIS_RI_1360x1024,0x00,0x00,0x00,0x00,0x3f}, /* 1360x1024x16 (BARCO) */ - {0x72,0x2eff,0x0000,SIS_RI_1360x1024,0x00,0x00,0x00,0x00,0x3f}, /* 1360x1024x32 (BARCO) */ + {0x6a,0x2212,0x0102,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00,-1}, /* 800x600x? */ + {0x2e,0x0a1b,0x0101,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08,-1}, + {0x2f,0x021b,0x0100,SIS_RI_640x400, 0x00,0x00,0x00,0x00,0x10,-1}, /* 640x400x8 */ + {0x30,0x2a1b,0x0103,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00,-1}, + {0x31,0x0a1b,0x0000,SIS_RI_720x480, 0x00,0x00,0x00,0x00,0x11,-1}, /* 720x480x8 */ + {0x32,0x2a1b,0x0000,SIS_RI_720x576, 0x00,0x00,0x00,0x00,0x12,-1}, /* 720x576x8 */ + {0x33,0x0a1d,0x0000,SIS_RI_720x480, 0x00,0x00,0x00,0x00,0x11,-1}, /* 720x480x16 */ + {0x34,0x2a1d,0x0000,SIS_RI_720x576, 0x00,0x00,0x00,0x00,0x12,-1}, /* 720x576x16 */ + {0x35,0x0a1f,0x0000,SIS_RI_720x480, 0x00,0x00,0x00,0x00,0x11,-1}, /* 720x480x32 */ + {0x36,0x2a1f,0x0000,SIS_RI_720x576, 0x00,0x00,0x00,0x00,0x12,-1}, /* 720x576x32 */ + {0x37,0x0212,0x0104,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13,-1}, /* 1024x768x? */ + {0x38,0x0a1b,0x0105,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13,-1}, /* 1024x768x8 */ + {0x3a,0x0e3b,0x0107,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a,-1}, /* 1280x1024x8 */ + {0x3c,0x063b,0x0130,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e,-1}, + {0x3d,0x067d,0x0131,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e,-1}, + {0x40,0x921c,0x010d,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23,-1}, /* 320x200x15 */ + {0x41,0x921d,0x010e,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23,-1}, /* 320x200x16 */ + {0x43,0x0a1c,0x0110,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08,-1}, + {0x44,0x0a1d,0x0111,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08,-1}, + {0x46,0x2a1c,0x0113,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00,-1}, /* 800x600x15 */ + {0x47,0x2a1d,0x0114,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00,-1}, /* 800x600x16 */ + {0x49,0x0a3c,0x0116,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13,-1}, + {0x4a,0x0a3d,0x0117,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13,-1}, + {0x4c,0x0e7c,0x0119,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a,-1}, + {0x4d,0x0e7d,0x011a,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a,-1}, + {0x50,0x921b,0x0132,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x24,-1}, /* 320x240x8 */ + {0x51,0xb21b,0x0133,SIS_RI_400x300, 0x00,0x00,0x00,0x00,0x25,-1}, /* 400x300x8 */ + {0x52,0x921b,0x0134,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x26,-1}, /* 512x384x8 */ + {0x56,0x921d,0x0135,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x24,-1}, /* 320x240x16 */ + {0x57,0xb21d,0x0136,SIS_RI_400x300, 0x00,0x00,0x00,0x00,0x25,-1}, /* 400x300x16 */ + {0x58,0x921d,0x0137,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x26,-1}, /* 512x384x16 */ + {0x59,0x921b,0x0138,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23,-1}, /* 320x200x8 */ + {0x5c,0x921f,0x0000,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x26,-1}, /* 512x384x32 */ + {0x5d,0x021d,0x0139,SIS_RI_640x400, 0x00,0x00,0x00,0x00,0x10,-1}, /* 640x400x16 */ + {0x5e,0x021f,0x0000,SIS_RI_640x400, 0x00,0x00,0x00,0x00,0x10,-1}, /* 640x400x32 */ + {0x62,0x0a3f,0x013a,SIS_RI_640x480, 0x00,0x00,0x00,0x00,0x08,-1}, + {0x63,0x2a3f,0x013b,SIS_RI_800x600, 0x00,0x00,0x00,0x00,0x00,-1}, /* 800x600x32 */ + {0x64,0x0a7f,0x013c,SIS_RI_1024x768, 0x00,0x00,0x00,0x00,0x13,-1}, + {0x65,0x0eff,0x013d,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a,-1}, + {0x66,0x06ff,0x013e,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e,-1}, + {0x68,0x067b,0x013f,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x27,-1}, + {0x69,0x06fd,0x0140,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x27,-1}, + {0x6b,0x07ff,0x0000,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x27,-1}, + {0x6c,0x067b,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x28,-1}, /* 2048x1536x8 - not in BIOS! */ + {0x6d,0x06fd,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x28,-1}, /* 2048x1536x16 - not in BIOS! */ + {0x70,0x2a1b,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x00,0x2d,-1}, /* 800x480x8 */ + {0x71,0x0a1b,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x30,-1}, /* 1024x576x8 */ + {0x74,0x0a1d,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x30,-1}, /* 1024x576x16 */ + {0x75,0x0e3d,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x33,-1}, /* 1280x720x16 */ + {0x76,0x2a1f,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x00,0x2d,-1}, /* 800x480x32 */ + {0x77,0x0a3f,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x30,-1}, /* 1024x576x32 */ + {0x78,0x0eff,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x33,-1}, /* 1280x720x32 */ + {0x79,0x0e3b,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x33,-1}, /* 1280x720x8 */ + {0x7a,0x2a1d,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x00,0x2d,-1}, /* 800x480x16 */ + {0x7c,0x0a3b,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x29,-1}, /* 1280x960x8 */ + {0x7d,0x0a7d,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x29,-1}, /* 1280x960x16 */ + {0x7e,0x0aff,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x29,-1}, /* 1280x960x32 */ + {0x20,0x0a1b,0x0000,SIS_RI_1024x600, 0x00,0x00,0x00,0x00,0x2b,-1}, /* 1024x600 */ + {0x21,0x0a3d,0x0000,SIS_RI_1024x600, 0x00,0x00,0x00,0x00,0x2b,-1}, + {0x22,0x0a7f,0x0000,SIS_RI_1024x600, 0x00,0x00,0x00,0x00,0x2b,-1}, + {0x23,0x0a1b,0x0000,SIS_RI_1152x768, 0x00,0x00,0x00,0x00,0x2c,-1}, /* 1152x768 */ + {0x24,0x0a3d,0x0000,SIS_RI_1152x768, 0x00,0x00,0x00,0x00,0x2c,-1}, + {0x25,0x0a7f,0x0000,SIS_RI_1152x768, 0x00,0x00,0x00,0x00,0x2c,-1}, + {0x29,0x0e1b,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x36,-1}, /* 1152x864 */ + {0x2a,0x0e3d,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x36,-1}, + {0x2b,0x0e7f,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x36,-1}, + {0x39,0x2a1b,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x38,-1}, /* 848x480 */ + {0x3b,0x2a3d,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x38,-1}, + {0x3e,0x2a7f,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x38,-1}, + {0x3f,0x2a1b,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x3a,-1}, /* 856x480 */ + {0x42,0x2a3d,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x3a,-1}, + {0x45,0x2a7f,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x3a,-1}, + {0x48,0x223b,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x3c,-1}, /* 1360x768 */ + {0x4b,0x227d,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x3c,-1}, + {0x4e,0x22ff,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x3c,-1}, + {0x4f,0x921f,0x0000,SIS_RI_320x200, 0x00,0x00,0x00,0x00,0x23,-1}, /* 320x200x32 */ + {0x53,0x921f,0x0000,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x24,-1}, /* 320x240x32 */ + {0x54,0xb21f,0x0000,SIS_RI_400x300, 0x00,0x00,0x00,0x00,0x25,-1}, /* 400x300x32 */ + {0x55,0x2e3b,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x3d,-1}, /* 1280x768 */ + {0x5a,0x2e7d,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x3d,-1}, + {0x5b,0x2eff,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x3d,-1}, + {0x5f,0x2a1b,0x0000,SIS_RI_768x576, 0x00,0x00,0x00,0x00,0x3e,-1}, /* 768x576x8 */ + {0x60,0x2a1d,0x0000,SIS_RI_768x576, 0x00,0x00,0x00,0x00,0x3e,-1}, /* 768x576x16 */ + {0x61,0x2a1f,0x0000,SIS_RI_768x576, 0x00,0x00,0x00,0x00,0x3e,-1}, /* 768x576x32 */ + {0x67,0x2e3b,0x0000,SIS_RI_1360x1024,0x00,0x00,0x00,0x00,0x3f,-1}, /* 1360x1024x8 (BARCO) */ + {0x6f,0x2e7d,0x0000,SIS_RI_1360x1024,0x00,0x00,0x00,0x00,0x3f,-1}, /* 1360x1024x16 (BARCO) */ + {0x72,0x2eff,0x0000,SIS_RI_1360x1024,0x00,0x00,0x00,0x00,0x3f,-1}, /* 1360x1024x32 (BARCO) */ {0xff,0x0000,0xffff,0, 0x00,0x00,0x00,0x00,0x00} }; diff -urN linux-2.4.26/drivers/video/sis/310vtbl.h linux-2.4.27/drivers/video/sis/310vtbl.h --- linux-2.4.26/drivers/video/sis/310vtbl.h 2004-04-14 06:05:39.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/310vtbl.h 2004-08-07 16:26:05.906398222 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Register settings for SiS 315/330 series * @@ -75,97 +76,97 @@ static const SiS_ExtStruct SiS310_EModeIDTable[]= { - {0x6a,0x2212,0x0102,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00}, /* 800x600x? */ - {0x2e,0x0a1b,0x0101,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08}, /* 640x480x8 */ - {0x2f,0x0a1b,0x0100,SIS_RI_640x400, 0x00,0x00,0x05,0x05,0x10}, /* 640x400x8 */ - {0x30,0x2a1b,0x0103,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00}, /* 800x600x8 */ - {0x31,0x0a1b,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11}, /* 720x480x8 */ - {0x32,0x0a1b,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12}, /* 720x576x8 */ - {0x33,0x0a1d,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11}, /* 720x480x16 */ - {0x34,0x2a1d,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12}, /* 720x576x16 */ - {0x35,0x0a1f,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11}, /* 720x480x32 */ - {0x36,0x2a1f,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12}, /* 720x576x32 */ - {0x37,0x0212,0x0104,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13}, /* 1024x768x? */ - {0x38,0x0a1b,0x0105,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13}, /* 1024x768x8 */ - {0x3a,0x0e3b,0x0107,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, /* 1280x1024x8 */ - {0x3c,0x0e3b,0x0130,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e}, /* 1600x1200x8 */ - {0x3d,0x0e7d,0x0131,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e}, /* 1600x1200x16 */ - {0x40,0x9a1c,0x010d,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25}, /* 320x200x15 */ - {0x41,0x9a1d,0x010e,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25}, /* 320x200x16 */ - {0x43,0x0a1c,0x0110,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08}, - {0x44,0x0a1d,0x0111,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08}, /* 640x480x16 */ - {0x46,0x2a1c,0x0113,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00}, - {0x47,0x2a1d,0x0114,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00}, /* 800x600x16 */ - {0x49,0x0a3c,0x0116,SIS_RI_1024x768, 0x00,0x00,0x00,0x07,0x13}, - {0x4a,0x0a3d,0x0117,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13}, /* 1024x768x16 */ - {0x4c,0x0e7c,0x0119,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, - {0x4d,0x0e7d,0x011a,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, /* 1280x1024x16 */ - {0x50,0x9a1b,0x0132,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x26}, /* 320x240x8 */ - {0x51,0xba1b,0x0133,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x27}, /* 400x300x8 */ - {0x52,0xba1b,0x0134,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x28}, /* 512x384x8 */ - {0x56,0x9a1d,0x0135,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x26}, /* 320x240x16 */ - {0x57,0xba1d,0x0136,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x27}, /* 400x300x16 */ - {0x58,0xba1d,0x0137,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x28}, /* 512x384x16 */ - {0x59,0x9a1b,0x0138,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25}, /* 320x200x8 */ - {0x5a,0x021b,0x0138,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x3f}, /* 320x240x8 fstn */ - {0x5b,0x0a1d,0x0135,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x3f}, /* 320x240x16 fstn */ - {0x5c,0xba1f,0x0000,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x28}, /* 512x384x32 */ - {0x5d,0x0a1d,0x0139,SIS_RI_640x400, 0x00,0x00,0x05,0x07,0x10}, - {0x5e,0x0a1f,0x0000,SIS_RI_640x400, 0x00,0x00,0x05,0x07,0x10}, /* 640x400x32 */ - {0x62,0x0a3f,0x013a,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08}, /* 640x480x32 */ - {0x63,0x2a3f,0x013b,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00}, /* 800x600x32 */ - {0x64,0x0a7f,0x013c,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13}, /* 1024x768x32 */ - {0x65,0x0eff,0x013d,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a}, /* 1280x1024x32 */ - {0x66,0x0eff,0x013e,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e}, /* 1600x1200x32 */ - {0x68,0x067b,0x013f,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x29}, /* 1920x1440x8 */ - {0x69,0x06fd,0x0140,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x29}, /* 1920x1440x16 */ - {0x6b,0x07ff,0x0141,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x29}, /* 1920x1440x32 */ - {0x6c,0x067b,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x2f}, /* 2048x1536x8 */ - {0x6d,0x06fd,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x2f}, /* 2048x1536x16 */ - {0x6e,0x07ff,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x2f}, /* 2048x1536x32 */ - {0x70,0x2a1b,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x34}, /* 800x480x8 */ - {0x71,0x0a1b,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x37}, /* 1024x576x8 */ - {0x74,0x0a1d,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x37}, /* 1024x576x16 */ - {0x75,0x0a3d,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x3a}, /* 1280x720x16 */ - {0x76,0x2a1f,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x34}, /* 800x480x32 */ - {0x77,0x0a1f,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x37}, /* 1024x576x32 */ - {0x78,0x0a3f,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x3a}, /* 1280x720x32 */ - {0x79,0x0a3b,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x3a}, /* 1280x720x8 */ - {0x7a,0x2a1d,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x34}, /* 800x480x16 */ - {0x7c,0x0e3b,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x3d}, /* 1280x960x8 */ - {0x7d,0x0e7d,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x3d}, /* 1280x960x16 */ - {0x7e,0x0eff,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x3d}, /* 1280x960x32 */ - {0x23,0x0e3b,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x40}, /* 1280x768x8 */ - {0x24,0x0e7d,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x40}, /* 1280x768x16 */ - {0x25,0x0eff,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x40}, /* 1280x768x32 */ - {0x26,0x0e3b,0x0000,SIS_RI_1400x1050,0x00,0x00,0x00,0x00,0x41}, /* 1400x1050x8 */ - {0x27,0x0e7d,0x0000,SIS_RI_1400x1050,0x00,0x00,0x00,0x00,0x41}, /* 1400x1050x16 */ - {0x28,0x0eff,0x0000,SIS_RI_1400x1050,0x00,0x00,0x00,0x00,0x41}, /* 1400x1050x32*/ - {0x29,0x0e1b,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x43}, /* 1152x864 */ - {0x2a,0x0e3d,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x43}, - {0x2b,0x0e7f,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x43}, - {0x39,0x2a1b,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x45}, /* 848x480 */ - {0x3b,0x2a3d,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x45}, - {0x3e,0x2a7f,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x45}, - {0x3f,0x2a1b,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x47}, /* 856x480 */ - {0x42,0x2a3d,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x47}, - {0x45,0x2a7f,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x47}, - {0x48,0x2a1b,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x49}, /* 1360x768 */ - {0x4b,0x2a3d,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x49}, - {0x4e,0x2a7f,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x49}, - {0x4f,0x9a1f,0x0000,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25}, /* 320x200x32 */ - {0x53,0x9a1f,0x0000,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x26}, /* 320x240x32 */ - {0x54,0xba1f,0x0000,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x27}, /* 400x300x32 */ - {0x5f,0x2a1b,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x4a}, /* 768x576 */ - {0x60,0x2a1d,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x4a}, - {0x61,0x2a1f,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x4a}, - {0x14,0x0e1b,0x0000,SIS_RI_1280x800, 0x00,0x00,0x00,0x00,0x4b}, /* 1280x800 */ - {0x15,0x0e3d,0x0000,SIS_RI_1280x800, 0x00,0x00,0x00,0x00,0x4b}, - {0x16,0x0e7f,0x0000,SIS_RI_1280x800, 0x00,0x00,0x00,0x00,0x4b}, - {0x17,0x0e1b,0x0000,SIS_RI_1680x1050,0x00,0x00,0x00,0x00,0x4c}, /* 1680x1050 */ - {0x18,0x0e3d,0x0000,SIS_RI_1680x1050,0x00,0x00,0x00,0x00,0x4c}, - {0x19,0x0e7f,0x0000,SIS_RI_1680x1050,0x00,0x00,0x00,0x00,0x4c}, - {0xff,0x0000,0x0000,0, 0x00,0x00,0x00,0x00,0x00} + {0x6a,0x2212,0x0102,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x? */ + {0x2e,0x0a1b,0x0101,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, /* 640x480x8 */ + {0x2f,0x0a1b,0x0100,SIS_RI_640x400, 0x00,0x00,0x05,0x05,0x10, 0}, /* 640x400x8 */ + {0x30,0x2a1b,0x0103,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x8 */ + {0x31,0x4a1b,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11,-1}, /* 720x480x8 */ + {0x32,0x4a1b,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12,-1}, /* 720x576x8 */ + {0x33,0x4a1d,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11,-1}, /* 720x480x16 */ + {0x34,0x6a1d,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12,-1}, /* 720x576x16 */ + {0x35,0x4a1f,0x0000,SIS_RI_720x480, 0x00,0x00,0x06,0x06,0x11,-1}, /* 720x480x32 */ + {0x36,0x6a1f,0x0000,SIS_RI_720x576, 0x00,0x00,0x06,0x06,0x12,-1}, /* 720x576x32 */ + {0x37,0x0212,0x0104,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x? */ + {0x38,0x0a1b,0x0105,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x8 */ + {0x3a,0x0e3b,0x0107,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a, 8}, /* 1280x1024x8 */ + {0x3c,0x0e3b,0x0130,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e,10}, /* 1600x1200x8 */ + {0x3d,0x0e7d,0x0131,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e,10}, /* 1600x1200x16 */ + {0x40,0x9a1c,0x010d,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25, 0}, /* 320x200x15 */ + {0x41,0x9a1d,0x010e,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25, 0}, /* 320x200x16 */ + {0x43,0x0a1c,0x0110,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, + {0x44,0x0a1d,0x0111,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, /* 640x480x16 */ + {0x46,0x2a1c,0x0113,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, + {0x47,0x2a1d,0x0114,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x16 */ + {0x49,0x0a3c,0x0116,SIS_RI_1024x768, 0x00,0x00,0x00,0x07,0x13, 4}, + {0x4a,0x0a3d,0x0117,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x16 */ + {0x4c,0x0e7c,0x0119,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a, 8}, + {0x4d,0x0e7d,0x011a,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a, 8}, /* 1280x1024x16 */ + {0x50,0x9a1b,0x0132,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x26, 2}, /* 320x240x8 */ + {0x51,0xba1b,0x0133,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x27, 3}, /* 400x300x8 */ + {0x52,0xba1b,0x0134,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x28, 4}, /* 512x384x8 */ + {0x56,0x9a1d,0x0135,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x26, 2}, /* 320x240x16 */ + {0x57,0xba1d,0x0136,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x27, 3}, /* 400x300x16 */ + {0x58,0xba1d,0x0137,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x28, 4}, /* 512x384x16 */ + {0x59,0x9a1b,0x0138,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25, 0}, /* 320x200x8 */ + {0x5a,0x021b,0x0138,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x3f, 2}, /* 320x240x8 fstn */ + {0x5b,0x0a1d,0x0135,SIS_RI_320x240, 0x00,0x00,0x00,0x00,0x3f, 2}, /* 320x240x16 fstn */ + {0x5c,0xba1f,0x0000,SIS_RI_512x384, 0x00,0x00,0x00,0x00,0x28, 4}, /* 512x384x32 */ + {0x5d,0x0a1d,0x0139,SIS_RI_640x400, 0x00,0x00,0x05,0x07,0x10, 0}, + {0x5e,0x0a1f,0x0000,SIS_RI_640x400, 0x00,0x00,0x05,0x07,0x10, 0}, /* 640x400x32 */ + {0x62,0x0a3f,0x013a,SIS_RI_640x480, 0x00,0x00,0x05,0x05,0x08, 2}, /* 640x480x32 */ + {0x63,0x2a3f,0x013b,SIS_RI_800x600, 0x00,0x00,0x07,0x06,0x00, 3}, /* 800x600x32 */ + {0x64,0x0a7f,0x013c,SIS_RI_1024x768, 0x00,0x00,0x08,0x07,0x13, 4}, /* 1024x768x32 */ + {0x65,0x0eff,0x013d,SIS_RI_1280x1024,0x00,0x00,0x00,0x00,0x1a, 8}, /* 1280x1024x32 */ + {0x66,0x0eff,0x013e,SIS_RI_1600x1200,0x00,0x00,0x00,0x00,0x1e,10}, /* 1600x1200x32 */ + {0x68,0x067b,0x013f,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x29,-1}, /* 1920x1440x8 */ + {0x69,0x06fd,0x0140,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x29,-1}, /* 1920x1440x16 */ + {0x6b,0x07ff,0x0141,SIS_RI_1920x1440,0x00,0x00,0x00,0x00,0x29,-1}, /* 1920x1440x32 */ + {0x6c,0x067b,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x2f,-1}, /* 2048x1536x8 */ + {0x6d,0x06fd,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x2f,-1}, /* 2048x1536x16 */ + {0x6e,0x07ff,0x0000,SIS_RI_2048x1536,0x00,0x00,0x00,0x00,0x2f,-1}, /* 2048x1536x32 */ + {0x70,0x6a1b,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x34,-1}, /* 800x480x8 */ + {0x71,0x4a1b,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x37,-1}, /* 1024x576x8 */ + {0x74,0x4a1d,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x37,-1}, /* 1024x576x16 */ + {0x75,0x0a3d,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x3a, 5}, /* 1280x720x16 */ + {0x76,0x6a1f,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x34,-1}, /* 800x480x32 */ + {0x77,0x4a1f,0x0000,SIS_RI_1024x576, 0x00,0x00,0x00,0x00,0x37,-1}, /* 1024x576x32 */ + {0x78,0x0a3f,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x3a, 5}, /* 1280x720x32 */ + {0x79,0x0a3b,0x0000,SIS_RI_1280x720, 0x00,0x00,0x00,0x00,0x3a, 5}, /* 1280x720x8 */ + {0x7a,0x6a1d,0x0000,SIS_RI_800x480, 0x00,0x00,0x07,0x07,0x34,-1}, /* 800x480x16 */ + {0x7c,0x0e3b,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x3d,-1}, /* 1280x960x8 */ + {0x7d,0x0e7d,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x3d,-1}, /* 1280x960x16 */ + {0x7e,0x0eff,0x0000,SIS_RI_1280x960, 0x00,0x00,0x00,0x00,0x3d,-1}, /* 1280x960x32 */ + {0x23,0x0e3b,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x40, 6}, /* 1280x768x8 */ + {0x24,0x0e7d,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x40, 6}, /* 1280x768x16 */ + {0x25,0x0eff,0x0000,SIS_RI_1280x768, 0x00,0x00,0x00,0x00,0x40, 6}, /* 1280x768x32 */ + {0x26,0x0e3b,0x0000,SIS_RI_1400x1050,0x00,0x00,0x00,0x00,0x41, 9}, /* 1400x1050x8 */ + {0x27,0x0e7d,0x0000,SIS_RI_1400x1050,0x00,0x00,0x00,0x00,0x41, 9}, /* 1400x1050x16 */ + {0x28,0x0eff,0x0000,SIS_RI_1400x1050,0x00,0x00,0x00,0x00,0x41, 9}, /* 1400x1050x32*/ + {0x29,0x4e1b,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x43,-1}, /* 1152x864 */ + {0x2a,0x4e3d,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x43,-1}, + {0x2b,0x4e7f,0x0000,SIS_RI_1152x864, 0x00,0x00,0x00,0x00,0x43,-1}, + {0x39,0x6a1b,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x45,-1}, /* 848x480 */ + {0x3b,0x6a3d,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x45,-1}, + {0x3e,0x6a7f,0x0000,SIS_RI_848x480, 0x00,0x00,0x00,0x00,0x45,-1}, + {0x3f,0x6a1b,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x47,-1}, /* 856x480 */ + {0x42,0x6a3d,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x47,-1}, + {0x45,0x6a7f,0x0000,SIS_RI_856x480, 0x00,0x00,0x00,0x00,0x47,-1}, + {0x48,0x6a1b,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x49,-1}, /* 1360x768 */ + {0x4b,0x6a3d,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x49,-1}, + {0x4e,0x6a7f,0x0000,SIS_RI_1360x768, 0x00,0x00,0x00,0x00,0x49,-1}, + {0x4f,0x9a1f,0x0000,SIS_RI_320x200, 0x00,0x00,0x04,0x04,0x25, 0}, /* 320x200x32 */ + {0x53,0x9a1f,0x0000,SIS_RI_320x240, 0x00,0x00,0x04,0x04,0x26, 2}, /* 320x240x32 */ + {0x54,0xba1f,0x0000,SIS_RI_400x300, 0x00,0x00,0x07,0x07,0x27, 3}, /* 400x300x32 */ + {0x5f,0x6a1b,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x4a,-1}, /* 768x576 */ + {0x60,0x6a1d,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x4a,-1}, + {0x61,0x6a1f,0x0000,SIS_RI_768x576, 0x00,0x00,0x06,0x06,0x4a,-1}, + {0x14,0x0e1b,0x0000,SIS_RI_1280x800, 0x00,0x00,0x00,0x00,0x4b, 7}, /* 1280x800 */ + {0x15,0x0e3d,0x0000,SIS_RI_1280x800, 0x00,0x00,0x00,0x00,0x4b, 7}, + {0x16,0x0e7f,0x0000,SIS_RI_1280x800, 0x00,0x00,0x00,0x00,0x4b, 7}, + {0x17,0x0e1b,0x0000,SIS_RI_1680x1050,0x00,0x00,0x00,0x00,0x4c, 9}, /* 1680x1050 */ + {0x18,0x0e3d,0x0000,SIS_RI_1680x1050,0x00,0x00,0x00,0x00,0x4c, 9}, + {0x19,0x0e7f,0x0000,SIS_RI_1680x1050,0x00,0x00,0x00,0x00,0x4c, 9}, + {0xff,0x0000,0x0000,0, 0x00,0x00,0x00,0x00,0x00,-1} }; static const SiS_Ext2Struct SiS310_RefIndex[]= @@ -187,8 +188,8 @@ {0xc047,0x0b,0x0e,0x00,0x04,0x2e, 640, 480, 0x40}, /* 0xe */ {0xc047,0x0c,0x15,0x00,0x04,0x2e, 640, 480, 0x40}, /* 0xf */ {0x487f,0x04,0x00,0x00,0x00,0x2f, 640, 400, 0x30}, /* 0x10 */ - {0xc04f,0x3c,0x01,0x06,0x00,0x31, 720, 480, 0x30}, /* 0x11 */ - {0x004f,0x3d,0x03,0x06,0x00,0x32, 720, 576, 0x30}, /* 0x12 */ + {0xc06f,0x3c,0x01,0x06,0x13,0x31, 720, 480, 0x30}, /* 0x11 */ + {0x006f,0x3d,0x03,0x06,0x14,0x32, 720, 576, 0x30}, /* 0x12 */ {0x0087,0x15,0x06,0x00,0x06,0x37,1024, 768, 0x30}, /* 0x13 */ {0xc877,0x16,0x0b,0x06,0x06,0x37,1024, 768, 0x20}, /* 0x14 */ {0xc067,0x17,0x0f,0x49,0x06,0x37,1024, 768, 0x20}, /* 0x15 */ @@ -222,12 +223,12 @@ {0x4007,0x2f,0x33,0x00,0x00,0x6c,2048,1536, 0x00}, /* 0x31 */ {0x4007,0x30,0x37,0x00,0x00,0x6c,2048,1536, 0x00}, /* 0x32 */ {0x4005,0x31,0x38,0x00,0x00,0x6c,2048,1536, 0x00}, /* 0x33 */ - {0x0057,0x32,0x40,0x08,0x00,0x70, 800, 480, 0x30}, /* 0x34 */ - {0x0047,0x33,0x07,0x08,0x00,0x70, 800, 480, 0x30}, /* 0x35 */ - {0x0047,0x34,0x0a,0x08,0x00,0x70, 800, 480, 0x30}, /* 0x36 */ - {0x0057,0x35,0x0b,0x09,0x00,0x71,1024, 576, 0x30}, /* 0x37 */ - {0x0047,0x36,0x11,0x09,0x00,0x71,1024, 576, 0x30}, /* 0x38 */ - {0x0047,0x37,0x16,0x09,0x00,0x71,1024, 576, 0x30}, /* 0x39 */ + {0x0077,0x32,0x40,0x08,0x18,0x70, 800, 480, 0x30}, /* 0x34 */ + {0x0047,0x33,0x07,0x08,0x18,0x70, 800, 480, 0x30}, /* 0x35 */ + {0x0047,0x34,0x0a,0x08,0x18,0x70, 800, 480, 0x30}, /* 0x36 */ + {0x0077,0x35,0x0b,0x09,0x19,0x71,1024, 576, 0x30}, /* 0x37 */ + {0x0047,0x36,0x11,0x09,0x19,0x71,1024, 576, 0x30}, /* 0x38 */ + {0x0047,0x37,0x16,0x09,0x19,0x71,1024, 576, 0x30}, /* 0x39 */ {0x1137,0x38,0x19,0x0a,0x0c,0x75,1280, 720, 0x30}, /* 0x3a */ {0x1107,0x39,0x1e,0x0a,0x0c,0x75,1280, 720, 0x30}, /* 0x3b */ {0x1307,0x3a,0x20,0x0a,0x0c,0x75,1280, 720, 0x30}, /* 0x3c */ @@ -237,14 +238,14 @@ {0x0077,0x42,0x5b,0x08,0x11,0x23,1280, 768, 0x30}, /* 0x40 */ /* 0x5b was 0x12 */ {0x0127,0x43,0x4d,0x08,0x0b,0x26,1400,1050, 0x30}, /* 0x41 */ {0x0207,0x4b,0x5a,0x08,0x0b,0x26,1400,1050, 0x30}, /* 0x42 1400x1050-75Hz */ - {0x0107,0x44,0x19,0x00,0x00,0x29,1152, 864, 0x30}, /* 0x43 1152x864-75Hz */ - {0x0107,0x4a,0x1e,0x00,0x00,0x29,1152, 864, 0x30}, /* 0x44 1152x864-85Hz */ - {0x0087,0x45,0x57,0x00,0x00,0x39, 848, 480, 0x30}, /* 0x45 848x480-38Hzi */ - {0xc067,0x46,0x55,0x0b,0x00,0x39, 848, 480, 0x30}, /* 0x46 848x480-60Hz */ - {0x0087,0x47,0x57,0x00,0x00,0x3f, 856, 480, 0x30}, /* 0x47 856x480-38Hzi */ - {0xc047,0x48,0x57,0x00,0x00,0x3f, 856, 480, 0x30}, /* 0x48 856x480-60Hz */ - {0x0067,0x49,0x58,0x0c,0x00,0x48,1360, 768, 0x30}, /* 0x49 1360x768-60Hz */ - {0x004f,0x4d,0x03,0x06,0x00,0x5f, 768, 576, 0x30}, /* 0x4a 768x576-56Hz */ + {0x0127,0x44,0x19,0x00,0x1a,0x29,1152, 864, 0x30}, /* 0x43 1152x864-75Hz */ + {0x0127,0x4a,0x1e,0x00,0x1a,0x29,1152, 864, 0x30}, /* 0x44 1152x864-85Hz */ + {0x0087,0x45,0x57,0x00,0x16,0x39, 848, 480, 0x30}, /* 0x45 848x480-38Hzi */ + {0xc067,0x46,0x55,0x0b,0x16,0x39, 848, 480, 0x30}, /* 0x46 848x480-60Hz */ + {0x0087,0x47,0x57,0x00,0x17,0x3f, 856, 480, 0x30}, /* 0x47 856x480-38Hzi */ + {0xc067,0x48,0x57,0x00,0x17,0x3f, 856, 480, 0x30}, /* 0x48 856x480-60Hz */ + {0x0067,0x49,0x58,0x0c,0x1b,0x48,1360, 768, 0x30}, /* 0x49 1360x768-60Hz */ + {0x006f,0x4d,0x03,0x06,0x15,0x5f, 768, 576, 0x30}, /* 0x4a 768x576-56Hz */ {0x0067,0x4f,0x5c,0x08,0x0d,0x14,1280, 800, 0x30}, /* 0x4b 1280x800-60Hz */ {0x0067,0x50,0x5d,0x0c,0x0e,0x17,1680,1050, 0x30}, /* 0x4c 1680x1050-60Hz */ {0xffff,0x00,0x00,0x00,0x00,0x00, 0, 0, 0} @@ -685,7 +686,12 @@ { 0x63,0x46, 68}, /* 0x60 1280x768_2 */ { 0x31,0x42, 79}, /* 0x61 1280x768_3 - temp */ { 0, 0, 0}, /* 0x62 - custom (will be filled out at run-time) */ - { 0x5a,0x64, 65} /* 0x63 1280x720 (LCD LVDS) */ + { 0x5a,0x64, 65}, /* 0x63 1280x720 (LCD LVDS) */ + { 0x70,0x28, 90}, /* 0x64 1152x864@60 */ + { 0x41,0xc4, 32}, /* 0x65 848x480@60 */ + { 0x5c,0xc6, 32}, /* 0x66 856x480@60 */ + { 0x76,0xe7, 27}, /* 0x67 720x480@60 */ + { 0x5f,0xc6, 33}, /* 0x68 720/768x576@60 */ }; static SiS_VBVCLKDataStruct SiS310_VBVCLKData[]= @@ -788,14 +794,19 @@ { 0x52,0x07,149}, /* 0x59 1280x960-85 */ { 0x56,0x07,156}, /* 0x5a 1400x1050-75 */ { 0x70,0x29, 81}, /* 0x5b 1280x768 LCD */ - { 0x9c,0x62, 69}, /* 0x5c 1280x800 LCD - wrong? */ + { 0x45,0x25, 83}, /* 0x5c 1280x800 LCD - (was 0x9c,0x62, 69 - wrong?) */ { 0xbe,0x44,121}, /* 0x5d 1680x1050 LCD */ { 0x70,0x24,162}, /* 0x5e 1600x1200 LCD */ { 0x52,0x27, 75}, /* 0x5f 1280x720 LCD TMDS + HDTV (correct) */ { 0x63,0x46, 68}, /* 0x60 1280x768_2 */ { 0x31,0x42, 79}, /* 0x61 1280x768_3 - temp */ { 0, 0, 0}, /* 0x62 - custom (will be filled out at run-time) */ - { 0x5a,0x64, 65} /* 0x63 1280x720 (LCD LVDS) */ + { 0x5a,0x64, 65}, /* 0x63 1280x720 (LCD LVDS) */ + { 0x70,0x28, 90}, /* 0x64 1152x864@60 */ + { 0x41,0xc4, 32}, /* 0x65 848x480@60 */ + { 0x5c,0xc6, 32}, /* 0x66 856x480@60 */ + { 0x76,0xe7, 27}, /* 0x67 720x480@60 */ + { 0x5f,0xc6, 33}, /* 0x68 720/768x576@60 */ }; static const DRAM4Type SiS310_SR15[8] = { diff -urN linux-2.4.26/drivers/video/sis/init.c linux-2.4.27/drivers/video/sis/init.c --- linux-2.4.26/drivers/video/sis/init.c 2004-04-14 06:05:39.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/init.c 2004-08-07 16:26:05.911398428 -0700 @@ -1,8 +1,9 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Mode initializing code (CRT1 section) for * for SiS 300/305/540/630/730 and - * SiS 315/550/650/M650/651/661FX/M661FX/740/741/M741/330/660/M660/760/M760 + * SiS 315/550/650/M650/651/661FX/M661FX/740/741(GX)/M741/330/660/M660/760/M760 * (Universal module for Linux kernel framebuffer and XFree86 4.x) * * Copyright (C) 2001-2004 by Thomas Winischhofer, Vienna, Austria @@ -635,47 +636,33 @@ else if(VDisplay == 400) ModeIndex = ModeIndex_640x400[Depth]; break; case 720: - if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 480) ModeIndex = ModeIndex_720x480[Depth]; - else if(VDisplay == 576) ModeIndex = ModeIndex_720x576[Depth]; - } + if(VDisplay == 480) ModeIndex = ModeIndex_720x480[Depth]; + else if(VDisplay == 576) ModeIndex = ModeIndex_720x576[Depth]; break; case 768: - if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 576) ModeIndex = ModeIndex_768x576[Depth]; - } + if(VDisplay == 576) ModeIndex = ModeIndex_768x576[Depth]; break; case 800: - if(VDisplay == 600) ModeIndex = ModeIndex_800x600[Depth]; - else if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 480) ModeIndex = ModeIndex_800x480[Depth]; - } + if(VDisplay == 600) ModeIndex = ModeIndex_800x600[Depth]; + else if(VDisplay == 480) ModeIndex = ModeIndex_800x480[Depth]; break; case 848: - if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 480) ModeIndex = ModeIndex_848x480[Depth]; - } + if(VDisplay == 480) ModeIndex = ModeIndex_848x480[Depth]; break; case 856: - if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 480) ModeIndex = ModeIndex_856x480[Depth]; - } + if(VDisplay == 480) ModeIndex = ModeIndex_856x480[Depth]; break; case 1024: - if(VDisplay == 768) ModeIndex = ModeIndex_1024x768[Depth]; - else if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 576) ModeIndex = ModeIndex_1024x576[Depth]; - else if(VGAEngine == SIS_300_VGA) { - if(VDisplay == 600) ModeIndex = ModeIndex_1024x600[Depth]; - } + if(VDisplay == 768) ModeIndex = ModeIndex_1024x768[Depth]; + else if(VDisplay == 576) ModeIndex = ModeIndex_1024x576[Depth]; + else if((!(VBFlags & CRT1_LCDA)) && (VGAEngine == SIS_300_VGA)) { + if(VDisplay == 600) ModeIndex = ModeIndex_1024x600[Depth]; } break; case 1152: - if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 864) ModeIndex = ModeIndex_1152x864[Depth]; - else if(VGAEngine == SIS_300_VGA) { - if(VDisplay == 768) ModeIndex = ModeIndex_1152x768[Depth]; - } + if(VDisplay == 864) ModeIndex = ModeIndex_1152x864[Depth]; + if((!(VBFlags & CRT1_LCDA)) && (VGAEngine == SIS_300_VGA)) { + if(VDisplay == 768) ModeIndex = ModeIndex_1152x768[Depth]; } break; case 1280: @@ -695,7 +682,7 @@ ModeIndex = ModeIndex_1280x720[Depth]; } } else if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 960) ModeIndex = ModeIndex_1280x960[Depth]; + if(VDisplay == 960) ModeIndex = ModeIndex_1280x960[Depth]; else if(VDisplay == 768) { if(VGAEngine == SIS_300_VGA) { ModeIndex = ModeIndex_300_1280x768[Depth]; @@ -706,9 +693,9 @@ } break; case 1360: + if(VDisplay == 768) ModeIndex = ModeIndex_1360x768[Depth]; if(!(VBFlags & CRT1_LCDA)) { - if(VDisplay == 768) ModeIndex = ModeIndex_1360x768[Depth]; - else if(VGAEngine == SIS_300_VGA) { + if(VGAEngine == SIS_300_VGA) { if(VDisplay == 1024) ModeIndex = ModeIndex_300_1360x1024[Depth]; } } @@ -874,17 +861,49 @@ if(VDisplay == 480) ModeIndex = ModeIndex_640x480[Depth]; else if(VDisplay == 400) ModeIndex = ModeIndex_640x400[Depth]; break; + case 720: + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 480) ModeIndex = ModeIndex_720x480[Depth]; + else if(VDisplay == 576) ModeIndex = ModeIndex_720x576[Depth]; + } + break; + case 768: + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 576) ModeIndex = ModeIndex_768x576[Depth]; + } + break; case 800: if(VDisplay == 600) ModeIndex = ModeIndex_800x600[Depth]; + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 480) ModeIndex = ModeIndex_800x480[Depth]; + } + break; + case 848: + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 480) ModeIndex = ModeIndex_848x480[Depth]; + } + break; + case 856: + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 480) ModeIndex = ModeIndex_856x480[Depth]; + } break; case 1024: if(VDisplay == 768) ModeIndex = ModeIndex_1024x768[Depth]; + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 576) ModeIndex = ModeIndex_1024x576[Depth]; + } + break; + case 1152: + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 864) ModeIndex = ModeIndex_1152x864[Depth]; + } break; case 1280: if(VDisplay == 1024) ModeIndex = ModeIndex_1280x1024[Depth]; else if(VDisplay == 768) { - if((LCDheight == 768) || (LCDwidth == 1680) || - ((LCDheight == 1024) && (VBFlags & (VB_301|VB_301B|VB_301C|VB_302B)))) { + if((LCDheight == 768) || (LCDwidth == 1680) || + (VBFlags & VB_SISTMDS)) { if(VGAEngine == SIS_300_VGA) { ModeIndex = ModeIndex_300_1280x768[Depth]; } else { @@ -892,24 +911,28 @@ } } } else if(VDisplay == 960) { - if((LCDheight == 960) || - ((LCDheight == 1024) && (VBFlags & (VB_301|VB_301B|VB_301C|VB_302B)))) { + if((LCDheight == 960) || (VBFlags & VB_SISTMDS)) { ModeIndex = ModeIndex_1280x960[Depth]; } } else if(VGAEngine == SIS_315_VGA) { if(VDisplay == 800) { if((LCDheight == 800) || (LCDwidth == 1680) || - ((LCDheight == 1024) && (VBFlags & (VB_301|VB_301B|VB_301C|VB_302B)))) { + (VBFlags & VB_SISTMDS)) { ModeIndex = ModeIndex_1280x800[Depth]; } } else if(VDisplay == 720) { - if((LCDheight == 720) || (LCDwidth == 1680) || - ((LCDheight == 1024) && (VBFlags & (VB_301|VB_301B|VB_301C|VB_302B)))) { + if((LCDheight == 720) || (LCDwidth == 1680) || (LCDwidth == 1400) || + (VBFlags & VB_SISTMDS)) { ModeIndex = ModeIndex_1280x720[Depth]; } } } break; + case 1360: + if(VGAEngine == SIS_315_VGA) { + if(VDisplay == 768) ModeIndex = ModeIndex_1360x768[Depth]; + } + break; case 1400: if(VGAEngine == SIS_315_VGA) { if(VBFlags & (VB_301B | VB_301C | VB_302B | VB_302LV | VB_302ELV)) { @@ -992,7 +1015,7 @@ case 720: if((!(VBFlags & TV_HIVISION)) && (!((VBFlags & TV_YPBPR) && (VBFlags & TV_YPBPR1080I)))) { if(VDisplay == 480) { - if((VBFlags & TV_YPBPR) || (VBFlags & (TV_NTSC | TV_PALM))) + /* if((VBFlags & TV_YPBPR) || (VBFlags & (TV_NTSC | TV_PALM))) */ ModeIndex = ModeIndex_720x480[Depth]; } else if(VDisplay == 576) { if( ((VBFlags & TV_YPBPR) && (VBFlags & TV_YPBPR750P)) || @@ -1280,10 +1303,12 @@ /* (SR11 is used for DDC and in enable/disablebridge) */ SiS_Pr->SiS_SensibleSR11 = FALSE; SiS_Pr->SiS_MyCR63 = 0x63; - if(HwInfo->jChipType >= SIS_661) { - SiS_Pr->SiS_SensibleSR11 = TRUE; + if(HwInfo->jChipType >= SIS_330) { SiS_Pr->SiS_MyCR63 = 0x53; - } + if(HwInfo->jChipType >= SIS_661) { + SiS_Pr->SiS_SensibleSR11 = TRUE; + } + } /* You should use the macros, not these flags directly */ @@ -1324,7 +1349,7 @@ } if(HwInfo->jChipType == SIS_760) { temp1 = SiS_GetReg(SiS_Pr->SiS_P3d4,0x78); - if(temp1 & 0x30) SiS_Pr->SiS_SysFlags |= SF_760UMA; + if(temp1 & 0x30) SiS_Pr->SiS_SysFlags |= SF_760LFB; } } @@ -1387,7 +1412,7 @@ void SiSSetLVDSetc(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) { - ULONG temp; + USHORT temp; SiS_Pr->SiS_IF_DEF_LVDS = 0; SiS_Pr->SiS_IF_DEF_TRUMPION = 0; @@ -1471,6 +1496,12 @@ USHORT romversoffs, romvmaj = 1, romvmin = 0; if(HwInfo->jChipType >= SIS_661) { + if((ROMAddr[0x1a] == 'N') && + (ROMAddr[0x1b] == 'e') && + (ROMAddr[0x1c] == 'w') && + (ROMAddr[0x1d] == 'V')) { + return TRUE; + } romversoffs = ROMAddr[0x16] | (ROMAddr[0x17] << 8); if(romversoffs) { if((ROMAddr[romversoffs+1] == '.') || (ROMAddr[romversoffs+4] == '.')) { @@ -1515,7 +1546,7 @@ */ SiS_Pr->SiS_UseROM = TRUE; } else { - /* 315/330 series stick to the standard */ + /* 315/330 series stick to the standard(s) */ SiS_Pr->SiS_UseROM = TRUE; if((SiS_Pr->SiS_ROMNew = SiSDetermineROMLayout661(SiS_Pr, HwInfo))) { /* Find out about LCD data table entry size */ @@ -1525,7 +1556,7 @@ else if(ROMAddr[romptr + (34 * 16)] == 0xff) SiS_Pr->SiS661LCD2TableSize = 34; else if(ROMAddr[romptr + (36 * 16)] == 0xff) - SiS_Pr->SiS661LCD2TableSize = 36; + SiS_Pr->SiS661LCD2TableSize = 36; /* 0.94 final */ } } } @@ -1641,7 +1672,7 @@ if(rev >= 0xE0) { flag = SiS_GetReg(SiS_Pr->SiS_Part4Port,0x39); if(flag == 0xff) SiS_Pr->SiS_VBType = VB_SIS302LV; - else SiS_Pr->SiS_VBType = VB_SIS302ELV; + else SiS_Pr->SiS_VBType = VB_SIS301C; /* VB_SIS302ELV; */ } else if(rev >= 0xD0) { SiS_Pr->SiS_VBType = VB_SIS301LV; } @@ -1649,95 +1680,6 @@ } /*********************************************/ -/* HELPER: GetDRAMSize */ -/*********************************************/ - -#ifndef LINUX_XF86 -static ULONG -GetDRAMSize(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) -{ - ULONG AdapterMemorySize = 0; -#ifdef SIS315H - USHORT counter; -#endif - - switch(HwInfo->jChipType) { -#ifdef SIS315H - case SIS_315H: - case SIS_315: - case SIS_315PRO: - counter = SiS_GetReg(SiS_Pr->SiS_P3c4,0x14); - AdapterMemorySize = 1 << ((counter & 0xF0) >> 4); - counter >>= 2; - counter &= 0x03; - if(counter == 0x02) { - AdapterMemorySize += (AdapterMemorySize / 2); /* DDR asymetric */ - } else if(counter != 0) { - AdapterMemorySize <<= 1; /* SINGLE_CHANNEL_2_RANK or DUAL_CHANNEL_1_RANK */ - } - AdapterMemorySize *= (1024*1024); - break; - - case SIS_330: - counter = SiS_GetReg(SiS_Pr->SiS_P3c4,0x14); - AdapterMemorySize = 1 << ((counter & 0xF0) >> 4); - counter &= 0x0c; - if(counter != 0) { - AdapterMemorySize <<= 1; - } - AdapterMemorySize *= (1024*1024); - break; - - case SIS_550: - case SIS_650: - case SIS_740: - counter = SiS_GetReg(SiS_Pr->SiS_P3c4,0x14) & 0x3F; - counter++; - AdapterMemorySize = counter * 4; - AdapterMemorySize *= (1024*1024); - break; - - case SIS_661: - case SIS_741: - counter = (SiS_GetReg(SiS_Pr->SiS_P3c4,0x79) & 0xf0) >> 4; - AdapterMemorySize = 1 << counter; - AdapterMemorySize *= (1024*1024); - break; - - case SIS_660: - case SIS_760: - counter = (SiS_GetReg(SiS_Pr->SiS_P3c4,0x79) & 0xf0) >> 4; - if(counter) { - AdapterMemorySize = 1 << counter; - AdapterMemorySize *= (1024*1024); - } - counter = SiS_GetReg(SiS_Pr->SiS_P3c4,0x78) & 0x30; - if(counter) { - if(counter == 0x10) AdapterMemorySize += (32 * 1024 * 1024); - else AdapterMemorySize += (64 * 1024 * 1024); - } - break; -#endif - -#ifdef SIS300 - case SIS_300: - case SIS_540: - case SIS_630: - case SIS_730: - AdapterMemorySize = SiS_GetReg(SiS_Pr->SiS_P3c4,0x14) & 0x3F; - AdapterMemorySize++; - AdapterMemorySize *= (1024*1024); - break; -#endif - default: - break; - } - - return AdapterMemorySize; -} -#endif - -/*********************************************/ /* HELPER: Check RAM size */ /*********************************************/ @@ -1746,8 +1688,8 @@ SiS_CheckMemorySize(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, USHORT ModeNo, USHORT ModeIdIndex) { + USHORT AdapterMemSize = HwInfo->ulVideoMemorySize / (1024*1024); USHORT memorysize,modeflag; - ULONG temp; if(SiS_Pr->UseCustomMode) { modeflag = SiS_Pr->CModeFlag; @@ -1763,11 +1705,8 @@ memorysize >>= MemorySizeShift; /* Get required memory size */ memorysize++; - temp = GetDRAMSize(SiS_Pr, HwInfo); /* Get adapter memory size (in MB) */ - temp /= (1024*1024); - - if(temp < memorysize) return(FALSE); - else return(TRUE); + if(AdapterMemSize < memorysize) return FALSE; + return TRUE; } #endif @@ -1781,14 +1720,14 @@ { UCHAR data, temp; - if(*SiS_Pr->pSiS_SoftSetting & SoftDRAMType) { - data = *SiS_Pr->pSiS_SoftSetting & 0x03; + if((*SiS_Pr->pSiS_SoftSetting) & SoftDRAMType) { + data = (*SiS_Pr->pSiS_SoftSetting) & 0x03; } else { - if(HwInfo->jChipType >= SIS_660) { - /* data = SiS_GetReg(SiS_Pr->SiS_P3d4,0x78) & 0x07; */ - data = ((SiS_GetReg(SiS_Pr->SiS_P3d4,0x78) & 0xc0) >> 6); - } else if(HwInfo->jChipType >= SIS_661) { - data = SiS_GetReg(SiS_Pr->SiS_P3d4,0x78) & 0x07; + if(HwInfo->jChipType >= SIS_661) { + data = SiS_GetReg(SiS_Pr->SiS_P3d4,0x78) & 0x07; + if(SiS_Pr->SiS_ROMNew) { + data = ((SiS_GetReg(SiS_Pr->SiS_P3d4,0x78) & 0xc0) >> 6); + } } else if(IS_SIS550650740) { data = SiS_GetReg(SiS_Pr->SiS_P3c4,0x13) & 0x07; } else { /* 315, 330 */ @@ -1815,10 +1754,14 @@ USHORT SiS_GetMCLK(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) { + UCHAR *ROMAddr = HwInfo->pjVirtualRomBase; USHORT index; index = SiS_Get310DRAMType(SiS_Pr, HwInfo); if(HwInfo->jChipType >= SIS_661) { + if(SiS_Pr->SiS_ROMNew) { + return((USHORT)(SISGETROMW((0x90 + (index * 5) + 3)))); + } return(SiS_Pr->SiS_MCLKData_0[index].CLOCK); } else if(index >= 4) { index -= 4; @@ -1844,20 +1787,17 @@ if(SiS_Pr->SiS_ModeType >= ModeEGA) { if(ModeNo > 0x13) { - AdapterMemorySize = GetDRAMSize(SiS_Pr, HwInfo); - SiS_SetMemory(VideoMemoryAddress,AdapterMemorySize,0); + SiS_SetMemory(VideoMemoryAddress, AdapterMemorySize, 0); } else { pBuffer = (USHORT *)VideoMemoryAddress; - for(i=0; i<0x4000; i++) - pBuffer[i] = 0x0000; + for(i=0; i<0x4000; i++) pBuffer[i] = 0x0000; } } else { - pBuffer = (USHORT *)VideoMemoryAddress; if(SiS_Pr->SiS_ModeType < ModeCGA) { - for(i=0; i<0x4000; i++) - pBuffer[i] = 0x0720; + pBuffer = (USHORT *)VideoMemoryAddress; + for(i=0; i<0x4000; i++) pBuffer[i] = 0x0720; } else { - SiS_SetMemory(VideoMemoryAddress,0x8000,0); + SiS_SetMemory(VideoMemoryAddress, 0x8000, 0); } } } @@ -1913,10 +1853,10 @@ UCHAR index; if(ModeNo <= 0x13) { - index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_StTableIndex; + index = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_StTableIndex; } else { - if(SiS_Pr->SiS_ModeType <= 0x02) index = 0x1B; /* 02 -> ModeEGA */ - else index = 0x0F; + if(SiS_Pr->SiS_ModeType <= ModeEGA) index = 0x1B; + else index = 0x0F; } return index; } @@ -1931,7 +1871,7 @@ USHORT temp,temp1,temp2; if((ModeNo != 0x03) && (ModeNo != 0x10) && (ModeNo != 0x12)) - return(1); + return(TRUE); temp = SiS_GetReg(SiS_Pr->SiS_P3d4,0x11); SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x11,0x80); temp1 = SiS_GetReg(SiS_Pr->SiS_P3d4,0x00); @@ -1941,13 +1881,13 @@ SiS_SetReg(SiS_Pr->SiS_P3d4,0x11,temp); if((HwInfo->jChipType >= SIS_315H) || (HwInfo->jChipType == SIS_300)) { - if(temp2 == 0x55) return(0); - else return(1); + if(temp2 == 0x55) return(FALSE); + else return(TRUE); } else { - if(temp2 != 0x55) return(1); + if(temp2 != 0x55) return(TRUE); else { SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x35,0x01); - return(0); + return(FALSE); } } } @@ -3002,7 +2942,7 @@ data = 0; if(ModeNo > 0x13) { - if(SiS_Pr->SiS_ModeType > 0x02) { + if(SiS_Pr->SiS_ModeType > ModeEGA) { data |= 0x02; data |= ((SiS_Pr->SiS_ModeType - ModeVGA) << 2); } @@ -3065,7 +3005,7 @@ SiS_SetReg(SiS_Pr->SiS_P3c4,0x17,data); } else if( (HwInfo->jChipType == SIS_330) || - ((HwInfo->jChipType == SIS_760) && (SiS_Pr->SiS_SysFlags & SF_760UMA))) { + ((HwInfo->jChipType == SIS_760) && (SiS_Pr->SiS_SysFlags & SF_760LFB))) { data = SiS_Get310DRAMType(SiS_Pr, HwInfo); if(HwInfo->jChipType == SIS_330) { @@ -3109,7 +3049,7 @@ if(data2 >= 0x127) data = 0xba; else data = 0x7a; } - } else { + } else { /* 760+LFB */ if (data2 >= 0x190) data = 0xba; else if(data2 >= 0xff) data = 0x7a; else if(data2 >= 0xd3) data = 0x3a; @@ -3379,8 +3319,6 @@ } } - - /*********************************************/ /* HELPER: RESET VIDEO BRIDGE */ /*********************************************/ @@ -5105,10 +5043,7 @@ int sisfb_mode_rate_to_ddata(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, unsigned char modeno, unsigned char rateindex, - ULONG *left_margin, ULONG *right_margin, - ULONG *upper_margin, ULONG *lower_margin, - ULONG *hsync_len, ULONG *vsync_len, - ULONG *sync, ULONG *vmode) + struct fb_var_screeninfo *var) { USHORT ModeNo = modeno; USHORT ModeIdIndex = 0, index = 0; @@ -5196,15 +5131,15 @@ /* Terrible hack, but the correct CRTC data for * these modes only produces a black screen... */ - *left_margin = (400 - 376); - *right_margin = (328 - 320); - *hsync_len = (376 - 328); + var->left_margin = (400 - 376); + var->right_margin = (328 - 320); + var->hsync_len = (376 - 328); } else { - *left_margin = D * 8; - *right_margin = F * 8; - *hsync_len = C * 8; + var->left_margin = D * 8; + var->right_margin = F * 8; + var->hsync_len = C * 8; } @@ -5266,47 +5201,47 @@ D = B - F - C; - *upper_margin = D; - *lower_margin = F; - *vsync_len = C; + var->upper_margin = D; + var->lower_margin = F; + var->vsync_len = C; if(SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_InfoFlag & 0x8000) - *sync &= ~FB_SYNC_VERT_HIGH_ACT; + var->sync &= ~FB_SYNC_VERT_HIGH_ACT; else - *sync |= FB_SYNC_VERT_HIGH_ACT; + var->sync |= FB_SYNC_VERT_HIGH_ACT; if(SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_InfoFlag & 0x4000) - *sync &= ~FB_SYNC_HOR_HIGH_ACT; + var->sync &= ~FB_SYNC_HOR_HIGH_ACT; else - *sync |= FB_SYNC_HOR_HIGH_ACT; + var->sync |= FB_SYNC_HOR_HIGH_ACT; - *vmode = FB_VMODE_NONINTERLACED; + var->vmode = FB_VMODE_NONINTERLACED; if(SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].Ext_InfoFlag & 0x0080) - *vmode = FB_VMODE_INTERLACED; + var->vmode = FB_VMODE_INTERLACED; else { - j = 0; - while(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID != 0xff) { + j = 0; + while(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID != 0xff) { if(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeID == SiS_Pr->SiS_RefIndex[RefreshRateTableIndex].ModeID) { if(SiS_Pr->SiS_EModeIDTable[j].Ext_ModeFlag & DoubleScanMode) { - *vmode = FB_VMODE_DOUBLE; + var->vmode = FB_VMODE_DOUBLE; } break; } j++; - } + } } - if((*vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { + if((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED) { #if 0 /* Do this? */ - *upper_margin <<= 1; - *lower_margin <<= 1; - *vsync_len <<= 1; -#endif - } else if((*vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { - *upper_margin >>= 1; - *lower_margin >>= 1; - *vsync_len >>= 1; + var->upper_margin <<= 1; + var->lower_margin <<= 1; + var->vsync_len <<= 1; +#endif + } else if((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE) { + var->upper_margin >>= 1; + var->lower_margin >>= 1; + var->vsync_len >>= 1; } return 1; diff -urN linux-2.4.26/drivers/video/sis/init.h linux-2.4.27/drivers/video/sis/init.h --- linux-2.4.26/drivers/video/sis/init.h 2004-04-14 06:05:39.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/init.h 2004-08-07 16:26:05.913398510 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Data and prototypes for init.c * @@ -253,8 +254,8 @@ { 1152, 768, 8,16}, /* 0x1a */ { 768, 576, 8,16}, /* 0x1b */ { 1360,1024, 8,16}, /* 0x1c */ - { 1280, 800, 8,16}, /* 0x1d */ - { 1680,1050, 8,16} /* 0x1e */ + { 1680,1050, 8,16}, /* 0x1d */ + { 1280, 800, 8,16} /* 0x1e */ }; static SiS_StandTableStruct SiS_StandTable[]= @@ -840,7 +841,8 @@ { 36, 25,1060, 648,1270, 530, 438, 0, 438,0xeb,0x05,0x25,0x16}, /* 800x600, 400x300 - better */ { 3, 2,1080, 619,1270, 540, 438, 0, 438,0xf3,0x00,0x1d,0x20}, /* 720x576 */ { 1, 1,1170, 821,1270, 520, 686, 0, 686,0xF3,0x00,0x1D,0x20}, /* 1024x768 */ - { 1, 1,1170, 821,1270, 520, 686, 0, 686,0xF3,0x00,0x1D,0x20} /* 1024x768 (for NTSC equ) */ + { 1, 1,1170, 821,1270, 520, 686, 0, 686,0xF3,0x00,0x1D,0x20}, /* 1024x768 (for NTSC equ) */ + { 9, 4, 848, 528,1270, 530, 0, 0, 50,0xf5,0xfb,0x1b,0x2a} /* 720x480 test */ }; static const SiS_TVDataStruct SiS_StNTSCData[] = @@ -929,7 +931,10 @@ { 143, 70, 0x39c,0x189,0x4f6,0x1b8,0x05c, 0, 0x05c, 0x00,0x00,0x00,0x00}, { 99, 32, 0x320,0x1fe,0x500,0x2d0, 50, 0, 0, 0x00,0x00,0x00,0x00}, /* 640x480 */ { 5, 4, 0x5d8,0x29e,0x500,0x2a8, 50, 0, 0, 0x00,0x00,0x00,0x00}, /* 800x600 */ +#if 0 { 2, 1, 0x35a,0x1f7,0x4f6,0x1e0, 0,128, 0, 0x00,0x00,0x00,0x00}, /* 720x480 */ +#endif + { 99, 32, 0x320,0x1fe,0x500,0x2d0, 50, 0, 0, 0x00,0x00,0x00,0x00}, /* 720x480 test */ { 68, 64, 0x55f,0x346,0x500,0x2a8,0x27e, 0, 0, 0x00,0x00,0x00,0x00}, /* 1024x768 */ { 5, 2, 0x3a7,0x226,0x500,0x2a8, 0,128, 0, 0x00,0x00,0x00,0x00}, /* 720x576 */ { 25, 24, 0x5d8,0x2f3,0x460,0x2a8, 50, 0, 0, 0x00,0x00,0x00,0x00} /* 1280x720 */ @@ -1057,17 +1062,30 @@ { 1, 1, 1688, 1066, 1688, 1066 } }; +#undef SISUSE6330MODES + static const SiS_LCDDataStruct SiS_ExtLCD1400x1050Data[] = { - { 211, 100, 2100, 408, 1688, 1066 }, /* 640x400 */ +#ifdef SISUSE6330MODES + { 211, 60, 1260, 410, 1688, 1066 }, /* 640x400 (6330) */ +#else + { 211, 100, 2100, 408, 1688, 1066 }, /* 640x400 (6325) WORKS */ +#endif { 211, 64, 1536, 358, 1688, 1066 }, { 211, 100, 2100, 408, 1688, 1066 }, { 211, 64, 1536, 358, 1688, 1066 }, - { 211, 48, 840, 488, 1688, 1066 }, /* 640x480 */ - { 211, 72, 1008, 609, 1688, 1066 }, /* 800x600 */ - { 211, 128, 1400, 776, 1688, 1066 }, - { 211, 205, 1680, 1041, 1688, 1066 }, - { 1, 1, 1688, 1066, 1688, 1066 } +#ifdef SISUSE6330MODES + { 211, 80, 1400, 490, 1688, 1066 }, /* 640x480 (6330) */ + { 211, 117, 1638, 613, 1688, 1066 }, /* 800x600 (6330) */ +#else + { 211, 48, 840, 488, 1688, 1066 }, /* 640x480 (6325) WORKS */ + { 211, 72, 1008, 609, 1688, 1066 }, /* 800x600 (6325) WORKS */ +#endif + { 211, 128, 1400, 776, 1688, 1066 }, /* 1024x768 */ + { 211, 205, 1680, 1041, 1688, 1066 }, /* 1280x1024 - not used (always unscaled) */ + { 1, 1, 1688, 1066, 1688, 1066 }, /* 1400x1050 */ + { 0, 0, 0, 0, 0, 0 }, /* kludge */ + { 211, 120, 1400, 730, 1688, 1066 } /* 1280x720 */ }; static const SiS_LCDDataStruct SiS_LCD1680x1050Data[] = @@ -1097,21 +1115,29 @@ { 4, 1,1080, 625, 2160, 1250 }, { 5, 2,1350, 800, 2160, 1250 }, {135,88,1600,1100, 2160, 1250 }, - {135,88,1600,1100, 2160, 1250 }, + {72, 49,1680,1092, 2160, 1250 }, { 1, 1,2160,1250, 2160, 1250 } }; static const SiS_LCDDataStruct SiS_ExtLCD1600x1200Data[] = { - {27, 4, 800, 500, 2160, 1250 }, +#ifndef SISUSE6330MODES + {72,11, 990, 422, 2160, 1250 }, /* 640x400 (6330) */ +#else + {27, 4, 800, 500, 2160, 1250 }, /* 640x400 (6235) */ +#endif {27, 4, 800, 500, 2160, 1250 }, { 6, 1, 900, 500, 2160, 1250 }, { 6, 1, 900, 500, 2160, 1250 }, - {27, 1, 800, 500, 2160, 1250 }, +#ifndef SISUSE6330MODES + {45, 8, 960, 505, 2160, 1250 }, /* 640x480 (6330) */ +#else + {27, 1, 800, 500, 2160, 1250 }, /* 640x480 (6325) */ +#endif { 4, 1,1080, 625, 2160, 1250 }, { 5, 2,1350, 800, 2160, 1250 }, - {27,16,1500,1064, 2160, 1250 }, - {27,16,1500,1064, 2160, 1250 }, + {27,16,1500,1064, 2160, 1250 }, /* 1280x1024 */ + {72,49,1680,1092, 2160, 1250 }, /* 1400x1050 (6330, was not supported on 6325) */ { 1, 1,2160,1250, 2160, 1250 } }; @@ -1129,13 +1155,22 @@ { 1, 1,2160,1250,2160,1250 }, /* 0x09: 1600x1200 */ { 1, 1,1800,1000,1800,1000 }, /* 0x0a: 1280x960 */ { 1, 1,1688,1066,1688,1066 }, /* 0x0b: 1400x1050 */ - { 1, 1,1650, 750,1650, 750 }, /* 0x0c: 1280x720 (TMDS) */ - { 1, 1,1408, 816,1408, 816 }, /* 0x0d: 1280x800 */ + { 1, 1,1650, 750,1650, 750 }, /* 0x0c: 1280x720 (TMDS, projector) */ + { 1, 1,1656, 841,1656, 841 }, /* 0x0d: 1280x800 (was: 1408, 816) */ { 1, 1,1900,1066,1900,1066 }, /* 0x0e: 1680x1050 (LVDS) */ { 1, 1,1408, 806,1408, 806 }, /* 0x0f: 1280x768_2 */ { 1, 1,1664, 798,1664, 798 }, /* 0x10: 1280x768_3 */ { 1, 1,1688, 802,1688, 802 }, /* 0x11: 1280x768: Std, TMDS only */ - { 1, 1,1344, 806,1344, 806 } /* 0x12: 1280x720 (LVDS) */ + { 1, 1,1344, 806,1344, 806 }, /* 0x12: 1280x720 (LVDS) */ + { 1, 1, 896, 497, 896, 497 }, /* 0x13: 720x480 */ + { 1, 1, 912, 597, 912, 597 }, /* 0x14: 720x576 */ + { 1, 1, 912, 597, 912, 597 }, /* 0x15: 768x576 */ + { 1, 1,1056, 497,1056, 497 }, /* 0x16: 848x480 */ + { 1, 1,1064, 497,1064, 497 }, /* 0x17: 856x480 */ + { 1, 1,1056, 497,1056, 497 }, /* 0x18: 800x480 */ + { 1, 1,1328, 739,1328, 739 }, /* 0x19: 1024x576 */ + { 1, 1,1680, 892,1680, 892 }, /* 0x1a: 1152x864 */ + { 1, 1,1808, 808,1808, 808 } /* 0x1b: 1360x768 */ }; @@ -2387,10 +2422,7 @@ unsigned char modeno, unsigned char rateindex); int sisfb_mode_rate_to_ddata(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, unsigned char modeno, unsigned char rateindex, - ULONG *left_margin, ULONG *right_margin, - ULONG *upper_margin, ULONG *lower_margin, - ULONG *hsync_len, ULONG *vsync_len, - ULONG *sync, ULONG *vmode); + struct fb_var_screeninfo *var); BOOLEAN sisfb_gettotalfrommode(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, unsigned char modeno, int *htotal, int *vtotal, unsigned char rateindex); #endif diff -urN linux-2.4.26/drivers/video/sis/init301.c linux-2.4.27/drivers/video/sis/init301.c --- linux-2.4.26/drivers/video/sis/init301.c 2004-04-14 06:05:40.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/init301.c 2004-08-07 16:26:05.928399126 -0700 @@ -1,9 +1,10 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Mode initializing code (CRT2 section) * for SiS 300/305/540/630/730 and - * SiS 315/550/650/M650/651/661FX/M661xX/740/741/M741/330/660/M660/760/M760 - * (Universal module for Linux kernel framebuffer and XFree86 4.x) + * SiS 315/550/650/M650/651/661FX/M661xX/740/741(GX)/M741/330/660/M660/760/M760 + * (Universal module for Linux kernel framebuffer and XFree86/X.org 4.x) * * Copyright (C) 2001-2004 by Thomas Winischhofer, Vienna, Austria * @@ -115,7 +116,10 @@ static void SiS_SetRegSR11ANDOR(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, USHORT DataAND, USHORT DataOR) { - if(HwInfo->jChipType >= SIS_661) DataAND &= 0x0f; + if(HwInfo->jChipType >= SIS_661) { + DataAND &= 0x0f; + DataOR &= 0x0f; + } SiS_SetRegANDOR(SiS_Pr->SiS_P3c4,0x11,DataAND,DataOR); } @@ -137,7 +141,7 @@ if((SiS_Pr->SiS_ROMNew) && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { myptr = (UCHAR *)SiS_LCDStruct661; - romindex = SISGETROMW(0x100); /* 10c, 0.93: 10e */ + romindex = SISGETROMW(0x100); if(romindex) { romindex += ((SiS_GetReg(SiS_Pr->SiS_P3d4,0x7d) & 0x1f) * 26); myptr = &ROMAddr[romindex]; @@ -157,7 +161,7 @@ */ if((SiS_Pr->SiS_ROMNew) && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { - romptr = SISGETROMW(0x102); /* 2ad */ + romptr = SISGETROMW(0x102); romptr += ((SiS_GetReg(SiS_Pr->SiS_P3d4,0x36) >> 4) * SiS_Pr->SiS661LCD2TableSize); } @@ -275,10 +279,11 @@ /* Do NOT check for UseCustomMode here, will skrew up FIFO */ if(ModeNo == 0xfe) return 0; - if(ModeNo <= 0x13) + if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; - else + } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; + } if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { @@ -474,7 +479,6 @@ Delay = 3; } else { if(DelayTime >= 2) DelayTime -= 2; - if(!(DelayTime & 0x01)) { Delay = SiS_Pr->SiS_PanelDelayTbl[DelayIndex].timer[0]; } else { @@ -482,15 +486,12 @@ } if(SiS_Pr->SiS_UseROM) { if(ROMAddr[0x220] & 0x40) { - if(!(DelayTime & 0x01)) { - Delay = (USHORT)ROMAddr[0x225]; - } else { - Delay = (USHORT)ROMAddr[0x226]; - } + if(!(DelayTime & 0x01)) Delay = (USHORT)ROMAddr[0x225]; + else Delay = (USHORT)ROMAddr[0x226]; } } } - SiS_ShortDelay(SiS_Pr,Delay); + SiS_ShortDelay(SiS_Pr, Delay); #endif /* SIS300 */ @@ -498,7 +499,9 @@ #ifdef SIS315H - if(HwInfo->jChipType >= SIS_661) { + if((HwInfo->jChipType >= SIS_661) || + (HwInfo->jChipType <= SIS_315PRO) || + (HwInfo->jChipType == SIS_330)) { if(!(DelayTime & 0x01)) { SiS_DDC2Delay(SiS_Pr, 0x1000); @@ -506,11 +509,9 @@ SiS_DDC2Delay(SiS_Pr, 0x4000); } - } else if(HwInfo->jChipType >= SIS_330) return; - - if((SiS_Pr->SiS_IF_DEF_LVDS == 1) || + } else if((SiS_Pr->SiS_IF_DEF_LVDS == 1) /* || (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { /* 315 series, LVDS; Special */ + (SiS_Pr->SiS_CustomT == CUT_CLEVO1400) */ ) { /* 315 series, LVDS; Special */ if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { PanelID = SiS_GetReg(SiS_Pr->SiS_P3d4,0x36); @@ -541,7 +542,7 @@ } } } - SiS_ShortDelay(SiS_Pr,Delay); + SiS_ShortDelay(SiS_Pr, Delay); } } else if(SiS_Pr->SiS_VBType & VB_SISVB) { /* 315 series, all bridges */ @@ -584,7 +585,6 @@ USHORT watchdog; if(SiS_GetReg(SiS_Pr->SiS_P3c4,0x1f) & 0xc0) return; - if(!(SiS_GetReg(SiS_Pr->SiS_P3d4,0x17) & 0x80)) return; watchdog = 65535; @@ -640,10 +640,10 @@ tempal = SiS_GetRegByte(SiS_Pr->SiS_P3da); if(temp & 0x01) { if((tempal & 0x08)) continue; - if(!(tempal & 0x08)) break; + else break; } else { if(!(tempal & 0x08)) continue; - if((tempal & 0x08)) break; + else break; } } temp ^= 0x01; @@ -664,12 +664,14 @@ /* HELPER: MISC */ /*********************************************/ +#ifdef SIS300 static BOOLEAN SiS_Is301B(SiS_Private *SiS_Pr) { if(SiS_GetReg(SiS_Pr->SiS_Part4Port,0x01) >= 0xb0) return TRUE; return FALSE; } +#endif static BOOLEAN SiS_CRT2IsLCD(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) @@ -715,6 +717,16 @@ return FALSE; } +#ifdef SIS315H +static BOOLEAN +SiS_IsVAorLCD(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) +{ + if(SiS_IsVAMode(SiS_Pr,HwInfo)) return TRUE; + if(SiS_CRT2IsLCD(SiS_Pr, HwInfo)) return TRUE; + return FALSE; +} +#endif + static BOOLEAN SiS_IsDualLink(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) { @@ -754,11 +766,8 @@ static BOOLEAN SiS_WeHaveBacklightCtrl(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) { - USHORT flag; - if((HwInfo->jChipType >= SIS_315H) && (HwInfo->jChipType < SIS_661)) { - flag = SiS_GetReg(SiS_Pr->SiS_P3d4,0x79); - if(flag & 0x10) return TRUE; + if(SiS_GetReg(SiS_Pr->SiS_P3d4,0x79) & 0x10) return TRUE; } return FALSE; } @@ -854,12 +863,12 @@ USHORT flag; if(SiS_Pr->SiS_IF_DEF_LVDS == 1) { - return FALSE; + return TRUE; } else if(SiS_Pr->SiS_VBType & VB_SISVB) { flag = SiS_GetReg(SiS_Pr->SiS_Part4Port,0x00); - if((flag == 1) || (flag == 2)) return FALSE; + if((flag == 1) || (flag == 2)) return TRUE; } - return TRUE; + return FALSE; } static BOOLEAN @@ -867,21 +876,21 @@ { USHORT flag; - if(!(SiS_BridgeIsOn(SiS_Pr))) { + if(SiS_BridgeIsOn(SiS_Pr)) { flag = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x00); if(HwInfo->jChipType < SIS_315H) { flag &= 0xa0; - if((flag == 0x80) || (flag == 0x20)) return FALSE; + if((flag == 0x80) || (flag == 0x20)) return TRUE; } else { flag &= 0x50; - if((flag == 0x40) || (flag == 0x10)) return FALSE; + if((flag == 0x40) || (flag == 0x10)) return TRUE; } } - return TRUE; + return FALSE; } static BOOLEAN -SiS_BridgeInSlave(SiS_Private *SiS_Pr) +SiS_BridgeInSlavemode(SiS_Private *SiS_Pr) { USHORT flag1; @@ -942,7 +951,7 @@ SiS_Pr->SiS_ModeType = modeflag & ModeInfoFlag; tempbx = 0; - if(SiS_BridgeIsOn(SiS_Pr) == 0) { + if(SiS_BridgeIsOn(SiS_Pr)) { temp = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); #if 0 if(HwInfo->jChipType < SIS_661) { @@ -970,7 +979,7 @@ SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x31,0xbf); } if(!(SiS_GetReg(SiS_Pr->SiS_P3d4,0x31) & (DriverMode >> 8))) { - /* Reset LCDA setting */ + /* Reset LCDA setting if not driver mode */ SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x38,0xfc); } if(IS_SIS650) { @@ -1125,9 +1134,9 @@ } } } else { - if(!(SiS_BridgeIsEnabled(SiS_Pr,HwInfo))) { + if(SiS_BridgeIsEnabled(SiS_Pr,HwInfo)) { if(!(tempbx & DriverMode)) { - if(SiS_BridgeInSlave(SiS_Pr)) { + if(SiS_BridgeInSlavemode(SiS_Pr)) { tempbx |= SetSimuScanMode; } } @@ -1437,21 +1446,49 @@ { #ifdef SIS315H UCHAR *ROMAddr; + USHORT temp; + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "Paneldata driver: [%d %d] [H %d %d] [V %d %d] [C %d 0x%02x 0x%02x]\n", + SiS_Pr->PanelHT, SiS_Pr->PanelVT, + SiS_Pr->PanelHRS, SiS_Pr->PanelHRE, + SiS_Pr->PanelVRS, SiS_Pr->PanelVRE, + SiS_Pr->SiS_VBVCLKData[SiS_Pr->PanelVCLKIdx315].CLOCK, + SiS_Pr->SiS_VBVCLKData[SiS_Pr->PanelVCLKIdx315].Part4_A, + SiS_Pr->SiS_VBVCLKData[SiS_Pr->PanelVCLKIdx315].Part4_B); +#endif if((ROMAddr = GetLCDStructPtr661(SiS_Pr, HwInfo))) { - SiS_Pr->PanelHT = SISGETROMW(6); - SiS_Pr->PanelVT = SISGETROMW(8); + if((temp = SISGETROMW(6)) != SiS_Pr->PanelHT) { + SiS_Pr->SiS_NeedRomModeData = TRUE; + SiS_Pr->PanelHT = temp; + } + if((temp = SISGETROMW(8)) != SiS_Pr->PanelVT) { + SiS_Pr->SiS_NeedRomModeData = TRUE; + SiS_Pr->PanelVT = temp; + } SiS_Pr->PanelHRS = SISGETROMW(10); SiS_Pr->PanelHRE = SISGETROMW(12); SiS_Pr->PanelVRS = SISGETROMW(14); SiS_Pr->PanelVRE = SISGETROMW(16); SiS_Pr->PanelVCLKIdx315 = VCLK_CUSTOM_315; SiS_Pr->SiS_VCLKData[VCLK_CUSTOM_315].CLOCK = - SiS_Pr->SiS_VBVCLKData[VCLK_CUSTOM_315].CLOCK = (USHORT)ROMAddr[18]; + SiS_Pr->SiS_VBVCLKData[VCLK_CUSTOM_315].CLOCK = (USHORT)((UCHAR)ROMAddr[18]); SiS_Pr->SiS_VCLKData[VCLK_CUSTOM_315].SR2B = SiS_Pr->SiS_VBVCLKData[VCLK_CUSTOM_315].Part4_A = ROMAddr[19]; - SiS_Pr->SiS_VCLKData[VCLK_CUSTOM_315].SR2B = + SiS_Pr->SiS_VCLKData[VCLK_CUSTOM_315].SR2C = SiS_Pr->SiS_VBVCLKData[VCLK_CUSTOM_315].Part4_B = ROMAddr[20]; + +#ifdef TWDEBUG + xf86DrvMsg(0, X_INFO, "Paneldata BIOS: [%d %d] [H %d %d] [V %d %d] [C %d 0x%02x 0x%02x]\n", + SiS_Pr->PanelHT, SiS_Pr->PanelVT, + SiS_Pr->PanelHRS, SiS_Pr->PanelHRE, + SiS_Pr->PanelVRS, SiS_Pr->PanelVRE, + SiS_Pr->SiS_VBVCLKData[SiS_Pr->PanelVCLKIdx315].CLOCK, + SiS_Pr->SiS_VBVCLKData[SiS_Pr->PanelVCLKIdx315].Part4_A, + SiS_Pr->SiS_VBVCLKData[SiS_Pr->PanelVCLKIdx315].Part4_B); +#endif + } #endif } @@ -1478,6 +1515,7 @@ SiS_Pr->PanelHRE = 999; /* HSync end */ SiS_Pr->PanelVRS = 999; /* VSync start */ SiS_Pr->PanelVRE = 999; /* VSync end */ + SiS_Pr->SiS_NeedRomModeData = FALSE; if(!(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA))) return; @@ -1491,6 +1529,7 @@ } temp = SiS_GetReg(SiS_Pr->SiS_P3d4,0x36); + if(!temp) return; if((HwInfo->jChipType >= SIS_661) || (SiS_Pr->SiS_ROMNew)) { SiS_Pr->SiS_LCDTypeInfo = (SiS_GetReg(SiS_Pr->SiS_P3d4,0x39) & 0x7c) >> 2; @@ -1628,10 +1667,10 @@ SiS_GetLCDInfoBIOS(SiS_Pr, HwInfo); break; case Panel_1280x800: SiS_Pr->PanelXRes = 1280; SiS_Pr->PanelYRes = 800; - SiS_Pr->PanelHT = 1408; SiS_Pr->PanelVT = 816; - SiS_Pr->PanelHRS = 21; SiS_Pr->PanelHRE = 24; - SiS_Pr->PanelVRS = 4; SiS_Pr->PanelVRE = 3; - SiS_Pr->PanelVCLKIdx315 = VCLK69_315; + SiS_Pr->PanelHT = 1656; SiS_Pr->PanelVT = 841; /* 1408, 816 */ + SiS_Pr->PanelHRS = 32; SiS_Pr->PanelHRE = 312; /* 16, 64 */ + SiS_Pr->PanelVRS = 16; SiS_Pr->PanelVRE = 8; /* 4, 3 */ + SiS_Pr->PanelVCLKIdx315 = VCLK83_315; SiS_GetLCDInfoBIOS(SiS_Pr, HwInfo); break; case Panel_1280x960: SiS_Pr->PanelXRes = 1280; SiS_Pr->PanelYRes = 960; @@ -1757,6 +1796,10 @@ if(SiS_Pr->SiS_VBType & VB_SISVB) { if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCD | SetCRT2ToLCDA)) { + if(modeflag & NoSupportLCDScale) { + /* No scaling for this mode on any panel */ + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } switch(SiS_Pr->SiS_LCDResInfo) { case Panel_Custom: /* For non-standard LCD resolution, we let the panel scale */ @@ -1774,7 +1817,7 @@ SiS_Pr->SiS_LCDInfo &= ~DontExpandLCD; break; case Panel_1280x1024: - if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + if(SiS_Pr->SiS_VBType & VB_SISTMDS) { if(ModeNo == 0x7c || ModeNo == 0x7d || ModeNo == 0x7e || ModeNo == 0x79 || ModeNo == 0x75 || ModeNo == 0x78 || ModeNo == 0x14 || ModeNo == 0x15 || ModeNo == 0x16) { @@ -1791,13 +1834,30 @@ } break; case Panel_1400x1050: + if(SiS_Pr->SiS_VBType & VB_SISTMDS) { + if(ModeNo == 0x7c || ModeNo == 0x7d || ModeNo == 0x7e || + ModeNo == 0x79 || ModeNo == 0x75 || ModeNo == 0x78 || + ModeNo == 0x14 || ModeNo == 0x15 || ModeNo == 0x16 || + ModeNo == 0x23 || ModeNo == 0x24 || ModeNo == 0x25) { + /* Do not scale to 1280x720/768/800/960 (B/C bridges only) */ + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } + } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(ModeNo == 0x79 || ModeNo == 0x75 || ModeNo == 0x78) { + if(SiS_Pr->UsePanelScaler == -1) { + /* Do not scale to 1280x720 by default (LVDS bridges) */ + SiS_Pr->SiS_LCDInfo |= DontExpandLCD; + } + } + } if(ModeNo == 0x3a || ModeNo == 0x4d || ModeNo == 0x65) { - /* We do not scale to 1280x1024 (all bridges) */ + /* Do not scale to 1280x1024 (all bridges) */ SiS_Pr->SiS_LCDInfo |= DontExpandLCD; } break; case Panel_1600x1200: - if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + if(SiS_Pr->SiS_VBType & VB_SISTMDS) { /* No idea about the timing and zoom factors (C bridge only) */ SiS_Pr->SiS_LCDInfo |= DontExpandLCD; } @@ -1832,6 +1892,10 @@ if(SiS_Pr->SiS_LCDResInfo == Panel_640x480) { SiS_Pr->SiS_LCDInfo |= LCDPass11; } + + if(SiS_Pr->UseCustomMode) { + SiS_Pr->SiS_LCDInfo |= (DontExpandLCD | LCDPass11); + } /* (In)validate LCDPass11 flag */ if(!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) { @@ -1975,10 +2039,25 @@ VCLKIndex = SiS_Pr->PanelVCLKIdx315; if((SiS_Pr->SiS_LCDInfo & DontExpandLCD) && (SiS_Pr->SiS_LCDInfo & LCDPass11)) { VCLKIndex = VCLKIndexGEN; - if(resinfo == SIS_RI_1280x720) VCLKIndex = VCLK_1280x720; - if(SiS_Pr->SiS_LCDResInfo == Panel_1280x720) { - if(SiS_Pr->PanelHT == 1344) VCLKIndex = VCLK_1280x720_2; + switch(resinfo) { + case SIS_RI_1280x720: VCLKIndex = VCLK_1280x720; + if(SiS_Pr->SiS_LCDResInfo == Panel_1280x720) { + if(SiS_Pr->PanelHT == 1344) { + VCLKIndex = VCLK_1280x720_2; + } + } + break; + case SIS_RI_720x480: VCLKIndex = VCLK_720x480; break; + case SIS_RI_720x576: VCLKIndex = VCLK_720x576; break; + case SIS_RI_768x576: VCLKIndex = VCLK_768x576; break; + case SIS_RI_848x480: VCLKIndex = VCLK_848x480; break; + case SIS_RI_856x480: VCLKIndex = VCLK_856x480; break; + case SIS_RI_800x480: VCLKIndex = VCLK_800x480; break; + case SIS_RI_1024x576: VCLKIndex = VCLK_1024x576; break; + case SIS_RI_1152x864: VCLKIndex = VCLK_1152x864; break; + case SIS_RI_1360x768: VCLKIndex = VCLK_1360x768; break; } + if(ModeNo <= 0x13) { if(HwInfo->jChipType <= SIS_315PRO) { if(SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_CRT2CRTC == 1) VCLKIndex = 0x42; @@ -2150,12 +2229,10 @@ PSIS_HW_INFO HwInfo) { USHORT i,j,modeflag; - USHORT tempcl,tempah=0; -#ifdef SIS300 - USHORT temp; -#endif + USHORT tempbl,tempcl,tempah=0; #ifdef SIS315H - USHORT tempbl, tempah2, tempbl2; + UCHAR *ROMAddr = HwInfo->pjVirtualRomBase; + USHORT tempah2, tempbl2; #endif if(ModeNo <= 0x13) { @@ -2178,6 +2255,9 @@ } else { for(i=0,j=4; i<3; i++,j++) SiS_SetReg(SiS_Pr->SiS_Part1Port,j,0); + if(HwInfo->jChipType >= SIS_315H) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x02,0x7F); + } tempcl = SiS_Pr->SiS_ModeType; @@ -2187,14 +2267,14 @@ /* For 301BDH: (with LCD via LVDS) */ if(SiS_Pr->SiS_VBType & VB_NoLCD) { - temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32); - temp &= 0xef; - temp |= 0x02; + tempbl = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32); + tempbl &= 0xef; + tempbl |= 0x02; if((SiS_Pr->SiS_VBInfo & SetCRT2ToTV) || (SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC)) { - temp |= 0x10; - temp &= 0xfd; + tempbl |= 0x10; + tempbl &= 0xfd; } - SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); + SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,tempbl); } if(ModeNo > 0x13) { @@ -2368,7 +2448,7 @@ #ifdef SIS315H - unsigned char bridgerev = SiS_GetReg(SiS_Pr->SiS_Part4Port,0x01);; + unsigned char bridgerev = SiS_GetReg(SiS_Pr->SiS_Part4Port,0x01); /* The following is nearly unpreditable and varies from machine * to machine. Especially the 301DH seems to be a real trouble @@ -2399,10 +2479,11 @@ * in a 650 box (Jake). What is the criteria? */ - if((IS_SIS740) || (HwInfo->jChipType >= SIS_661)) { + if((IS_SIS740) || (HwInfo->jChipType >= SIS_661) || (SiS_Pr->SiS_ROMNew)) { tempah = 0x30; tempbl = 0xc0; - if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) { + if((SiS_Pr->SiS_VBInfo & DisableCRT2Display) || + ((SiS_Pr->SiS_ROMNew) && (!(ROMAddr[0x5b] & 0x04)))) { tempah = 0x00; tempbl = 0x00; } @@ -2433,23 +2514,23 @@ if(IS_SIS740) { tempah = 0x80; - if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) { - tempah = 0x00; - } + if(SiS_Pr->SiS_VBInfo & DisableCRT2Display) tempah = 0x00; SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x23,0x7f,tempah); } else { tempah = 0x00; tempbl = 0x7f; if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { tempbl = 0xff; - if(!(SiS_IsDualEdge(SiS_Pr, HwInfo))) { - tempah = 0x80; - } + if(!(SiS_IsDualEdge(SiS_Pr, HwInfo))) tempah = 0x80; } SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x23,tempbl,tempah); } - - /* 661: Sets p4 27 and 34 here, done in SetGroup4 here (old BIOS) */ + +#if 0 + if(SiS_Pr->SiS_VBType & VB_SIS301C) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x3a,0xc0); + } +#endif #endif /* SIS315H */ @@ -2478,9 +2559,7 @@ tempbl = 0xfb; if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA)) { tempah = 0x00; - if(SiS_IsDualEdge(SiS_Pr, HwInfo)) { - tempbl = 0xff; - } + if(SiS_IsDualEdge(SiS_Pr, HwInfo)) tempbl = 0xff; } SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x13,tempbl,tempah); @@ -2560,9 +2639,11 @@ if((SiS_Pr->SiS_VBType & VB_SISVB) && (!(SiS_Pr->SiS_VBType & VB_NoLCD))) { +#if 0 if(SiS_Pr->SiS_VBInfo & (SetCRT2ToLCDA | SetCRT2ToLCD | SetCRT2ToHiVision)) { if(xres == 720) xres = 640; } +#endif if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { switch(SiS_Pr->SiS_LCDResInfo) { @@ -2726,7 +2807,12 @@ tempbx = SiS_Pr->SiS_LCDResInfo; if(!(SiS_Pr->SiS_SetFlag & LCDVESATiming)) tempbx += 32; - + + if(SiS_Pr->SiS_LCDResInfo == Panel_1680x1050) { + if (resinfo == SIS_RI_1280x800) tempal = 9; + else if(resinfo == SIS_RI_1400x1050) tempal = 11; + } + if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { /* Pass 1:1 only (center-screen handled outside) */ tempbx = 100; if(ModeNo >= 0x13) { @@ -2783,6 +2869,9 @@ (resinfo == SIS_RI_720x576) || (resinfo == SIS_RI_768x576)) { tempal = 6; + if(SiS_Pr->SiS_TVMode & (TVSetPAL | TVSetPALN)) { + if(resinfo == SIS_RI_720x480) tempal = 9; + } } if(SiS_Pr->SiS_VBInfo & SetCRT2ToYPbPr525750) { if(SiS_Pr->SiS_TVMode & TVSetYPbPr525p) { @@ -3080,11 +3169,14 @@ USHORT RefreshRateTableIndex, PSIS_HW_INFO HwInfo) { - USHORT tempax,tempbx,modeflag; - USHORT resinfo; - USHORT CRT2Index,ResIndex; + UCHAR *ROMAddr = NULL; + USHORT tempax,tempbx,modeflag,romptr=0; + USHORT resinfo,CRT2Index,ResIndex; const SiS_LCDDataStruct *LCDPtr = NULL; const SiS_TVDataStruct *TVPtr = NULL; +#ifdef SIS315H + SHORT resinfo661; +#endif if(ModeNo <= 0x13) { modeflag = SiS_Pr->SiS_SModeIDTable[ModeIdIndex].St_ModeFlag; @@ -3095,6 +3187,20 @@ } else { modeflag = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_ModeFlag; resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; +#ifdef SIS315H + resinfo661 = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].ROMMODEIDX661; + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && + (SiS_Pr->SiS_SetFlag & LCDVESATiming) && + (resinfo661 >= 0) && + (SiS_Pr->SiS_NeedRomModeData) ) { + if((ROMAddr = GetLCDStructPtr661(SiS_Pr, HwInfo))) { + if((romptr = (SISGETROMW(21)))) { + romptr += (resinfo661 * 10); + ROMAddr = HwInfo->pjVirtualRomBase; + } + } + } +#endif } SiS_Pr->SiS_NewFlickerMode = 0; @@ -3238,6 +3344,8 @@ SiS_Pr->SiS_VDE = SiS_Pr->SiS_VGAVDE; } else { + + BOOLEAN gotit = FALSE; if((SiS_Pr->SiS_LCDInfo & DontExpandLCD) && (!(SiS_Pr->SiS_LCDInfo & LCDPass11))) { @@ -3245,8 +3353,23 @@ SiS_Pr->SiS_VGAVT = SiS_Pr->PanelVT; SiS_Pr->SiS_HT = SiS_Pr->PanelHT; SiS_Pr->SiS_VT = SiS_Pr->PanelVT; + gotit = TRUE; - } else { + } else if( (!(SiS_Pr->SiS_LCDInfo & DontExpandLCD)) && (romptr) && (ROMAddr) ) { + +#ifdef SIS315H + SiS_Pr->SiS_RVBHCMAX = ROMAddr[romptr]; + SiS_Pr->SiS_RVBHCFACT = ROMAddr[romptr+1]; + SiS_Pr->SiS_VGAHT = ROMAddr[romptr+2] | ((ROMAddr[romptr+3] & 0x0f) << 8); + SiS_Pr->SiS_VGAVT = ROMAddr[romptr+4] | ((ROMAddr[romptr+3] & 0xf0) << 4); + SiS_Pr->SiS_HT = ROMAddr[romptr+5] | ((ROMAddr[romptr+6] & 0x0f) << 8); + SiS_Pr->SiS_VT = ROMAddr[romptr+7] | ((ROMAddr[romptr+6] & 0xf0) << 4); + if(SiS_Pr->SiS_VGAHT) gotit = TRUE; +#endif + + } + + if(!gotit) { SiS_GetCRT2Ptr(SiS_Pr,ModeNo,ModeIdIndex,RefreshRateTableIndex, &CRT2Index,&ResIndex,HwInfo); @@ -3445,8 +3568,10 @@ #ifdef SIS315H if(SiS_Pr->SiS_LCDInfo & DontExpandLCD) { /* non-pass 1:1 only, see above */ - if(SiS_Pr->SiS_VGAVDE != SiS_Pr->PanelYRes) { + if(SiS_Pr->SiS_VGAHDE != SiS_Pr->PanelXRes) { SiS_Pr->SiS_LCDHDES = SiS_Pr->SiS_HT - ((SiS_Pr->PanelXRes - SiS_Pr->SiS_VGAHDE) / 2); + } + if(SiS_Pr->SiS_VGAVDE != SiS_Pr->PanelYRes) { SiS_Pr->SiS_LCDVDES = SiS_Pr->SiS_VT - ((SiS_Pr->PanelYRes - SiS_Pr->SiS_VGAVDE) / 2); } } @@ -3562,207 +3687,142 @@ if(SiS_Pr->SiS_VBType & VB_SISVB) { - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* ===== For 30xB/LV ===== */ + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* ===== For 30xB/LV ===== */ if(HwInfo->jChipType < SIS_315H) { #ifdef SIS300 /* 300 series */ - if(HwInfo->jChipType == SIS_300) { /* For 300+301LV (A907) */ - - if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwInfo))) { - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFE); - SiS_PanelDelay(SiS_Pr, HwInfo, 3); - } + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwInfo))) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFE); + } else { + SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xF7,0x08); } + SiS_PanelDelay(SiS_Pr, HwInfo, 3); + } + if(SiS_Is301B(SiS_Pr)) { SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0x3f); SiS_ShortDelay(SiS_Pr,1); - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); - SiS_DisplayOff(SiS_Pr); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { - if( (!(SiS_CRT2IsLCD(SiS_Pr, HwInfo))) || - (!(SiS_CR36BIOSWord23d(SiS_Pr, HwInfo))) ) { - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFD); - } - } - - } else { - - if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwInfo))) { - SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xF7,0x08); - SiS_PanelDelay(SiS_Pr, HwInfo, 3); - } - if(SiS_Is301B(SiS_Pr)) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0x3f); - SiS_ShortDelay(SiS_Pr,1); - } - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); - SiS_DisplayOff(SiS_Pr); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_UnLockCRT2(SiS_Pr,HwInfo); + } + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); + SiS_DisplayOff(SiS_Pr); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_UnLockCRT2(SiS_Pr,HwInfo); + if(!(SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x01,0x80); SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x02,0x40); - if( (!(SiS_CRT2IsLCD(SiS_Pr, HwInfo))) || - (!(SiS_CR36BIOSWord23d(SiS_Pr, HwInfo))) ) { - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xFB,0x04); - } } - + if( (!(SiS_CRT2IsLCD(SiS_Pr, HwInfo))) || + (!(SiS_CR36BIOSWord23d(SiS_Pr, HwInfo))) ) { + SiS_PanelDelay(SiS_Pr, HwInfo, 2); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFD); + } else { + SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xFB,0x04); + } + } + #endif /* SIS300 */ } else { #ifdef SIS315H /* 315 series */ + + BOOLEAN custom1 = ((SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || + (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) ? TRUE : FALSE; - if(IS_SIS550650740660) { /* 550, 650, 740, 660 */ - - modenum = SiS_GetReg(SiS_Pr->SiS_P3d4,0x34) & 0x7f; + modenum = SiS_GetReg(SiS_Pr->SiS_P3d4,0x34) & 0x7f; - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { /* LV */ + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + #ifdef SET_EMI - if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { - if(SiS_Pr->SiS_CustomT != CUT_CLEVO1400) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x30,0x0c); - } + if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + if(SiS_Pr->SiS_CustomT != CUT_CLEVO1400) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x30,0x0c); } + } #endif - if( (modenum <= 0x13) || - (!(SiS_IsDualEdge(SiS_Pr,HwInfo))) || - (SiS_IsVAMode(SiS_Pr,HwInfo)) ) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFE); - if((SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - SiS_PanelDelay(SiS_Pr, HwInfo, 3); - } - } - - if((SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - SiS_DDC2Delay(SiS_Pr,0xff00); - SiS_DDC2Delay(SiS_Pr,0xe000); - - SiS_SetRegByte(SiS_Pr->SiS_P3c6,0x00); - - pushax = SiS_GetReg(SiS_Pr->SiS_P3c4,0x06); - - if(IS_SIS740) { - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); - } - - SiS_PanelDelay(SiS_Pr, HwInfo, 3); + if( (modenum <= 0x13) || + (SiS_IsVAMode(SiS_Pr,HwInfo)) || + (!(SiS_IsDualEdge(SiS_Pr,HwInfo))) ) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFE); + if(custom1) SiS_PanelDelay(SiS_Pr, HwInfo, 3); + } - if(!(SiS_IsNotM650orLater(SiS_Pr, HwInfo))) { - tempah = 0xef; - if(SiS_IsVAMode(SiS_Pr,HwInfo)) { - tempah = 0xf7; - } - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,tempah); - } + if(!custom1) { + SiS_DDC2Delay(SiS_Pr,0xff00); + SiS_DDC2Delay(SiS_Pr,0xe000); + SiS_SetRegByte(SiS_Pr->SiS_P3c6,0x00); + pushax = SiS_GetReg(SiS_Pr->SiS_P3c4,0x06); + if(IS_SIS740) { + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); } + SiS_PanelDelay(SiS_Pr, HwInfo, 3); + } - } else if(SiS_Pr->SiS_VBType & VB_NoLCD) { /* B-DH */ - - if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,0xef); - } + } + + if(!(SiS_IsNotM650orLater(SiS_Pr, HwInfo))) { + tempah = 0xef; + if(SiS_IsVAMode(SiS_Pr,HwInfo)) tempah = 0xf7; + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,tempah); + } + + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,~0x10); + } + + tempah = 0x3f; + if(SiS_IsDualEdge(SiS_Pr,HwInfo)) { + tempah = 0x7f; + if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) tempah = 0xbf; + } + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); - } + if((SiS_IsVAMode(SiS_Pr,HwInfo)) || + ((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && (modenum <= 0x13))) { - if((SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,0xef); + SiS_DisplayOff(SiS_Pr); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_PanelDelay(SiS_Pr, HwInfo, 2); } - - if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - tempah = 0x3f; - if(SiS_IsDualEdge(SiS_Pr,HwInfo)) { - tempah = 0x7f; - if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - tempah = 0xbf; - } - } - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); + + } + + if((!(SiS_IsVAMode(SiS_Pr,HwInfo))) || + ((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && (modenum <= 0x13))) { + + if(!(SiS_IsDualEdge(SiS_Pr,HwInfo))) { + SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); + SiS_DisplayOff(SiS_Pr); } - - if((SiS_IsVAMode(SiS_Pr,HwInfo)) || - ((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && (modenum <= 0x13))) { - - if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); - SiS_DisplayOff(SiS_Pr); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - } else { - SiS_DisplayOff(SiS_Pr); - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x1E,0xDF); - if((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && (modenum <= 0x13)) { - SiS_DisplayOff(SiS_Pr); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x00); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x00,temp); - } - } - - } else { - - if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - if(!(SiS_IsDualEdge(SiS_Pr,HwInfo))) { - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); - SiS_DisplayOff(SiS_Pr); - } - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); - } else { - SiS_DisplayOff(SiS_Pr); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - } - - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x00); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x00,temp); - + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); + + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_PanelDelay(SiS_Pr, HwInfo, 2); } - if((SiS_Pr->SiS_VBType & VB_SIS301LV302LV) && - (SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,~0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); + temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x00); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x00,temp); - tempah = 0x3f; - if(SiS_IsDualEdge(SiS_Pr,HwInfo)) { - tempah = 0x7f; - if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - tempah = 0xbf; - } - } - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); + } + + if(SiS_IsNotM650orLater(SiS_Pr,HwInfo)) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + } - if(SiS_IsNotM650orLater(SiS_Pr,HwInfo)) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); - } + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + + if(!custom1) { if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xdf); if(!(SiS_CRT2IsLCD(SiS_Pr,HwInfo))) { if(!(SiS_IsDualEdge(SiS_Pr,HwInfo))) { SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFD); @@ -3773,89 +3833,31 @@ SiS_SetReg(SiS_Pr->SiS_P3c4,0x06,pushax); if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { - if( (SiS_IsVAMode(SiS_Pr, HwInfo)) || - (SiS_CRT2IsLCD(SiS_Pr, HwInfo)) ) { + if(SiS_IsVAorLCD(SiS_Pr, HwInfo)) { SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 20); } } - - } else if(SiS_Pr->SiS_VBType & VB_NoLCD) { - - /* NIL */ - - } else if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - - if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { - tempah = 0xef; - if(SiS_IsDualEdge(SiS_Pr,HwInfo)) { - if(modenum > 0x13) { - tempah = 0xf7; - } - } - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,tempah); - } - if((SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - if((SiS_IsVAMode(SiS_Pr,HwInfo)) || - (!(SiS_IsDualEdge(SiS_Pr,HwInfo)))) { - if((!(SiS_WeHaveBacklightCtrl(SiS_Pr, HwInfo))) || - (!(SiS_CRT2IsLCD(SiS_Pr, HwInfo)))) { - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFD); - SiS_PanelDelay(SiS_Pr, HwInfo, 4); - } + + } else { + + if((SiS_IsVAMode(SiS_Pr,HwInfo)) || + (!(SiS_IsDualEdge(SiS_Pr,HwInfo)))) { + if((!(SiS_WeHaveBacklightCtrl(SiS_Pr, HwInfo))) || + (!(SiS_CRT2IsLCD(SiS_Pr, HwInfo)))) { + SiS_PanelDelay(SiS_Pr, HwInfo, 2); + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x26,0xFD); + SiS_PanelDelay(SiS_Pr, HwInfo, 4); } } } - - } else { /* 315, 330 - all bridge types */ - - if(SiS_Is301B(SiS_Pr)) { - tempah = 0x3f; - if(SiS_IsDualEdge(SiS_Pr,HwInfo)) { - tempah = 0x7f; - if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - tempah = 0xbf; - } - } - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1F,tempah); - if(SiS_IsVAMode(SiS_Pr,HwInfo)) { - SiS_DisplayOff(SiS_Pr); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - } - } - if( (!(SiS_Is301B(SiS_Pr))) || - (!(SiS_IsVAMode(SiS_Pr,HwInfo))) ) { - - if( (!(SiS_Is301B(SiS_Pr))) || - (!(SiS_IsDualEdge(SiS_Pr,HwInfo))) ) { - - SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xDF); - SiS_DisplayOff(SiS_Pr); - - } - - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x80); - - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x32,0xDF); - - temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x00); - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x00,0x10); - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x1E,0xDF); - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x00,temp); - - } - - } /* 315/330 */ + } #endif /* SIS315H */ } - } else { /* ============ For 301 ================ */ + } else { /* ============ For 301 ================ */ if(HwInfo->jChipType < SIS_315H) { #ifdef SIS300 @@ -3947,6 +3949,10 @@ #ifdef SIS315H /* 315 series */ + if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x4c,~0x18); + } + if(SiS_Pr->SiS_IF_DEF_CH70xx != 0) { if(HwInfo->jChipType == SIS_740) { @@ -4077,7 +4083,6 @@ BOOLEAN delaylong = FALSE; #endif - if(SiS_Pr->SiS_VBType & VB_SISVB) { if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { /* ====== For 301B et al ====== */ @@ -4086,28 +4091,55 @@ #ifdef SIS300 /* 300 series */ - if(HwInfo->jChipType == SIS_300) { - - if(SiS_CRT2IsLCD(SiS_Pr, HwInfo)) { - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); - if(!(SiS_CR36BIOSWord23d(SiS_Pr, HwInfo))) { - SiS_PanelDelay(SiS_Pr, HwInfo, 0); - } + if(SiS_CRT2IsLCD(SiS_Pr, HwInfo)) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); + } else if(SiS_Pr->SiS_VBType & VB_NoLCD) { + SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xFB,0x00); + } + if(SiS_Pr->SiS_VBType & (VB_SIS301LV302LV | VB_NoLCD)) { + if(!(SiS_CR36BIOSWord23d(SiS_Pr, HwInfo))) { + SiS_PanelDelay(SiS_Pr, HwInfo, 0); } } + } + + if((SiS_Pr->SiS_VBType & VB_NoLCD) && + (SiS_CRT2IsLCD(SiS_Pr, HwInfo))) { + + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* Enable CRT2 */ + SiS_DisplayOn(SiS_Pr); + SiS_UnLockCRT2(SiS_Pr,HwInfo); + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x02,0xBF); + if(SiS_BridgeInSlavemode(SiS_Pr)) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x01,0x1F); + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x01,0x1F,0x40); + } + if(!(SiS_GetReg(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { + if(!(SiS_GetReg(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { + if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwInfo))) { + SiS_PanelDelay(SiS_Pr, HwInfo, 1); + } + SiS_WaitVBRetrace(SiS_Pr,HwInfo); + SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xF7,0x00); + } + } + + } else { + temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32) & 0xDF; /* lock mode */ - if(SiS_BridgeInSlave(SiS_Pr)) { + if(SiS_BridgeInSlavemode(SiS_Pr)) { tempah = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; } SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1F,0x20); /* enable VB processor */ SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); SiS_DisplayOn(SiS_Pr); - if(SiS_CRT2IsLCD(SiS_Pr, HwInfo)) { - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + if(SiS_CRT2IsLCD(SiS_Pr, HwInfo)) { if(!(SiS_GetReg(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwInfo))) { SiS_PanelDelay(SiS_Pr, HwInfo, 1); @@ -4116,441 +4148,279 @@ } } } + + } - } else { - - if((SiS_Pr->SiS_VBType & VB_NoLCD) && - (SiS_CRT2IsLCD(SiS_Pr, HwInfo))) { - /* This is only for LCD output on 301B-DH via LVDS */ - SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xFB,0x00); - if(!(SiS_CR36BIOSWord23d(SiS_Pr,HwInfo))) { - SiS_PanelDelay(SiS_Pr, HwInfo, 0); - } - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* Enable CRT2 */ - SiS_DisplayOn(SiS_Pr); - SiS_UnLockCRT2(SiS_Pr,HwInfo); - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x02,0xBF); - if(SiS_BridgeInSlave(SiS_Pr)) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x01,0x1F); - } else { - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x01,0x1F,0x40); - } - if(!(SiS_GetReg(SiS_Pr->SiS_P3c4,0x13) & 0x40)) { - if(!(SiS_GetReg(SiS_Pr->SiS_P3c4,0x16) & 0x10)) { - if(!(SiS_CR36BIOSWord23b(SiS_Pr,HwInfo))) { - SiS_PanelDelay(SiS_Pr, HwInfo, 1); - } - SiS_WaitVBRetrace(SiS_Pr,HwInfo); - SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xF7,0x00); - } - } - } else { - temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32) & 0xDF; /* lock mode */ - if(SiS_BridgeInSlave(SiS_Pr)) { - tempah = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; - } - SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1F,0x20); /* enable VB processor */ - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0); - SiS_DisplayOn(SiS_Pr); - } - - } + #endif /* SIS300 */ } else { #ifdef SIS315H /* 315 series */ - if(IS_SIS550650740660) { /* 550, 650, 740, 660 */ - - UCHAR r30=0, r31=0, r32=0, r33=0, cr36=0; - - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + UCHAR r30=0, r31=0, r32=0, r33=0, cr36=0; + /* USHORT emidelay=0; */ - if((SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0xef); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x1f,0xef); #ifdef SET_EMI - if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x30,0x0c); - } + if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x30,0x0c); + } #endif - } - - if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { - tempah = 0x10; - if(SiS_LCDAEnabled(SiS_Pr, HwInfo)) { - if(SiS_TVEnabled(SiS_Pr, HwInfo)) tempah = 0x18; - else tempah = 0x08; - } - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x4c,tempah); - } - - if((SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - SiS_SetRegByte(SiS_Pr->SiS_P3c6,0x00); - SiS_DisplayOff(SiS_Pr); - pushax = SiS_GetReg(SiS_Pr->SiS_P3c4,0x06); - if(IS_SIS740) { - SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); - } - } - - if( (SiS_IsVAMode(SiS_Pr,HwInfo)) || - (SiS_CRT2IsLCD(SiS_Pr, HwInfo)) ) { - if(!(SiS_GetReg(SiS_Pr->SiS_Part4Port,0x26) & 0x02)) { - if((SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 2); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); - if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { - SiS_GenericDelay(SiS_Pr, 0x4500); - } - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 2); - } else { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); - SiS_PanelDelay(SiS_Pr, HwInfo, 0); - } - } - } - - if((SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - if(!(SiS_GetReg(SiS_Pr->SiS_P3d4,0x31) & 0x40)) { - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 10); - delaylong = TRUE; - } - } - - } else if(SiS_Pr->SiS_VBType & VB_NoLCD) { - - if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x4c,0x10); - } - - } else if(SiS_Pr->SiS_VBType & VB_SIS301B302B) { + } - if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { - tempah = 0x10; - if(SiS_LCDAEnabled(SiS_Pr, HwInfo)) { - if(SiS_TVEnabled(SiS_Pr, HwInfo)) tempah = 0x18; - else tempah = 0x08; - } - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x4c,tempah); - } - + if(!(SiS_IsNotM650orLater(SiS_Pr, HwInfo))) { + tempah = 0x10; + if(SiS_LCDAEnabled(SiS_Pr, HwInfo)) { + if(SiS_TVEnabled(SiS_Pr, HwInfo)) tempah = 0x18; + else tempah = 0x08; } + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x4c,tempah); + } - if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32) & 0xDF; - if(SiS_BridgeInSlave(SiS_Pr)) { - tempah = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) { - if(!(SiS_LCDAEnabled(SiS_Pr, HwInfo))) temp |= 0x20; - } - } - SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + + SiS_SetRegByte(SiS_Pr->SiS_P3c6,0x00); + SiS_DisplayOff(SiS_Pr); + pushax = SiS_GetReg(SiS_Pr->SiS_P3c4,0x06); + if(IS_SIS740) { + SiS_SetRegAND(SiS_Pr->SiS_P3c4,0x06,0xE3); + } - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ - - if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); - temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x2e); - if(!(temp & 0x80)) { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + if(SiS_IsVAorLCD(SiS_Pr, HwInfo)) { + if(!(SiS_GetReg(SiS_Pr->SiS_Part4Port,0x26) & 0x02)) { + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 2); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x02); + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 2); + if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + SiS_GenericDelay(SiS_Pr, 0x4500); } - } else { - SiS_PanelDelay(SiS_Pr, HwInfo, 2); } - } else { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1e,0x20); } - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); - - if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) { - temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x2e); - if(!(temp & 0x80)) { - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); - } + if(!(SiS_GetReg(SiS_Pr->SiS_P3d4,0x31) & 0x40)) { + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 10); + delaylong = TRUE; } - tempah = 0xc0; - if(SiS_IsDualEdge(SiS_Pr, HwInfo)) { - tempah = 0x80; - if(!(SiS_IsVAMode(SiS_Pr, HwInfo))) { - tempah = 0x40; - } - } - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,tempah); + } - if((SiS_Pr->SiS_VBType & VB_SIS301B302B) || - (((SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400)) && - (!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))))) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); - } + if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { + + temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32) & 0xDF; + if(SiS_BridgeInSlavemode(SiS_Pr)) { + tempah = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); + if(!(tempah & SetCRT2ToRAMDAC)) { + if(!(SiS_LCDAEnabled(SiS_Pr, HwInfo))) temp |= 0x20; + } + } + SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); + SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ + + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x2e,0x7f); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { + SiS_PanelDelay(SiS_Pr, HwInfo, 2); + } + + } else { + + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x1e,0x20); + + } - if((SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) && - (SiS_Pr->SiS_CustomT != CUT_CLEVO1400)) { - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - } -#ifdef COMPAQ_HACK - if(SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) { - SiS_PanelDelay(SiS_Pr, HwInfo, 2); - } -#endif + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + + tempah = 0xc0; + if(SiS_IsDualEdge(SiS_Pr, HwInfo)) { + tempah = 0x80; + if(!(SiS_IsVAMode(SiS_Pr, HwInfo))) tempah = 0x40; + } + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,tempah); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1f,0x10); + if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { - if(SiS_Pr->SiS_CustomT != CUT_CLEVO1400) { + SiS_PanelDelay(SiS_Pr, HwInfo, 2); + + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1f,0x10); + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); + + if(SiS_Pr->SiS_CustomT != CUT_CLEVO1400) { #ifdef SET_EMI - if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { - SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x30,0x0c); - } + if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + SiS_SetRegAND(SiS_Pr->SiS_Part4Port,0x30,0x0c); + } #endif - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x0c); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x27,0x0c); #ifdef SET_EMI - if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { - cr36 = SiS_GetReg(SiS_Pr->SiS_P3d4,0x36); - - /* (P4_30|0x40) */ - /* Compal 1400x1050: 0x05, 0x60, 0x00 YES (1.10.7w; CR36=69) */ - /* Compal 1400x1050: 0x0d, 0x70, 0x40 YES (1.10.7x; CR36=69) */ - /* Acer 1280x1024: 0x12, 0xd0, 0x6b NO (1.10.9k; CR36=73) */ - /* Compaq 1280x1024: 0x0d, 0x70, 0x6b YES (1.12.04b; CR36=03) */ - /* Clevo 1024x768: 0x05, 0x60, 0x33 NO (1.10.8e; CR36=12, DL!) */ - /* Clevo 1024x768: 0x0d, 0x70, 0x40 (if type == 3) YES (1.10.8y; CR36=?2) */ - /* Clevo 1024x768: 0x05, 0x60, 0x33 (if type != 3) YES (1.10.8y; CR36=?2) */ - /* Asus 1024x768: ? ? (1.10.8o; CR36=?2) */ - /* Asus 1024x768: 0x08, 0x10, 0x3c (problematic) YES (1.10.8q; CR36=22) */ - - if(SiS_Pr->HaveEMI) { - r30 = SiS_Pr->EMI_30; - r31 = SiS_Pr->EMI_31; - r32 = SiS_Pr->EMI_32; - r33 = SiS_Pr->EMI_33; - } else { - r30 = 0; + cr36 = SiS_GetReg(SiS_Pr->SiS_P3d4,0x36); + + if(SiS_Pr->SiS_ROMNew) { + UCHAR *ROMAddr = HwInfo->pjVirtualRomBase; + USHORT romptr = GetLCDStructPtr661_2(SiS_Pr, HwInfo); + if(romptr) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x20); + SiS_Pr->EMI_30 = 0; + SiS_Pr->EMI_31 = ROMAddr[romptr + 14]; + SiS_Pr->EMI_32 = ROMAddr[romptr + 15]; + SiS_Pr->EMI_33 = ROMAddr[romptr + 16]; + if(ROMAddr[romptr + 1] & 0x10) SiS_Pr->EMI_30 = 0x40; + /* emidelay = SISGETROMW((romptr + 0x22)); */ + SiS_Pr->HaveEMI = SiS_Pr->HaveEMILCD = SiS_Pr->OverruleEMI = TRUE; } + } - /* EMI_30 is read at driver start; however, the BIOS sets this - * (if it is used) only if the LCD is in use. In case we caught - * the machine while on TV output, this bit is not set and we - * don't know if it should be set - hence our detection is wrong. - * Work-around this here: - */ - - if((!SiS_Pr->HaveEMI) || (!SiS_Pr->HaveEMILCD)) { - if((cr36 & 0x0f) == 0x02) { /* 1024x768 */ - r30 |= 0x40; - if(SiS_Pr->SiS_CustomT == CUT_CLEVO1024) { - r30 &= ~0x40; - } - } else if((cr36 & 0x0f) == 0x03) { /* 1280x1024 */ - r30 |= 0x40; - if(SiS_Pr->SiS_CustomT != CUT_COMPAQ1280) { - r30 &= ~0x40; - } - } else if((cr36 & 0x0f) == 0x09) { /* 1400x1050 */ - r30 |= 0x40; - } else if((cr36 & 0x0f) == 0x0b) { /* 1600x1200 - unknown */ - r30 |= 0x40; - } - } + /* (P4_30|0x40) */ + /* Compal 1400x1050: 0x05, 0x60, 0x00 YES (1.10.7w; CR36=69) */ + /* Compal 1400x1050: 0x0d, 0x70, 0x40 YES (1.10.7x; CR36=69) */ + /* Acer 1280x1024: 0x12, 0xd0, 0x6b NO (1.10.9k; CR36=73) */ + /* Compaq 1280x1024: 0x0d, 0x70, 0x6b YES (1.12.04b; CR36=03) */ + /* Clevo 1024x768: 0x05, 0x60, 0x33 NO (1.10.8e; CR36=12, DL!) */ + /* Clevo 1024x768: 0x0d, 0x70, 0x40 (if type == 3) YES (1.10.8y; CR36=?2) */ + /* Clevo 1024x768: 0x05, 0x60, 0x33 (if type != 3) YES (1.10.8y; CR36=?2) */ + /* Asus 1024x768: ? ? (1.10.8o; CR36=?2) */ + /* Asus 1024x768: 0x08, 0x10, 0x3c (problematic) YES (1.10.8q; CR36=22) */ + + if(SiS_Pr->HaveEMI) { + r30 = SiS_Pr->EMI_30; r31 = SiS_Pr->EMI_31; + r32 = SiS_Pr->EMI_32; r33 = SiS_Pr->EMI_33; + } else { + r30 = 0; + } - if(!SiS_Pr->HaveEMI) { - if((cr36 & 0x0f) == 0x02) { + /* EMI_30 is read at driver start; however, the BIOS sets this + * (if it is used) only if the LCD is in use. In case we caught + * the machine while on TV output, this bit is not set and we + * don't know if it should be set - hence our detection is wrong. + * Work-around this here: + */ + + if((!SiS_Pr->HaveEMI) || (!SiS_Pr->HaveEMILCD)) { + switch((cr36 & 0x0f)) { + case 2: + r30 |= 0x40; + if(SiS_Pr->SiS_CustomT == CUT_CLEVO1024) r30 &= ~0x40; + if(!SiS_Pr->HaveEMI) { + r31 = 0x05; r32 = 0x60; r33 = 0x33; if((cr36 & 0xf0) == 0x30) { r31 = 0x0d; r32 = 0x70; r33 = 0x40; - } else { - r31 = 0x05; r32 = 0x60; r33 = 0x33; - } - } else if((cr36 & 0x0f) == 0x03) { + } + } + break; + case 3: /* 1280x1024 */ + if(SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) r30 |= 0x40; + if(!SiS_Pr->HaveEMI) { + r31 = 0x12; r32 = 0xd0; r33 = 0x6b; if(SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) { r31 = 0x0d; r32 = 0x70; r33 = 0x6b; - } else { - r31 = 0x12; r32 = 0xd0; r33 = 0x6b; } - } else if((cr36 & 0x0f) == 0x09) { + } + break; + case 9: /* 1400x1050 */ + r30 |= 0x40; + if(!SiS_Pr->HaveEMI) { + r31 = 0x05; r32 = 0x60; r33 = 0x00; if(SiS_Pr->SiS_CustomT == CUT_COMPAL1400_2) { r31 = 0x0d; r32 = 0x70; r33 = 0x40; /* BIOS values */ - } else { - r31 = 0x05; r32 = 0x60; r33 = 0x00; } - } else { + } + break; + case 11: /* 1600x1200 - unknown */ + r30 |= 0x40; + if(!SiS_Pr->HaveEMI) { r31 = 0x05; r32 = 0x60; r33 = 0x00; } } + } - /* BIOS values don't work so well sometimes */ - if(!SiS_Pr->OverruleEMI) { + /* BIOS values don't work so well sometimes */ + if(!SiS_Pr->OverruleEMI) { #ifdef COMPAL_HACK - if(SiS_Pr->SiS_CustomT == CUT_COMPAL1400_2) { - if((cr36 & 0x0f) == 0x09) { - r30 = 0x60; r31 = 0x05; r32 = 0x60; r33 = 0x00; - } - } + if(SiS_Pr->SiS_CustomT == CUT_COMPAL1400_2) { + if((cr36 & 0x0f) == 0x09) { + r30 = 0x60; r31 = 0x05; r32 = 0x60; r33 = 0x00; + } + } #endif #ifdef COMPAQ_HACK - if(SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) { - if((cr36 & 0x0f) == 0x03) { - r30 = 0x20; r31 = 0x12; r32 = 0xd0; r33 = 0x6b; /* rev 1 */ - } + if(SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) { + if((cr36 & 0x0f) == 0x03) { + r30 = 0x20; r31 = 0x12; r32 = 0xd0; r33 = 0x6b; } + } #endif #ifdef ASUS_HACK - if(SiS_Pr->SiS_CustomT == CUT_ASUSA2H_2) { - if((cr36 & 0x0f) == 0x02) { - /* r30 = 0x60; r31 = 0x05; r32 = 0x60; r33 = 0x33; */ /* rev 2 */ - /* r30 = 0x20; r31 = 0x05; r32 = 0x60; r33 = 0x33; */ /* rev 3 */ - /* r30 = 0x60; r31 = 0x0d; r32 = 0x70; r33 = 0x40; */ /* rev 4 */ - /* r30 = 0x20; r31 = 0x0d; r32 = 0x70; r33 = 0x40; */ /* rev 5 */ - } + if(SiS_Pr->SiS_CustomT == CUT_ASUSA2H_2) { + if((cr36 & 0x0f) == 0x02) { + /* r30 = 0x60; r31 = 0x05; r32 = 0x60; r33 = 0x33; */ /* rev 2 */ + /* r30 = 0x20; r31 = 0x05; r32 = 0x60; r33 = 0x33; */ /* rev 3 */ + /* r30 = 0x60; r31 = 0x0d; r32 = 0x70; r33 = 0x40; */ /* rev 4 */ + /* r30 = 0x20; r31 = 0x0d; r32 = 0x70; r33 = 0x40; */ /* rev 5 */ } -#endif - } - if(!(SiS_Pr->OverruleEMI && (!r30) && (!r31) && (!r32) && (!r33))) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x20); - } - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x31,r31); - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x32,r32); - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x33,r33); - if(!(SiS_Pr->OverruleEMI && (!r30) && (!r31) && (!r32) && (!r33))) { - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x34,0x10); - } else { - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x34,0x00); - } - if( (SiS_IsVAMode(SiS_Pr,HwInfo)) || - (SiS_CRT2IsLCD(SiS_Pr, HwInfo)) ) { - if(r30 & 0x40) { - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 5); - if(delaylong) { - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 5); - delaylong = FALSE; - } - SiS_WaitVBRetrace(SiS_Pr,HwInfo); - if(SiS_Pr->SiS_CustomT == CUT_ASUSA2H_2) { - SiS_GenericDelay(SiS_Pr, 0x500); - } - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x40); - } } - } #endif - } - - if(SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) { - - if( (SiS_IsVAMode(SiS_Pr,HwInfo)) || - (SiS_CRT2IsLCD(SiS_Pr, HwInfo)) ) { - SiS_DisplayOn(SiS_Pr); - SiS_PanelDelay(SiS_Pr, HwInfo, 1); - SiS_WaitVBRetrace(SiS_Pr, HwInfo); - SiS_PanelDelay(SiS_Pr, HwInfo, 3); - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x01); - } + } + + if(!(SiS_Pr->OverruleEMI && (!r30) && (!r31) && (!r32) && (!r33))) { + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x20); } - - } else if(SiS_Pr->SiS_CustomT == CUT_CLEVO1400) { - - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))) { - if( (SiS_IsVAMode(SiS_Pr, HwInfo)) || - (SiS_CRT2IsLCD(SiS_Pr, HwInfo)) ) { - SiS_DisplayOn(SiS_Pr); - SiS_PanelDelay(SiS_Pr, HwInfo, 1); - SiS_WaitVBRetrace(SiS_Pr,HwInfo); - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x01); - } + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x31,r31); + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x32,r32); + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x33,r33); + if(!(SiS_Pr->OverruleEMI && (!r30) && (!r31) && (!r32) && (!r33))) { + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x34,0x10); + } else { + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x34,0x00); } - - } else { - - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2e,0x80); - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))) { - if( (SiS_IsVAMode(SiS_Pr,HwInfo)) || - ((SiS_CRT2IsLCD(SiS_Pr, HwInfo))) ) { - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 10); - if(delaylong) { - SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 10); - } - SiS_WaitVBRetrace(SiS_Pr,HwInfo); - if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + + if( (SiS_LCDAEnabled(SiS_Pr, HwInfo)) || + (SiS_CRT2IsLCD(SiS_Pr, HwInfo)) ) { + if(r30 & 0x40) { + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 5); + if(delaylong) { + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 5); + delaylong = FALSE; + } + SiS_WaitVBRetrace(SiS_Pr,HwInfo); + if(SiS_Pr->SiS_CustomT == CUT_ASUSA2H_2) { SiS_GenericDelay(SiS_Pr, 0x500); } - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x01); + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x30,0x40); } - } - - SiS_SetReg(SiS_Pr->SiS_P3c4,0x06,pushax); - SiS_DisplayOn(SiS_Pr); - SiS_SetRegByte(SiS_Pr->SiS_P3c6,0xff); - - if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))) { - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); - } - + } } - +#endif } - } else { /* 315, 330 */ - - if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32) & 0xDF; - if(SiS_BridgeInSlave(SiS_Pr)) { - tempah = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; - } - SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); - - SiS_SetRegOR(SiS_Pr->SiS_P3c4,0x1E,0x20); /* enable CRT2 */ - - temp = SiS_GetReg(SiS_Pr->SiS_Part1Port,0x2E); - if(!(temp & 0x80)) - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); - } - - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x00,0x1f,0x20); - - if(SiS_Is301B(SiS_Pr)) { - - temp=SiS_GetReg(SiS_Pr->SiS_Part1Port,0x2E); - if(!(temp & 0x80)) - SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x2E,0x80); - - tempah = 0xc0; - if(SiS_IsDualEdge(SiS_Pr,HwInfo)) { - tempah = 0x80; - if(!(SiS_IsVAMode(SiS_Pr,HwInfo))) { - tempah = 0x40; - } + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))) { + if(SiS_IsVAorLCD(SiS_Pr, HwInfo)) { + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 10); + if(delaylong) { + SiS_PanelDelayLoop(SiS_Pr, HwInfo, 3, 10); + } + SiS_WaitVBRetrace(SiS_Pr,HwInfo); + if(SiS_Pr->SiS_VBType & (VB_SIS302LV | VB_SIS302ELV)) { + SiS_GenericDelay(SiS_Pr, 0x500); + } + SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x26,0x01); } - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x1F,tempah); - - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); - - } else { - - SiS_VBLongWait(SiS_Pr); - SiS_DisplayOn(SiS_Pr); - SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7F); - SiS_VBLongWait(SiS_Pr); - } - } /* 315, 330 */ + SiS_SetReg(SiS_Pr->SiS_P3c4,0x06,pushax); + SiS_DisplayOn(SiS_Pr); + SiS_SetRegByte(SiS_Pr->SiS_P3c6,0xff); + + } + + if(!(SiS_WeHaveBacklightCtrl(SiS_Pr,HwInfo))) { + SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x00,0x7f); + } #endif /* SIS315H */ @@ -4566,9 +4436,9 @@ } temp = SiS_GetReg(SiS_Pr->SiS_P3c4,0x32) & 0xDF; /* lock mode */ - if(SiS_BridgeInSlave(SiS_Pr)) { + if(SiS_BridgeInSlavemode(SiS_Pr)) { tempah = SiS_GetReg(SiS_Pr->SiS_P3d4,0x30); - if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; + if(!(tempah & SetCRT2ToRAMDAC)) temp |= 0x20; } SiS_SetReg(SiS_Pr->SiS_P3c4,0x32,temp); @@ -4621,7 +4491,7 @@ SiS_DisplayOn(SiS_Pr); SiS_UnLockCRT2(SiS_Pr,HwInfo); SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x02,0xBF); - if(SiS_BridgeInSlave(SiS_Pr)) { + if(SiS_BridgeInSlavemode(SiS_Pr)) { SiS_SetRegAND(SiS_Pr->SiS_Part1Port,0x01,0x1F); } else { SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x01,0x1F,0x40); @@ -4653,6 +4523,10 @@ #ifdef SIS315H /* 315 series */ + if(!(SiS_IsNotM650orLater(SiS_Pr,HwInfo))) { + SiS_SetRegOR(SiS_Pr->SiS_Part1Port,0x4c,0x18); + } + if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { if(SiS_CRT2IsLCD(SiS_Pr, HwInfo)) { SiS_SetRegSR11ANDOR(SiS_Pr,HwInfo,0xFB,0x00); @@ -5234,14 +5108,22 @@ static void SiS_SetCRT2FIFO_310(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo) { - USHORT temp; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x01,0x3B); - temp = 0x04; - if(HwInfo->jChipType >= SIS_661) { - if((SiS_GetReg(SiS_Pr->SiS_P3d4,0x5c) & 0xf8) == 0x80) temp = 0x44; + if( (HwInfo->jChipType == SIS_760) && + (SiS_Pr->SiS_SysFlags & SF_760LFB) && + (SiS_Pr->SiS_ModeType == Mode32Bpp) && + (SiS_Pr->SiS_VGAHDE >= 1280) && + (SiS_Pr->SiS_VGAVDE >= 1024) ) { + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x2f,0x03); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x01,0x3b); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x4d,0xc0); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x2f,0x01); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x4d,0xc0); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x02,0x6e); + } else { + SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x02,~0x3f,0x04); } - SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x02,~0x3f,temp); + } #endif @@ -5250,10 +5132,10 @@ { ULONG tempax,tempbx; - tempbx = ((SiS_Pr->SiS_VGAVT - SiS_Pr->SiS_VGAVDE) * SiS_Pr->SiS_RVBHCMAX) & 0xFFFF; + tempbx = (SiS_Pr->SiS_VGAVT - SiS_Pr->SiS_VGAVDE) * SiS_Pr->SiS_RVBHCMAX; tempax = (SiS_Pr->SiS_VT - SiS_Pr->SiS_VDE) * SiS_Pr->SiS_RVBHCFACT; tempax = (tempax * SiS_Pr->SiS_HT) / tempbx; - return((USHORT) tempax); + return((USHORT)tempax); } /* Set Part 1 / SiS bridge slave mode */ @@ -6143,7 +6025,7 @@ PSIS_HW_INFO HwInfo, USHORT RefreshRateTableIndex) { UCHAR *ROMAddr = HwInfo->pjVirtualRomBase; - USHORT temp=0, tempax=0, tempbx=0, tempcx=0; + USHORT temp=0, tempax=0, tempbx=0, tempcx=0, bridgeadd=0; USHORT pushbx=0, CRT1Index=0, modeflag, resinfo=0; #ifdef SIS315H USHORT tempbl=0; @@ -6200,40 +6082,8 @@ tempbx = pushbx + tempcx; tempcx <<= 1; tempcx += tempbx; - - if(SiS_Pr->SiS_VBType & VB_SISVB) { - - if(SiS_Pr->UseCustomMode) { - tempbx = SiS_Pr->CHSyncStart + 12; - tempcx = SiS_Pr->CHSyncEnd + 12; - } - - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { - unsigned char cr4, cr14, cr5, cr15; - if(SiS_Pr->UseCustomMode) { - cr4 = SiS_Pr->CCRT1CRTC[4]; - cr14 = SiS_Pr->CCRT1CRTC[14]; - cr5 = SiS_Pr->CCRT1CRTC[5]; - cr15 = SiS_Pr->CCRT1CRTC[15]; - } else { - cr4 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[4]; - cr14 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14]; - cr5 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[5]; - cr15 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[15]; - } - tempbx = ((cr4 | ((cr14 & 0xC0) << 2)) - 1) << 3; - tempcx = (((cr5 & 0x1F) | ((cr15 & 0x04) << (6-2))) - 1) << 3; - } - - if(SiS_Pr->SiS_TVMode & TVSetNTSC1024) { - tempbx = 1040; - tempcx = 1044; - } - - } - - temp = tempbx & 0x00FF; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0B,temp); /* CRT2 Horizontal Retrace Start */ + + bridgeadd = 12; #endif /* SIS300 */ @@ -6254,11 +6104,8 @@ } } tempcx--; - - temp = tempcx & 0xff; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x08,temp); /* CRT2 Horizontal Total */ - - temp = ((tempcx & 0xff00) >> 8) << 4; + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x08,tempcx); /* CRT2 Horizontal Total */ + temp = (tempcx >> 4) & 0xF0; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x09,0x0F,temp); /* CRT2 Horizontal Total Overflow [7:4] */ tempcx = SiS_Pr->SiS_VGAHT; /* BTVGA2HDEE 0x0A,0x0C */ @@ -6270,85 +6117,83 @@ tempcx >>= 1; } tempbx += 16; - - temp = tempbx & 0xff; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0A,temp); /* CRT2 Horizontal Display Enable End */ + + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0A,tempbx); /* CRT2 Horizontal Display Enable End */ pushbx = tempbx; tempcx >>= 1; tempbx += tempcx; tempcx += tempbx; + + bridgeadd = 16; if(SiS_Pr->SiS_VBType & VB_SISVB) { - if(HwInfo->jChipType >= SIS_661) { if((SiS_Pr->SiS_LCDResInfo == Panel_1600x1200) || (SiS_Pr->SiS_LCDResInfo == Panel_1280x1024)) { if(resinfo == SIS_RI_1280x1024) { - tempcx = 0x30; + tempcx = (tempcx & 0xff00) | 0x30; } else if(resinfo == SIS_RI_1600x1200) { - tempcx = 0xff; + tempcx = (tempcx & 0xff00) | 0xff; } } } - - if(SiS_Pr->UseCustomMode) { - tempbx = SiS_Pr->CHSyncStart + 16; - tempcx = SiS_Pr->CHSyncEnd + 16; - tempax = SiS_Pr->SiS_VGAHT; - if(modeflag & HalfDCLK) tempax >>= 1; - tempax--; - if(tempcx > tempax) tempcx = tempax; - } - - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { - unsigned char cr4, cr14, cr5, cr15; - if(SiS_Pr->UseCustomMode) { - cr4 = SiS_Pr->CCRT1CRTC[4]; - cr14 = SiS_Pr->CCRT1CRTC[14]; - cr5 = SiS_Pr->CCRT1CRTC[5]; - cr15 = SiS_Pr->CCRT1CRTC[15]; - } else { - cr4 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[4]; - cr14 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14]; - cr5 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[5]; - cr15 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[15]; - } - tempbx = ((cr4 | ((cr14 & 0xC0) << 2)) - 3) << 3; /* (VGAHRS-3)*8 */ - tempcx = (((cr5 & 0x1f) | ((cr15 & 0x04) << (5-2))) - 3) << 3; /* (VGAHRE-3)*8 */ - tempcx &= 0x00FF; - tempcx |= (tempbx & 0xFF00); - tempbx += 16; - tempcx += 16; - tempax = SiS_Pr->SiS_VGAHT; - if(modeflag & HalfDCLK) tempax >>= 1; - tempax--; - if(tempcx > tempax) tempcx = tempax; - } - - if(SiS_Pr->SiS_TVMode & TVSetNTSC1024) { - tempbx = 1040; - tempcx = 1044; /* HWCursor bug! */ - } - } - temp = tempbx & 0xff; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0B,temp); /* CRT2 Horizontal Retrace Start */ #endif /* SIS315H */ } /* 315/330 series */ - - tempax = tempbx & 0xFF00; - tempbx = pushbx; - tempbx = (tempbx & 0x00FF) | ((tempbx & 0xFF00) << 4); - tempax |= (tempbx & 0xFF00); - temp = (tempax & 0xFF00) >> 8; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0C,temp); - - temp = tempcx & 0x00FF; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0D,temp); /* CRT2 Horizontal Retrace End */ - + + if(SiS_Pr->SiS_VBType & VB_SISVB) { + + if(SiS_Pr->UseCustomMode) { + tempbx = SiS_Pr->CHSyncStart + bridgeadd; + tempcx = SiS_Pr->CHSyncEnd + bridgeadd; + tempax = SiS_Pr->SiS_VGAHT; + if(modeflag & HalfDCLK) tempax >>= 1; + tempax--; + if(tempcx > tempax) tempcx = tempax; + } + + if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { + unsigned char cr4, cr14, cr5, cr15; + if(SiS_Pr->UseCustomMode) { + cr4 = SiS_Pr->CCRT1CRTC[4]; + cr14 = SiS_Pr->CCRT1CRTC[14]; + cr5 = SiS_Pr->CCRT1CRTC[5]; + cr15 = SiS_Pr->CCRT1CRTC[15]; + } else { + cr4 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[4]; + cr14 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[14]; + cr5 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[5]; + cr15 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[15]; + } + tempbx = ((cr4 | ((cr14 & 0xC0) << 2)) - 3) << 3; /* (VGAHRS-3)*8 */ + tempcx = (((cr5 & 0x1f) | ((cr15 & 0x04) << (5-2))) - 3) << 3; /* (VGAHRE-3)*8 */ + tempcx &= 0x00FF; + tempcx |= (tempbx & 0xFF00); + tempbx += bridgeadd; + tempcx += bridgeadd; + tempax = SiS_Pr->SiS_VGAHT; + if(modeflag & HalfDCLK) tempax >>= 1; + tempax--; + if(tempcx > tempax) tempcx = tempax; + } + + if(SiS_Pr->SiS_TVMode & TVSetNTSC1024) { + tempbx = 1040; + tempcx = 1044; /* HWCursor bug! */ + } + + } + + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0B,tempbx); /* CRT2 Horizontal Retrace Start */ + + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0D,tempcx); /* CRT2 Horizontal Retrace End */ + + temp = ((tempbx >> 8) & 0x0F) | ((pushbx >> 4) & 0xF0); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0C,temp); /* Overflow */ + /* 2. Vertical setup */ tempcx = SiS_Pr->SiS_VGAVT - 1; @@ -6372,12 +6217,10 @@ SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0E,temp); /* CRT2 Vertical Total */ tempbx = SiS_Pr->SiS_VGAVDE - 1; - temp = tempbx & 0x00FF; - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0F,temp); /* CRT2 Vertical Display Enable End */ + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x0F,tempbx); /* CRT2 Vertical Display Enable End */ - temp = ((tempbx & 0xFF00) << 3) >> 8; - temp |= ((tempcx & 0xFF00) >> 8); - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x12,temp); /* Overflow (and HWCursor Test Mode) */ + temp = ((tempbx >> 5) & 0x38) | ((tempcx >> 8) & 0x07); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x12,temp); /* Overflow */ if((HwInfo->jChipType >= SIS_315H) && (HwInfo->jChipType < SIS_661)) { tempbx++; @@ -6396,37 +6239,33 @@ } if(SiS_Pr->SiS_VBType & VB_SISVB) { - if(SiS_Pr->UseCustomMode) { tempbx = SiS_Pr->CVSyncStart; - tempcx = (tempcx & 0xFF00) | (SiS_Pr->CVSyncEnd & 0x00FF); + tempcx = SiS_Pr->CVSyncEnd; } - if(SiS_Pr->SiS_VBInfo & SetCRT2ToRAMDAC) { - unsigned char cr8, cr7, cr13, cr9; + unsigned char cr8, cr7, cr13; if(SiS_Pr->UseCustomMode) { - cr8 = SiS_Pr->CCRT1CRTC[8]; - cr7 = SiS_Pr->CCRT1CRTC[7]; - cr13 = SiS_Pr->CCRT1CRTC[13]; - cr9 = SiS_Pr->CCRT1CRTC[9]; + cr8 = SiS_Pr->CCRT1CRTC[8]; + cr7 = SiS_Pr->CCRT1CRTC[7]; + cr13 = SiS_Pr->CCRT1CRTC[13]; + tempcx = SiS_Pr->CCRT1CRTC[9]; } else { - cr8 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[8]; - cr7 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[7]; - cr13 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[13]; - cr9 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[9]; + cr8 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[8]; + cr7 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[7]; + cr13 = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[13]; + tempcx = SiS_Pr->SiS_CRT1Table[CRT1Index].CR[9]; } tempbx = cr8; - if(cr7 & 0x04) tempbx |= 0x0100; - if(cr7 & 0x80) tempbx |= 0x0200; + if(cr7 & 0x04) tempbx |= 0x0100; + if(cr7 & 0x80) tempbx |= 0x0200; if(cr13 & 0x08) tempbx |= 0x0400; - tempcx = (tempcx & 0xFF00) | (cr9 & 0x00FF); - } + } } SiS_SetReg(SiS_Pr->SiS_Part1Port,0x10,tempbx); /* CRT2 Vertical Retrace Start */ - temp = ((tempbx & 0xFF00) >> 8) << 4; - temp |= (tempcx & 0x000F); - SiS_SetReg(SiS_Pr->SiS_Part1Port,0x11,temp); /* CRT2 Vert. Retrace End; Overflow; "Enable CRTC Check" */ + temp = ((tempbx >> 4) & 0x70) | (tempcx & 0x0F); + SiS_SetReg(SiS_Pr->SiS_Part1Port,0x11,temp); /* CRT2 Vert. Retrace End; Overflow */ /* 3. Panel delay compensation */ @@ -6436,7 +6275,6 @@ if(SiS_Pr->SiS_VBType & VB_SISVB) { temp = 0x20; - if(HwInfo->jChipType == SIS_300) { temp = 0x10; if(SiS_Pr->SiS_LCDResInfo == Panel_1024x768) temp = 0x2c; @@ -6447,10 +6285,10 @@ } if(SiS_Pr->SiS_LCDResInfo == Panel_1280x960) temp = 0x24; if(SiS_Pr->SiS_LCDResInfo == Panel_Custom) temp = 0x2c; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp = 0x08; + if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) temp = 0x08; if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) temp = 0x2c; - else temp = 0x20; + if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) temp = 0x2c; + else temp = 0x20; } if(SiS_Pr->SiS_UseROM) { if(ROMAddr[0x220] & 0x80) { @@ -6521,8 +6359,8 @@ } /* < 661 */ tempax = 0; - if (modeflag & DoubleScanMode) tempax |= 0x80; - if (modeflag & HalfDCLK) tempax |= 0x40; + if(modeflag & DoubleScanMode) tempax |= 0x80; + if(modeflag & HalfDCLK) tempax |= 0x40; SiS_SetRegANDOR(SiS_Pr->SiS_Part1Port,0x2C,0x3f,tempax); #endif /* SIS315H */ @@ -6585,7 +6423,6 @@ if(SiS_Pr->SiS_TVMode & TVSetYPbPr525i) tableptr = SiS_Part2CLVX_3; else if(SiS_Pr->SiS_TVMode & TVSetYPbPr525p) tableptr = SiS_Part2CLVX_3; else tableptr = SiS_Part2CLVX_5; - } else if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { tableptr = SiS_Part2CLVX_6; } @@ -6865,9 +6702,9 @@ SiS_SetGroup2(SiS_Private *SiS_Pr, USHORT ModeNo, USHORT ModeIdIndex,USHORT RefreshRateTableIndex, PSIS_HW_INFO HwInfo) { - USHORT i, j, tempax, tempbx, tempcx, temp; - USHORT push1, push2, modeflag, crt2crtc, bridgeoffset; - ULONG longtemp, tempeax; + USHORT i, j, tempax, tempbx, tempcx, tempch, tempcl, temp; + USHORT push2, modeflag, crt2crtc, bridgeoffset; + ULONG longtemp; const UCHAR *PhasePoint; const UCHAR *TimingPoint; #ifdef SIS315H @@ -7034,47 +6871,38 @@ if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempcx >>= 1; tempcx--; if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) tempcx--; - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x1B,(tempcx & 0xff)); + SiS_SetReg(SiS_Pr->SiS_Part2Port,0x1B,tempcx); SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1D,0xF0,((tempcx >> 8) & 0x0f)); - - tempcx++; - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) tempcx++; - tempcx >>= 1; - - push1 = tempcx; - + + tempcx = SiS_Pr->SiS_HT >> 1; + if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempcx >>= 1; tempcx += 7; if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) tempcx -= 4; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x22,0x0F,((tempcx << 4) & 0xf0)); tempbx = TimingPoint[j] | (TimingPoint[j+1] << 8); tempbx += tempcx; - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x24,tempbx); - temp = ((tempbx & 0xFF00) >> 8) << 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x25,0x0F,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x25,0x0F,((tempbx >> 4) & 0xf0)); tempbx += 8; if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { tempbx -= 4; tempcx = tempbx; } - temp = (tempbx & 0x00FF) << 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x29,0x0F,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x29,0x0F,((tempbx << 4) & 0xf0)); j += 2; tempcx += (TimingPoint[j] | (TimingPoint[j+1] << 8)); - temp = tempcx & 0x00FF; - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x27,temp); - temp = ((tempcx & 0xFF00) >> 8) << 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x28,0x0F,temp); + SiS_SetReg(SiS_Pr->SiS_Part2Port,0x27,tempcx); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x28,0x0F,((tempcx >> 4) & 0xf0)); tempcx += 8; if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) tempcx -= 4; SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x2A,0x0F,((tempcx << 4) & 0xf0)); - tempcx = push1; - + tempcx = SiS_Pr->SiS_HT >> 1; + if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempcx >>= 1; j += 2; tempcx -= (TimingPoint[j] | ((TimingPoint[j+1]) << 8)); SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x2D,0x0F,((tempcx << 4) & 0xf0)); @@ -7086,54 +6914,47 @@ SiS_SetReg(SiS_Pr->SiS_Part2Port,0x2E,tempcx); tempbx = SiS_Pr->SiS_VDE; - if(SiS_Pr->SiS_VGAVDE == 360) tempbx = 746; - if(SiS_Pr->SiS_VGAVDE == 375) tempbx = 746; - if(SiS_Pr->SiS_VGAVDE == 405) tempbx = 853; - if(HwInfo->jChipType < SIS_315H) { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) tempbx >>= 1; - } else { - if( (SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && - (!(SiS_Pr->SiS_TVMode & (TVSetYPbPr525p|TVSetYPbPr750p))) ) { - tempbx >>= 1; - if(SiS_Pr->SiS_TVMode & TVSetTVSimuMode) { - if(ModeNo <= 0x13) { - if(crt2crtc == 1) tempbx++; - } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + if(SiS_Pr->SiS_VGAVDE == 360) tempbx = 746; + if(SiS_Pr->SiS_VGAVDE == 375) tempbx = 746; + if(SiS_Pr->SiS_VGAVDE == 405) tempbx = 853; + } else if( (SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && + (!(SiS_Pr->SiS_TVMode & (TVSetYPbPr525p|TVSetYPbPr750p))) ) { + tempbx >>= 1; + if(HwInfo->jChipType >= SIS_315H) { + if(SiS_Pr->SiS_TVMode & TVSetTVSimuMode) { + if((ModeNo <= 0x13) && (crt2crtc == 1)) tempbx++; } else if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - if(crt2crtc == 4) { - if(SiS_Pr->SiS_ModeType <= 3) tempbx++; + if(SiS_Pr->SiS_ModeType <= ModeVGA) { + if(crt2crtc == 4) tempbx++; } } } - } - tempbx -= 2; - temp = tempbx & 0x00FF; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - if((ModeNo == 0x2f) || (ModeNo == 0x5d) || (ModeNo == 0x5e)) temp++; - } - } - - if(HwInfo->jChipType < SIS_661) { - /* From 1.10.7w - doesn't make sense */ - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { - if(!(SiS_Pr->SiS_TVMode & TVSetPAL)) { - if(ModeNo == 0x03) temp++; - } + if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { + if((ModeNo == 0x2f) || (ModeNo == 0x5d) || (ModeNo == 0x5e)) tempbx++; + } + if(!(SiS_Pr->SiS_TVMode & TVSetPAL)) { + if(ModeNo == 0x03) tempbx++; /* From 1.10.7w - doesn't make sense */ } } } - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x2F,temp); + tempbx -= 2; + SiS_SetReg(SiS_Pr->SiS_Part2Port,0x2F,tempbx); temp = (tempcx >> 8) & 0x0F; - temp |= (((tempbx >> 8) << 6) & 0xC0); + temp |= ((tempbx >> 2) & 0xC0); if(SiS_Pr->SiS_VBInfo & (SetCRT2ToSVIDEO | SetCRT2ToAVIDEO)) { temp |= 0x10; if(SiS_Pr->SiS_VBInfo & SetCRT2ToAVIDEO) temp |= 0x20; } SiS_SetReg(SiS_Pr->SiS_Part2Port,0x30,temp); + + if(SiS_Pr->SiS_VBType & (VB_SIS301C | VB_SIS302LV | VB_SIS302ELV)) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x10,0xdf,((tempbx & 0x0400) >> 5)); + } +#if 0 /* TEST qqqq */ if(SiS_Pr->SiS_VBInfo & SetCRT2ToYPbPr525750) { for(i=0x01, j=0; i<=0x2D; i++, j++) { @@ -7143,86 +6964,64 @@ SiS_SetReg(SiS_Pr->SiS_Part2Port,i,TimingPoint[j]); } } +#endif if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { tempbx = SiS_Pr->SiS_VDE; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - if(!(SiS_Pr->SiS_TVMode & (TVSetYPbPr525p | TVSetYPbPr750p))) { - tempbx >>= 1; - } + if( (SiS_Pr->SiS_VBInfo & SetCRT2ToTV) && + (!(SiS_Pr->SiS_TVMode & (TVSetYPbPr525p | TVSetYPbPr750p))) ) { + tempbx >>= 1; } tempbx -= 3; - if(SiS_Pr->SiS_VBType & (VB_SIS301C | VB_SIS302LV | VB_SIS302ELV)) { /* Why not 301B/LV? */ - if(HwInfo->jChipType >= SIS_661) { - temp = 0; - if(tempcx & 0x0400) temp |= 0x20; - if(tempbx & 0x0400) temp |= 0x40; - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x10,temp); - } else { - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if((SiS_Pr->SiS_LCDResInfo == Panel_1400x1050) || - (SiS_Pr->SiS_LCDResInfo == Panel_1600x1200) || - (SiS_Pr->SiS_LCDResInfo == Panel_1680x1050)) { - SiS_SetRegOR(SiS_Pr->SiS_Part4Port,0x10,0x60); - } - } - } - } - tempbx &= 0x03ff; - temp = ((tempbx & 0xFF00) >> 8) << 5; - temp |= 0x18; + temp = ((tempbx >> 3) & 0x60) | 0x18; SiS_SetReg(SiS_Pr->SiS_Part2Port,0x46,temp); SiS_SetReg(SiS_Pr->SiS_Part2Port,0x47,tempbx); + + if(SiS_Pr->SiS_VBType & (VB_SIS301C | VB_SIS302LV | VB_SIS302ELV)) { + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x10,0xbf,((tempbx & 0x0400) >> 4)); + } } tempbx = 0; if(!(modeflag & HalfDCLK)) { if(SiS_Pr->SiS_VGAHDE >= SiS_Pr->SiS_HDE) { tempax = 0; - tempbx |= 0x2000; + tempbx |= 0x20; } } - tempcx = 0x0101; + tempch = tempcl = 0x01; if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(SiS_Pr->SiS_VGAHDE >= 1024) { if((!(modeflag & HalfDCLK)) || (HwInfo->jChipType < SIS_315H)) { - tempcx = 0x1920; + tempch = 0x19; + tempcl = 0x20; if(SiS_Pr->SiS_VGAHDE >= 1280) { - tempcx = 0x1420; - tempbx &= ~0x2000; + tempch = 0x14; + tempbx &= ~0x20; } } } } - if(!(tempbx & 0x2000)) { - if(modeflag & HalfDCLK) { - tempcx = (tempcx & 0xFF00) | ((tempcx << 1) & 0x00FF); - } - longtemp = (SiS_Pr->SiS_VGAHDE * ((tempcx & 0xFF00) >> 8)) / (tempcx & 0x00FF); - longtemp <<= 13; - if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - longtemp <<= 3; - } - tempeax = longtemp / SiS_Pr->SiS_HDE; - if(longtemp % SiS_Pr->SiS_HDE) tempeax++; - tempax = (USHORT)tempeax; - tempbx |= (tempax & 0x1F00); - tempcx = (tempax & 0xFF00) >> (8 + 5); + if(!(tempbx & 0x20)) { + if(modeflag & HalfDCLK) tempcl <<= 1; + longtemp = ((SiS_Pr->SiS_VGAHDE * tempch) / tempcl) << 13; + if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) longtemp <<= 3; + tempax = longtemp / SiS_Pr->SiS_HDE; + if(longtemp % SiS_Pr->SiS_HDE) tempax++; + tempbx |= ((tempax >> 8) & 0x1F); + tempcx = tempax >> 13; } SiS_SetReg(SiS_Pr->SiS_Part2Port,0x44,tempax); - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x45,0xC0,(tempbx >> 8)); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x45,0xC0,tempbx); if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { - temp = tempcx & 0x0007; - if(tempbx & 0x2000) temp = 0; - if((HwInfo->jChipType < SIS_661) || (!(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD))) { - temp |= 0x18; - } - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x46,0xE0,temp); + tempcx &= 0x07; + if(tempbx & 0x20) tempcx = 0; + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x46,0xF8,tempcx); if(SiS_Pr->SiS_TVMode & TVSetPAL) { tempbx = 0x0382; @@ -7233,7 +7032,7 @@ } SiS_SetReg(SiS_Pr->SiS_Part2Port,0x4B,tempbx); SiS_SetReg(SiS_Pr->SiS_Part2Port,0x4C,tempcx); - temp = (tempcx & 0x0300) >> (8 - 2); + temp = (tempcx & 0x0300) >> 6; temp |= ((tempbx >> 8) & 0x03); if(SiS_Pr->SiS_VBInfo & SetCRT2ToYPbPr525750) { temp |= 0x10; @@ -7243,7 +7042,7 @@ SiS_SetReg(SiS_Pr->SiS_Part2Port,0x4D,temp); temp = SiS_GetReg(SiS_Pr->SiS_Part2Port,0x43); - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x43,(USHORT)(temp - 3)); + SiS_SetReg(SiS_Pr->SiS_Part2Port,0x43,(temp - 3)); SiS_SetTVSpecial(SiS_Pr, ModeNo); @@ -7258,7 +7057,7 @@ if(SiS_Pr->SiS_TVMode & TVSetPALM) { if(!(SiS_Pr->SiS_TVMode & TVSetNTSC1024)) { temp = SiS_GetReg(SiS_Pr->SiS_Part2Port,0x01); - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x01,temp - 1); + SiS_SetReg(SiS_Pr->SiS_Part2Port,0x01,(temp - 1)); } SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x00,0xEF); } @@ -7277,8 +7076,7 @@ if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempbx >>= 1; tempbx--; /* RHACTE = HDE - 1 */ SiS_SetReg(SiS_Pr->SiS_Part2Port,0x2C,tempbx); - temp = (tempbx & 0xFF00) >> 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x2B,0x0F,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x2B,0x0F,((tempbx >> 4) & 0xf0)); temp = 0x01; if(SiS_Pr->SiS_LCDResInfo == Panel_1280x1024) { @@ -7297,21 +7095,17 @@ tempbx = SiS_Pr->SiS_VDE - 1; SiS_SetReg(SiS_Pr->SiS_Part2Port,0x03,tempbx); - temp = ((tempbx & 0xFF00) >> 8) & 0x07; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x0C,0xF8,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x0C,0xF8,((tempbx >> 8) & 0x07)); tempcx = SiS_Pr->SiS_VT - 1; SiS_SetReg(SiS_Pr->SiS_Part2Port,0x19,tempcx); - - temp = ((tempcx & 0xFF00) >> 8) << 5; - - /* Enable dithering; only do this for 32bpp mode */ + temp = (tempcx >> 3) & 0xE0; if(SiS_Pr->SiS_LCDInfo & LCDRGB18Bit) { + /* Enable dithering; only do this for 32bpp mode */ if(SiS_GetReg(SiS_Pr->SiS_Part1Port,0x00) & 0x01) { temp |= 0x10; } } - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1A,0x0f,temp); SiS_SetRegAND(SiS_Pr->SiS_Part2Port,0x09,0xF0); @@ -7380,7 +7174,7 @@ tempbx -= tempax; /* lcdvdee */ } - /* Non-expanding: lcdvdees = tempcx = VT-1; lcdvdee = tempbx = VDE-1 */ + /* Non-expanding: lcdvdes = tempcx = VT-1; lcdvdee = tempbx = VDE-1 */ #ifdef TWDEBUG xf86DrvMsg(0, X_INFO, "lcdvdes 0x%x lcdvdee 0x%x\n", tempcx, tempbx); @@ -7389,8 +7183,8 @@ SiS_SetReg(SiS_Pr->SiS_Part2Port,0x05,tempcx); /* lcdvdes */ SiS_SetReg(SiS_Pr->SiS_Part2Port,0x06,tempbx); /* lcdvdee */ - temp = ((tempbx & 0xFF00) >> 8) << 3; - temp |= ((tempcx & 0xFF00) >> 8); + temp = (tempbx >> 5) & 0x38; + temp |= ((tempcx >> 8) & 0x07); SiS_SetReg(SiS_Pr->SiS_Part2Port,0x02,temp); tempax = SiS_Pr->SiS_VDE; @@ -7412,7 +7206,10 @@ if(tempax % 4) { tempax >>= 2; tempax++; } else { tempax >>= 2; } tempbx -= (tempax - 1); - } else tempbx -= 10; + } else { + tempbx -= 10; + if(tempbx <= SiS_Pr->SiS_VDE) tempbx = SiS_Pr->SiS_VDE + 1; + } } } if(SiS_Pr->SiS_LCDResInfo == Panel_1024x768) { @@ -7437,9 +7234,9 @@ SiS_SetReg(SiS_Pr->SiS_Part2Port,0x04,tempbx); /* lcdvrs */ - temp = ((tempbx & 0xFF00) >> 8) << 4; + temp = (tempbx >> 4) & 0xF0; tempbx += (tempcx + 1); - temp |= (tempbx & 0x000F); + temp |= (tempbx & 0x0F); if(SiS_Pr->UseCustomMode) { temp &= 0xf0; @@ -7469,9 +7266,8 @@ } } temp += bridgeoffset; - SiS_SetReg(SiS_Pr->SiS_Part2Port,0x1F,temp); /* lcdhdes[7:0] */ - temp = (temp >> 4) & 0xf0; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x20,0x0F,temp); /* lcdhdes [11:8] */ + SiS_SetReg(SiS_Pr->SiS_Part2Port,0x1F,temp); /* lcdhdes */ + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x20,0x0F,((temp >> 4) & 0xf0)); tempcx = SiS_Pr->SiS_HT; tempax = tempbx = SiS_Pr->SiS_HDE; @@ -7494,8 +7290,7 @@ tempbx += bridgeoffset; SiS_SetReg(SiS_Pr->SiS_Part2Port,0x23,tempbx); /* lcdhdee */ - temp = (tempbx >> 8) & 0x0f; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x25,0xF0,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x25,0xF0,((tempbx >> 8) & 0x0f)); tempcx = (tempcx - tempax) >> 2; @@ -7521,8 +7316,7 @@ #endif SiS_SetReg(SiS_Pr->SiS_Part2Port,0x1C,tempbx); /* lcdhrs */ - temp = (tempbx & 0x0F00) >> 4; - SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1D,0x0F,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part2Port,0x1D,0x0F,((tempbx >> 4) & 0xf0)); tempbx = push2; @@ -7646,7 +7440,8 @@ { USHORT temp, temp1, resinfo = 0; - if(!(SiS_Pr->SiS_VBType & (VB_SIS301C | VB_SIS302ELV))) return; + if(!(SiS_Pr->SiS_VBType & VB_SIS301C)) return; + if(!(SiS_Pr->SiS_VBInfo & (SetCRT2ToHiVision | SetCRT2ToYPbPr525750))) return; if(ModeNo > 0x13) { resinfo = SiS_Pr->SiS_EModeIDTable[ModeIdIndex].Ext_RESINFO; @@ -7737,7 +7532,7 @@ SiS_SetGroup4(SiS_Private *SiS_Pr, USHORT ModeNo, USHORT ModeIdIndex, USHORT RefreshRateTableIndex, PSIS_HW_INFO HwInfo) { - USHORT tempax,tempcx,tempbx,modeflag,temp,temp2,resinfo; + USHORT tempax,tempcx,tempbx,modeflag,temp,resinfo; ULONG tempebx,tempeax,templong; if(ModeNo <= 0x13) { @@ -7783,167 +7578,138 @@ } } - temp = SiS_Pr->SiS_RVBHCFACT; - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x13,temp); + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x13,SiS_Pr->SiS_RVBHCFACT); tempbx = SiS_Pr->SiS_RVBHCMAX; SiS_SetReg(SiS_Pr->SiS_Part4Port,0x14,tempbx); - temp2 = (tempbx >> 1) & 0x0080; + temp = (tempbx >> 1) & 0x80; tempcx = SiS_Pr->SiS_VGAHT - 1; SiS_SetReg(SiS_Pr->SiS_Part4Port,0x16,tempcx); - temp2 |= (((tempcx & 0xFF00) >> 8) << 3) & 0x00ff; + temp |= ((tempcx >> 5) & 0x78); tempcx = SiS_Pr->SiS_VGAVT - 1; if(!(SiS_Pr->SiS_VBInfo & SetCRT2ToTV)) tempcx -= 5; SiS_SetReg(SiS_Pr->SiS_Part4Port,0x17,tempcx); - temp = temp2 | (tempcx >> 8); + temp |= ((tempcx >> 8) & 0x07); SiS_SetReg(SiS_Pr->SiS_Part4Port,0x15,temp); tempbx = SiS_Pr->SiS_VGAHDE; - if(modeflag & HalfDCLK) tempbx >>= 1; - if(HwInfo->jChipType >= SIS_661) { - if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempbx >>= 1; - } - - temp = 0xA0; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { + if(modeflag & HalfDCLK) tempbx >>= 1; + if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempbx >>= 1; + + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { temp = 0; - if(tempbx > 800) { - temp = 0xA0; - if(tempbx != 1024) { - temp = 0xC0; - if(tempbx != 1280) temp = 0; - } - } - } else if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { - if(tempbx <= 800) { - temp = 0x80; - } + if(tempbx > 800) temp = 0x60; + } else if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { + temp = 0; + if(tempbx == 1024) temp = 0xA0; + else if(tempbx > 1024) temp = 0xC0; + } else if(SiS_Pr->SiS_TVMode & (TVSetYPbPr525p | TVSetYPbPr750p)) { + temp = 0; + if(tempbx >= 1280) temp = 0x40; + else if(tempbx >= 1024) temp = 0x20; } else { temp = 0x80; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - temp = 0; - if(tempbx > 800) temp = 0x60; - } - } - - if(SiS_Pr->SiS_TVMode & (TVSetYPbPr525p | TVSetYPbPr750p)) { - temp = 0; - if(SiS_Pr->SiS_VGAHDE == 1024) temp = 0x20; - } - - if(HwInfo->jChipType < SIS_661) { - if(SiS_IsDualLink(SiS_Pr, HwInfo)) temp = 0; + if(tempbx >= 1024) temp = 0xA0; } if(SiS_Pr->SiS_VBType & VB_SIS301) { - if(SiS_Pr->SiS_LCDResInfo != Panel_1280x1024) - temp |= 0x0A; + if(SiS_Pr->SiS_LCDResInfo != Panel_1280x1024) temp |= 0x0A; } SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x0E,0x10,temp); - + + tempeax = SiS_Pr->SiS_VGAVDE; tempebx = SiS_Pr->SiS_VDE; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToHiVision) { if(!(temp & 0xE0)) tempebx >>=1; - } - + } + tempcx = SiS_Pr->SiS_RVBHRS; - temp = tempcx & 0x00FF; - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x18,temp); - - tempeax = SiS_Pr->SiS_VGAVDE; - tempcx |= 0x4000; + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x18,tempcx); + tempcx >>= 8; + tempcx |= 0x40; + if(tempeax <= tempebx) { - tempcx ^= 0x4000; + tempcx ^= 0x40; } else { tempeax -= tempebx; } - templong = (tempeax * 256 * 1024) % tempebx; - tempeax = (tempeax * 256 * 1024) / tempebx; - tempebx = tempeax; - if(templong != 0) tempebx++; + tempeax *= (256 * 1024); + templong = tempeax % tempebx; + tempeax /= tempebx; + if(templong) tempeax++; - temp = (USHORT)(tempebx & 0x000000FF); + temp = (USHORT)(tempeax & 0x000000FF); SiS_SetReg(SiS_Pr->SiS_Part4Port,0x1B,temp); - temp = (USHORT)((tempebx & 0x0000FF00) >> 8); + temp = (USHORT)((tempeax & 0x0000FF00) >> 8); SiS_SetReg(SiS_Pr->SiS_Part4Port,0x1A,temp); - - tempbx = (USHORT)(tempebx >> 16); - temp = tempbx & 0x00FF; - temp <<= 4; - temp |= ((tempcx & 0xFF00) >> 8); + temp = (USHORT)((tempeax >> 12) & 0x70); /* sic! */ + temp |= (tempcx & 0x4F); SiS_SetReg(SiS_Pr->SiS_Part4Port,0x19,temp); if(SiS_Pr->SiS_VBType & VB_SIS301BLV302BLV) { SiS_SetReg(SiS_Pr->SiS_Part4Port,0x1C,0x28); + /* Calc Linebuffer max address and set/clear decimode */ tempbx = 0; + if(SiS_Pr->SiS_TVMode & (TVSetHiVision | TVSetYPbPr750p)) tempbx = 0x08; tempax = SiS_Pr->SiS_VGAHDE; - if(modeflag & HalfDCLK) tempax >>= 1; + if(modeflag & HalfDCLK) tempax >>= 1; if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempax >>= 1; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { - if(tempax > 800) tempax -= 800; - } else /* if(SiS_Pr->SiS_VBInfo & TvNoHiviNoYPbPr) */ { /* 651+301C */ - if(tempax > 800) { - tempbx = 8; + if(tempax > 800) { + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + tempax -= 800; + } else { /* 651+301C: Only if TVNoHiviNoYPbPr */ + tempbx = 0x08; if(tempax == 1024) tempax *= 25; else tempax *= 20; temp = tempax % 32; tempax /= 32; - tempax--; - if (temp!=0) tempax++; - } - } + if(temp) tempax++; + tempax++; + if((SiS_Pr->SiS_VBInfo & SetCRT2ToTVNoYPbPrHiVision) || + (SiS_Pr->SiS_TVMode & TVSetYPbPr525i)) { + if(resinfo == SIS_RI_1024x768) { + /* Otherwise white line at right edge */ + tempax = (tempax & 0xff00) | 0x20; + } + } + } + } tempax--; - temp = ((tempax & 0xFF00) >> 8) & 0x03; - if(SiS_Pr->SiS_VBInfo & SetCRT2ToTVNoYPbPrHiVision) { /* From 1.10.7w */ - if(ModeNo > 0x13) { /* From 1.10.7w */ - if(resinfo == SIS_RI_1024x768) tempax = 0x1f; /* From 1.10.7w */ - /* ax normally 0x1e */ /* From 1.10.7w */ - } /* From 1.10.7w */ - } /* From 1.10.7w */ - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x1D,tempax & 0x00FF); - temp <<= 4; - temp |= tempbx; + temp = ((tempax >> 4) & 0x30) | tempbx; + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x1D,tempax); SiS_SetReg(SiS_Pr->SiS_Part4Port,0x1E,temp); - if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { - if(IS_SIS550650740660) { - temp = 0x0026; /* 1.10.7w; 1.10.8r; needs corresponding code in Dis/EnableBridge! */ - } else { - temp = 0x0036; - } - } else { - temp = 0x0036; + temp = 0x0036; tempbx = 0xD0; + if((IS_SIS550650740660) && (SiS_Pr->SiS_VBType & VB_SIS301LV302LV)) { + temp = 0x0026; tempbx = 0xC0; /* See En/DisableBridge() */ } if(SiS_Pr->SiS_VBInfo & SetCRT2ToTV) { if(!(SiS_Pr->SiS_TVMode & (TVSetNTSC1024 | TVSetHiVision | TVSetYPbPr750p | TVSetYPbPr525p))) { temp |= 0x01; if(SiS_Pr->SiS_VBInfo & SetInSlaveMode) { if(!(SiS_Pr->SiS_TVMode & TVSetTVSimuMode)) { - temp &= 0xFE; + temp &= ~0x01; } } } } - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x1F,0xC0,temp); + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x1F,tempbx,temp); - tempbx = SiS_Pr->SiS_HT; + tempbx = SiS_Pr->SiS_HT >> 1; if(SiS_IsDualLink(SiS_Pr, HwInfo)) tempbx >>= 1; - tempbx >>= 1; tempbx -= 2; - temp = ((tempbx & 0x0700) >> 8) << 3; + SiS_SetReg(SiS_Pr->SiS_Part4Port,0x22,tempbx); + temp = (tempbx >> 5) & 0x38; SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x21,0xC0,temp); - temp = tempbx & 0x00FF; - SiS_SetReg(SiS_Pr->SiS_Part4Port,0x22,temp); if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { @@ -9881,7 +9647,7 @@ } break; case 1280: - if(yres == 1024) { + if(yres == 1024) { paneltype = Panel_1280x1024; checkexpand = TRUE; } else if(yres == 960) { @@ -10328,14 +10094,17 @@ if(!SiS_Pr->CP_PreferredX) SiS_Pr->CP_PreferredX = SiS_Pr->CP_MaxX; if(!SiS_Pr->CP_PreferredY) SiS_Pr->CP_PreferredY = SiS_Pr->CP_MaxY; SiS_SetRegOR(SiS_Pr->SiS_P3d4,0x32,0x08); - SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x36,0xf0,paneltype); + SiS_SetReg(SiS_Pr->SiS_P3d4,0x36,paneltype); cr37 &= 0xf1; - SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x37,0xf3,cr37); + SiS_SetRegANDOR(SiS_Pr->SiS_P3d4,0x37,0x0c,cr37); SiS_Pr->PanelSelfDetected = TRUE; #ifdef TWDEBUG xf86DrvMsgVerb(pSiS->pScrn->scrnIndex, X_PROBED, 3, - "CRT2: [DDC LCD results: 0x%02x, 0x%02x]\n", paneltype, cr37); + "CRT2: [DDC LCD results: 0x%02x, 0x%02x]\n", paneltype, cr37); #endif + } else { + SiS_SetRegAND(SiS_Pr->SiS_P3d4,0x32,~0x08); + SiS_SetReg(SiS_Pr->SiS_P3d4,0x36,0x00); } return 0; } @@ -10944,6 +10713,7 @@ delay = SiS310_LCDDelayCompensation_301[myindex]; if(SiS_Pr->SiS_VBType & VB_SIS301LV302LV) { if(IS_SIS740) delay = 0x01; + else if(HwInfo->jChipType <= SIS_315PRO) delay = SiS310_LCDDelayCompensation_3xx301LV[myindex]; else delay = SiS310_LCDDelayCompensation_650301LV[myindex]; } else if(SiS_Pr->SiS_VBType & VB_SIS301C) { if(IS_SIS740) delay = 0x01; /* ? */ @@ -11329,7 +11099,9 @@ } if(index < 25) index = 25; index = ((index / 25) - 1) << 1; - if(SiS_Pr->SiS_VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToLCD)) index++; + if((ROMAddr[0x5b] & 0x80) || (SiS_Pr->SiS_VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToLCD))) { + index++; + } romptr = SISGETROMW(0x104); /* 0x4ae */ delay = ROMAddr[romptr + index]; if(SiS_Pr->SiS_VBInfo & (SetCRT2ToRAMDAC | SetCRT2ToLCD)) { @@ -11386,16 +11158,18 @@ } else delay = 0x0000; } + + /* Override by detected or user-set values */ + /* (but only if, for some reason, we can't read value from BIOS) */ + if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (SiS_Pr->PDC != -1)) { + delay = SiS_Pr->PDC & 0x1f; + } + if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) && (SiS_Pr->PDCA != -1)) { + delay = (SiS_Pr->PDCA & 0x1f) << 8; + } } - - /* Override by detected or user-set values */ - if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) && (SiS_Pr->PDC != -1)) { - delay = SiS_Pr->PDC & 0x1f; - } - if((SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) && (SiS_Pr->PDCA != -1)) { - delay = (SiS_Pr->PDCA & 0x1f) << 8; - } + } if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCDA) { @@ -11477,8 +11251,10 @@ } SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x24,temp2,temp1); } - temp1 = (ROMAddr[romptr + 1] & 0x80) >> 1; - SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x0d,0xbf,temp1); + if(SiS_Pr->SiS_VBInfo & SetCRT2ToLCD) { + temp1 = (ROMAddr[romptr + 1] & 0x80) >> 1; + SiS_SetRegANDOR(SiS_Pr->SiS_Part4Port,0x0d,0xbf,temp1); + } } } diff -urN linux-2.4.26/drivers/video/sis/init301.h linux-2.4.27/drivers/video/sis/init301.h --- linux-2.4.26/drivers/video/sis/init301.h 2004-04-14 06:05:40.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/init301.h 2004-08-07 16:26:05.930399208 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Data and prototypes for init301.c * @@ -88,8 +89,8 @@ 0x06,0x14,0x0d,0x04,0x0a,0x00,0x85,0x1b, 0x0c,0x50,0x00,0x97,0x00,0xda,0x4a,0x17, 0x7d,0x05,0x4b,0x00,0x00,0xe2,0x00,0x02, - 0x03,0x0a,0x65,0x8d /*0x9d*/,0x08,0x92,0x8f,0x40, - 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x50 /*0x53*/, + 0x03,0x0a,0x65,0x9d /*0x8d*/,0x08,0x92,0x8f,0x40, + 0x60,0x80,0x14,0x90,0x8c,0x60,0x14,0x53 /*0x50*/, 0x00,0x40,0x44,0x00,0xdb,0x02,0x3b,0x00 }, { @@ -217,49 +218,44 @@ 0x00,0x04, 0x04,0x1A,0x04,0x7E,0x02,0x1B,0x05,0x7E,0x01,0x1A,0x07,0x7E,0x00,0x1A,0x09,0x7D, 0x7F,0x19,0x0B,0x7D,0x7E,0x18,0x0D,0x7D,0x7D,0x17,0x10,0x7C,0x7D,0x15,0x12,0x7C, - 0x7C,0x14,0x14,0x7C,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x1D /* 0x7D? */ ,0x7C,0x0D,0x18,0x7F, + 0x7C,0x14,0x14,0x7C,0x7C,0x12,0x15,0x7D,0x7C,0x10,0x17,0x7D /* 0x1D(6330)? */ ,0x7C,0x0D,0x18,0x7F, 0x7D,0x0B,0x19,0x7F,0x7D,0x09,0x1A,0x00,0x7D,0x07,0x1A,0x02,0x7E,0x05,0x1B,0x02, 0xFF,0xFF, }; - #ifdef SIS315H -/* 661 et al LCD data structure */ +/* 661 et al LCD data structure (0.94.0) */ static const UCHAR SiS_LCDStruct661[] = { /* 1024x768 */ /* type|CR37| HDE | VDE | HT | VT | hss | hse */ 0x02,0xC0,0x00,0x04,0x00,0x03,0x40,0x05,0x26,0x03,0x10,0x00,0x88, - 0x00,0x02,0x00,0x06,0x00,0x41,0x5A,0x64,0x37,0x6E,0x05,0x6E,0x04, + 0x00,0x02,0x00,0x06,0x00,0x41,0x5A,0x64,0x00,0x00,0x00,0x00,0x04, /* | vss | vse |clck| clock |CRT2DataP|CRT2DataP|idx */ /* VESA non-VESA noscale */ /* 1280x1024 */ - 0x03,0xE0,0x00,0x05,0x00,0x04,0x98,0x06,0x2A,0x04,0x30,0x00,0x70, - 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x5F,0x6F,0xDB,0x6F,0x08, + 0x03,0xC0,0x00,0x05,0x00,0x04,0x98,0x06,0x2A,0x04,0x30,0x00,0x70, + 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x00,0x00,0x00,0x00,0x08, /* 1400x1050 */ 0x09,0x20,0x78,0x05,0x1A,0x04,0x98,0x06,0x2A,0x04,0x18,0x00,0x38, - 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x35,0x70,0x00,0x00,0x09, + 0x00,0x01,0x00,0x03,0x00,0x6C,0xF8,0x2F,0x00,0x00,0x00,0x00,0x09, /* 1600x1200 */ - 0x0B,0xE0,0x40,0x06,0xB0,0x04,0x70,0x08,0xE2,0x04,0x40,0x00,0xC0, - 0x00,0x01,0x00,0x03,0x00,0xA2,0x70,0x24,0x07,0x71,0x00,0x00,0x0B, + 0x0B,0xC0,0x40,0x06,0xB0,0x04,0x70,0x08,0xE2,0x04,0x40,0x00,0xC0, + 0x00,0x01,0x00,0x03,0x00,0xA2,0x70,0x24,0x00,0x00,0x00,0x00,0x0B, /* 1280x768 */ 0x0A,0xC0,0x00,0x05,0x00,0x03,0x80,0x05,0x26,0x03,0x10,0x00,0x40, - 0x00,0x03,0x00,0x06,0x00,0x44,0x63,0x46,0xEB,0x6E,0xA5,0x6E,0x06, + 0x00,0x03,0x00,0x06,0x00,0x44,0x63,0x46,0x00,0x00,0x00,0x00,0x06, /* 1280x720 */ - 0x0E,0xE0,0x00,0x05,0xD0,0x02,0x40,0x05,0x26,0x03,0x10,0x00,0x02, - 0x00,0x01,0x00,0x06,0x00,0x41,0x5A,0x64,0x69,0x6E,0x00,0x00,0x05, -#if 0 /* 0.92: */ - 0x0E,0xE0,0x00,0x05,0xD0,0x02,0x72,0x06,0xEE,0x02,0x2A,0x00,0x3F, - 0x00,0x01,0x00,0x06,0x00,0x41,0x00,0x00,0xA7,0x6A,0x00,0x00,0x05, -#endif + 0x0E,0xE0,0x00,0x05,0xD0,0x02,0x80,0x05,0x26,0x03,0x10,0x00,0x02, + 0x00,0x01,0x00,0x06,0x00,0x45,0x9C,0x62,0x00,0x00,0x00,0x00,0x05, /* 1280x800 */ - 0x0C,0xE0,0x00,0x05,0x20,0x03,0x80,0x05,0x30,0x03,0x15,0x00,0x18, - 0x00,0x04,0x00,0x03,0x00,0x45,0x9C,0x62,0x31,0x6F,0x00,0x00,0x07, + 0x0C,0xE0,0x00,0x05,0x20,0x03,0x80,0x05,0x30,0x03,0x10,0x00,0x40, + 0x00,0x04,0x00,0x03,0x00,0x45,0x9C,0x62,0x00,0x00,0x00,0x00,0x07, /* 1680x1050 */ 0x0D,0xE0,0x90,0x06,0x1A,0x04,0x6C,0x07,0x2A,0x04,0x1A,0x00,0x4C, - 0x00,0x03,0x00,0x06,0x00,0x79,0xBE,0x44,0x99,0x70,0x00,0x00,0x0A, /* 0.93: 0x0A=0x06 - WRONG? */ + 0x00,0x03,0x00,0x06,0x00,0x79,0xBE,0x44,0x00,0x00,0x00,0x00,0x06, /* 1280x768 (not in 0.93) */ 0x0A,0xC0,0x00,0x05,0x00,0x03,0x80,0x06,0x1E,0x03,0x40,0x00,0x80, - 0x00,0x03,0x00,0x07,0x00,0x4F,0x00,0x00,0x6F,0x6B,0x00,0x00,0x06 + 0x00,0x03,0x00,0x07,0x00,0x4F,0x00,0x00,0x00,0x00,0x00,0x00,0x06 }; #endif @@ -347,46 +343,45 @@ static BOOLEAN SiS_SetTrumpionBlock(SiS_Private *SiS_Pr, UCHAR *dataptr); #endif - -USHORT SiS_ReadDDC1Bit(SiS_Private *SiS_Pr); -void SiS_SetSwitchDDC2(SiS_Private *SiS_Pr); -USHORT SiS_SetStart(SiS_Private *SiS_Pr); -USHORT SiS_SetStop(SiS_Private *SiS_Pr); -void SiS_DDC2Delay(SiS_Private *SiS_Pr, USHORT delaytime); -USHORT SiS_SetSCLKLow(SiS_Private *SiS_Pr); -USHORT SiS_SetSCLKHigh(SiS_Private *SiS_Pr); -USHORT SiS_ReadDDC2Data(SiS_Private *SiS_Pr, USHORT tempax); -USHORT SiS_WriteDDC2Data(SiS_Private *SiS_Pr, USHORT tempax); -USHORT SiS_CheckACK(SiS_Private *SiS_Pr); - -USHORT SiS_InitDDCRegs(SiS_Private *SiS_Pr, unsigned long VBFlags, int VGAEngine, - USHORT adaptnum, USHORT DDCdatatype, BOOLEAN checkcr32); -USHORT SiS_WriteDABDDC(SiS_Private *SiS_Pr); -USHORT SiS_PrepareReadDDC(SiS_Private *SiS_Pr); -USHORT SiS_PrepareDDC(SiS_Private *SiS_Pr); -void SiS_SendACK(SiS_Private *SiS_Pr, USHORT yesno); -USHORT SiS_DoProbeDDC(SiS_Private *SiS_Pr); -USHORT SiS_ProbeDDC(SiS_Private *SiS_Pr); -USHORT SiS_ReadDDC(SiS_Private *SiS_Pr, USHORT DDCdatatype, unsigned char *buffer); -USHORT SiS_HandleDDC(SiS_Private *SiS_Pr, unsigned long VBFlags, int VGAEngine, - USHORT adaptnum, USHORT DDCdatatype, unsigned char *buffer); +USHORT SiS_ReadDDC1Bit(SiS_Private *SiS_Pr); +void SiS_SetSwitchDDC2(SiS_Private *SiS_Pr); +USHORT SiS_SetStart(SiS_Private *SiS_Pr); +USHORT SiS_SetStop(SiS_Private *SiS_Pr); +void SiS_DDC2Delay(SiS_Private *SiS_Pr, USHORT delaytime); +USHORT SiS_SetSCLKLow(SiS_Private *SiS_Pr); +USHORT SiS_SetSCLKHigh(SiS_Private *SiS_Pr); +USHORT SiS_ReadDDC2Data(SiS_Private *SiS_Pr, USHORT tempax); +USHORT SiS_WriteDDC2Data(SiS_Private *SiS_Pr, USHORT tempax); +USHORT SiS_CheckACK(SiS_Private *SiS_Pr); + +USHORT SiS_InitDDCRegs(SiS_Private *SiS_Pr, unsigned long VBFlags, int VGAEngine, + USHORT adaptnum, USHORT DDCdatatype, BOOLEAN checkcr32); +USHORT SiS_WriteDABDDC(SiS_Private *SiS_Pr); +USHORT SiS_PrepareReadDDC(SiS_Private *SiS_Pr); +USHORT SiS_PrepareDDC(SiS_Private *SiS_Pr); +void SiS_SendACK(SiS_Private *SiS_Pr, USHORT yesno); +USHORT SiS_DoProbeDDC(SiS_Private *SiS_Pr); +USHORT SiS_ProbeDDC(SiS_Private *SiS_Pr); +USHORT SiS_ReadDDC(SiS_Private *SiS_Pr, USHORT DDCdatatype, unsigned char *buffer); +USHORT SiS_HandleDDC(SiS_Private *SiS_Pr, unsigned long VBFlags, int VGAEngine, + USHORT adaptnum, USHORT DDCdatatype, unsigned char *buffer); #ifdef LINUX_XF86 -USHORT SiS_SenseLCDDDC(SiS_Private *SiS_Pr, SISPtr pSiS); -USHORT SiS_SenseVGA2DDC(SiS_Private *SiS_Pr, SISPtr pSiS); +USHORT SiS_SenseLCDDDC(SiS_Private *SiS_Pr, SISPtr pSiS); +USHORT SiS_SenseVGA2DDC(SiS_Private *SiS_Pr, SISPtr pSiS); #endif #ifdef SIS315H -void SiS_OEM310Setting(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, - USHORT ModeNo,USHORT ModeIdIndex); -void SiS_OEM661Setting(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, - USHORT ModeNo,USHORT ModeIdIndex, USHORT RRTI); -void SiS_FinalizeLCD(SiS_Private *, USHORT, USHORT, PSIS_HW_INFO); +void SiS_OEM310Setting(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, + USHORT ModeNo,USHORT ModeIdIndex); +void SiS_OEM661Setting(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, + USHORT ModeNo,USHORT ModeIdIndex, USHORT RRTI); +void SiS_FinalizeLCD(SiS_Private *, USHORT, USHORT, PSIS_HW_INFO); #endif #ifdef SIS300 -void SiS_OEM300Setting(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, - USHORT ModeNo, USHORT ModeIdIndex, USHORT RefTabindex); -void SetOEMLCDData2(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, - USHORT ModeNo, USHORT ModeIdIndex,USHORT RefTableIndex); +void SiS_OEM300Setting(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, + USHORT ModeNo, USHORT ModeIdIndex, USHORT RefTabindex); +void SetOEMLCDData2(SiS_Private *SiS_Pr, PSIS_HW_INFO HwInfo, + USHORT ModeNo, USHORT ModeIdIndex,USHORT RefTableIndex); #endif extern void SiS_SetReg(SISIOADDRESS, USHORT, USHORT); diff -urN linux-2.4.26/drivers/video/sis/initdef.h linux-2.4.27/drivers/video/sis/initdef.h --- linux-2.4.26/drivers/video/sis/initdef.h 2004-04-14 06:05:40.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/initdef.h 2004-08-07 16:26:05.931399249 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * Global definitions for init.c and init301.c * @@ -84,6 +85,8 @@ #define VB_SIS301B302B (VB_SIS301B|VB_SIS301C|VB_SIS302B) #define VB_SIS301LV302LV (VB_SIS301LV|VB_SIS302LV|VB_SIS302ELV) #define VB_SISVB (VB_SIS301 | VB_SIS301BLV302BLV) +#define VB_SISTMDS (VB_SIS301 | VB_SIS301B302B) +#define VB_SISLVDS VB_SIS301LV302LV /* VBInfo */ #define SetSimuScanMode 0x0001 /* CR 30 */ @@ -136,6 +139,7 @@ #define CRT2Mode 0x0800 #define HalfDCLK 0x1000 #define NoSupportSimuTV 0x2000 +#define NoSupportLCDScale 0x4000 /* TMDS: No scaling possible (no matter what panel) */ #define DoubleScanMode 0x8000 /* Infoflag */ @@ -200,7 +204,7 @@ #define SF_IsM661 0x0020 #define SF_IsM741 0x0040 #define SF_IsM760 0x0080 -#define SF_760UMA 0x8000 +#define SF_760LFB 0x8000 /* 760: We have LFB */ /* CR32 (Newer 630, and 315 series) @@ -478,13 +482,23 @@ #define VCLK100_315 0x46 /* Index in VBVCLKData table (315) */ #define VCLK34_315 0x55 #define VCLK68_315 0x0d -#define VCLK69_315 0x5c /* Index in VBVCLKData table (315) */ +#define VCLK69_315 0x5c /* deprecated ! Index in VBVCLKData table (315) */ +#define VCLK83_315 0x5c /* Index in VBVCLKData table (315) */ #define VCLK121_315 0x5d /* Index in VBVCLKData table (315) */ #define VCLK_1280x720 0x5f #define VCLK_1280x768_2 0x60 #define VCLK_1280x768_3 0x61 #define VCLK_CUSTOM_315 0x62 #define VCLK_1280x720_2 0x63 +#define VCLK_720x480 0x67 +#define VCLK_720x576 0x68 +#define VCLK_768x576 0x68 +#define VCLK_848x480 0x65 +#define VCLK_856x480 0x66 +#define VCLK_800x480 0x65 +#define VCLK_1024x576 0x51 +#define VCLK_1152x864 0x64 +#define VCLK_1360x768 0x58 #define TVCLKBASE_300 0x21 /* Indices on TV clocks in VCLKData table (300) */ #define TVCLKBASE_315 0x3a /* Indices on TV clocks in (VB)VCLKData table (315) */ @@ -595,7 +609,7 @@ /* ============================================================= - for 315 series + for 315 series (old data layout) ============================================================= */ #define SoftDRAMType 0x80 diff -urN linux-2.4.26/drivers/video/sis/oem300.h linux-2.4.27/drivers/video/sis/oem300.h --- linux-2.4.26/drivers/video/sis/oem300.h 2004-04-14 06:05:40.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/oem300.h 2004-08-07 16:26:05.931399249 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * OEM Data for 300 series * diff -urN linux-2.4.26/drivers/video/sis/oem310.h linux-2.4.27/drivers/video/sis/oem310.h --- linux-2.4.26/drivers/video/sis/oem310.h 2004-04-14 06:05:40.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/oem310.h 2004-08-07 16:26:05.932399291 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * OEM Data for 315/330 series * @@ -126,7 +127,7 @@ 0x33,0x33,0x33 }; -static const UCHAR SiS310_LCDDelayCompensation_3xx301B[] = /* 30xB,LV */ +static const UCHAR SiS310_LCDDelayCompensation_3xx301B[] = /* 30xB */ { 0x01,0x01,0x01, /* 800x600 */ 0x0C,0x0C,0x0C, /* 1024x768 */ @@ -145,6 +146,25 @@ 0x02,0x02,0x02 }; +static const UCHAR SiS310_LCDDelayCompensation_3xx301LV[] = /* 315+30xLV */ +{ + 0x01,0x01,0x01, /* 800x600 */ + 0x04,0x04,0x04, /* 1024x768 (A531/BIOS 1.14.05f: 4 - works with 6 */ + 0x0C,0x0C,0x0C, /* 1280x1024 */ + 0x08,0x08,0x08, /* 640x480 */ + 0x0C,0x0C,0x0C, /* 1024x600 (guessed) */ + 0x0C,0x0C,0x0C, /* 1152x864 (guessed) */ + 0x0C,0x0C,0x0C, /* 1280x960 (guessed) */ + 0x0C,0x0C,0x0C, /* 1152x768 (guessed) */ + 0x0C,0x0C,0x0C, /* 1400x1050 (guessed) */ + 0x0C,0x0C,0x0C, /* 1280x768 (guessed) */ + 0x0C,0x0C,0x0C, /* 1600x1200 (guessed) */ + 0x02,0x02,0x02, + 0x02,0x02,0x02, + 0x02,0x02,0x02, + 0x02,0x02,0x02 +}; + static const UCHAR SiS310_TVDelayCompensation_301[] = /* 301 */ { 0x02,0x02, /* NTSC Enhanced, Standard */ diff -urN linux-2.4.26/drivers/video/sis/osdef.h linux-2.4.27/drivers/video/sis/osdef.h --- linux-2.4.26/drivers/video/sis/osdef.h 2004-04-14 06:05:40.000000000 -0700 +++ linux-2.4.27/drivers/video/sis/osdef.h 2004-08-07 16:26:05.932399291 -0700 @@ -1,4 +1,5 @@ /* $XFree86$ */ +/* $XdotOrg$ */ /* * OS depending defines * @@ -84,7 +85,6 @@ #ifdef LINUX_KERNEL #include -#include #ifdef CONFIG_FB_SIS_300 #define SIS300 @@ -94,10 +94,6 @@ #define SIS315H #endif -#if 1 -#define SISFBACCEL /* Include 2D acceleration */ -#endif - #define OutPortByte(p,v) outb((u8)(v),(SISIOADDRESS)(p)) #define OutPortWord(p,v) outw((u16)(v),(SISIOADDRESS)(p)) #define OutPortLong(p,v) outl((u32)(v),(SISIOADDRESS)(p)) @@ -108,7 +104,7 @@ #endif /**********************************************************************/ -/* XFree86 */ +/* XFree86, X.org */ /**********************************************************************/ #ifdef LINUX_XF86 diff -urN linux-2.4.26/drivers/video/sis/sis.h linux-2.4.27/drivers/video/sis/sis.h --- linux-2.4.26/drivers/video/sis/sis.h 2002-11-28 15:53:15.000000000 -0800 +++ linux-2.4.27/drivers/video/sis/sis.h 2004-08-07 16:26:05.934399373 -0700 @@ -1,10 +1,512 @@ +/* + * SiS 300/630/730/540/315/550/650/651/M650/661FX/M661FX/740/741/330/760 + * frame buffer driver for Linux kernels >=2.4.14 and >=2.6.3 + * + * Copyright (C) 2001-2004 Thomas Winischhofer, Vienna, Austria. + * + * 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 named License, + * or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA + */ + #ifndef _SIS_H #define _SIS_H -#if 1 -#define TWDEBUG(x) +#include +#include + +#include "osdef.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) +#include